Android技能树 — 多进程相关小结

前言

最近过完年了,打算把本身的Android知识都整理一下。html

Android技能书系列:java

Android基础知识android

Android技能树 — 动画小结git

Android技能树 — View小结github

Android技能树 — Activity小结面试

Android技能树 — View事件体系小结算法

Android技能树 — Android存储路径及IO操做小结数组

Android技能树 — 多进程相关小结bash

Android技能树 — Drawable小结数据结构

Android技能树 — Fragment整体小结

数据结构基础知识

Android技能树 — 数组,链表,散列表基础小结

Android技能树 — 树基础知识小结(一)

算法基础知识

Android技能树 — 排序算法基础小结

此次是讲Android存储路径及IO的基本操做。由于咱们在开发的时候会常常这种方便的需求。这篇文章的内容我写的可能不多,都没有细写。别吐槽。o( ̄︶ ̄)o

其余很少说,先上脑图:

多进程小结脑图下载

多进程

进程与线程

有时候面试别人的时候,我会问说什么是多进程,怎么开启多进程,他们会说new 一个Thread。因此不少人会把多进程和多线程弄错。我就简单说明下:通常来讲咱们启动一个APP,就是一个进程,而后这个APP里面有不少线程,最熟悉的就是咱们日常的主线程(UI)线程。因此进程是包含线程的。

固然我这讲的就比较通俗了: 能够看下其余相似的文章介绍:Android--进程与线程

开启多进程

其实开启多进程很简单。只须要在AndroidManifest.xml的四大组件中添加android:process便可。这时候就会运行在你定义好的名字的进程中了。

多进程开启后的问题

简单来讲就是同步会有问题。咱们刚才说了通常来讲启动一个APP,就建立了一个进程,而后全部的东西都在这个进程里面。这时候你对某个Activity定义了android:process。他就运行在另一个进程了。这时候Application也会从新建立一次,在这个新的进程中。这个Activity也会在这个新的进程中。并且咱们创建的一些实体类对象也是不一样进程里面各自产生本身的副本对象。互不关联。

因此咱们知道了一些线程同步。单例模式都无效了,由于对象是各个进程中有副本,同步锁的锁对象都不是同一个对象。固然线程同步机制就失效了。

其中SharePreferences自己是一个文件,因此不受多进程的影响,可是由于SharePreferences不支持多个进程同时执行写操做,因此有可能会致使出现数据丢失等问题。甚至是并发读和写也可能有问题。可是若是你只是一个进程写,一个进程读,并且不是同时,那就问题不大了。

进程间通讯

既然说了多进程,若是咱们如今就是二个进程进行通讯怎么办。在讲如何通讯以前,咱们能够先看下相关的基础,那就是序列化及反序列化。

咱们看序列化有哪些:

咱们能够看到,序列化通常主要是二个,那就是Serialzable和Parcelable。

具体的时候都很简单。下面写大体提下这二个的使用。

Serialzable

User.java (要传递的实体类)

public class User implements Serializable {

	private static final long serialVersionUID = 512345678910L;

    public int userId;
    public String userName;
    public boolean isMale;

    public User(int userId, String userName, boolean isMale) {
        this.userId = userId;
        this.userName = userName;
        this.isMale = isMale;
    }
}
复制代码

咱们只要直接将咱们的类实现Serializable接口便可。很简单。这里我提一下serialVersionUID。由于咱们平时写都不会写这个。也是正常使用。可是好比我把这个User对象经过ObjectOutputStream序列化后写到了本地文件,可是这时候咱们把咱们的User对象里面的属性改了,好比增长了一项:public boolean haha;而后再经过ObjectInputStream去读取出来就会抛异常。由于反序列化会和序列化时候的serialVersionUID进行比较,若是不一样,直接不进行反序列化了,就抛出异常。可是咱们不手动写这个值,它会根据当前这个类结构去生成的hash值为值。因此当咱们把这个类结构更改后,再去反序列化就报错了。

Parcelable

public class User implements Parcelable {

    public int userId;
    public String userName;
    public boolean isMale;


    public User(int userId, String userName, boolean isMale) {
        this.userId = userId;
        this.userName = userName;
        this.isMale = isMale;
    }

    protected User(Parcel in) {

        userId = in.readInt();
        userName = in.readString();
        isMale = in.readInt() == 1;
    }

    public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel in) {
            return new User(in);
        }

        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {

        dest.writeInt(userId);
        dest.writeString(userName);
        dest.writeInt(isMale ? 1 : 0);

    }
}

复制代码

你写的类实现了Parcelable后AS会自动提示,你就按照他的提示生成相应的代码便可。这里咱们只要注意这么几个地方:

1.咱们在序列化前,总要先把这个类实例化成对象,而后把相应的内容赋值进去是吧,因此上面代码中,我写了个构造函数:

public User(int userId, String userName, boolean isMale) {
        this.userId = userId;
        this.userName = userName;
        this.isMale = isMale;
}
复制代码

这样咱们写代码的时候就new User(10,"dyp",true)(固然你也能够写setXXX方法去设置)

2.咱们要序列化了,咱们把咱们的这个类里面的属性值都写进Parcel中,就比如咱们是拿了个本子,一行行的记下内容,而后等会一行行的取出来。因此咱们看到了。咱们是按照顺序先记录下来,因此等会还原的时候也要按顺序取出来相应的值。因此顺序很重要。

@Override
public void writeToParcel(Parcel dest, int flags) {
   //记下userId,由于是Int类型,因此用writeInt
   dest.writeInt(userId);
   //记下userName,由于是String类型,因此用writeString
   dest.writeString(userName);
   
   /*记下isMale ,由于是Boolean类型,
     可是没有writeBoolean,只有writeBooleanArray,
     因此咱们用writeInt()来记录,1是true,0是false。
     额外说下writeBooleanArray内部其实仍是用writeInt来记录的。
   */
   dest.writeInt(isMale ? 1 : 0);
}
复制代码

3.咱们最后传到了其余的进程,确定是要从Parcel里面把咱们的对象给还原出来,确定是先new 一个User对象,而后把各类咱们前面第二步保存好的值给它从新赋值。

public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel in) {
			
	    //这里是否是咱们先进行了new一个对象,同时把Parcel对象传入。
            return new User(in);
        }

        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };
复制代码
protected User(Parcel in) {
	
     //而后咱们再生成这个对象的同时,再把这个对象的属性都赋值好,切记要按照上面写入的顺序来读取出来赋值。
     userId = in.readInt();
     userName = in.readString();
     isMale = in.readInt() == 1;
}
复制代码

Binder

序列化的相关的基础讲了。咱们来看Binder ,其实吧,Binder我也不知道怎么讲,直接贴别的大佬的相关文章了。

图文详解 Android Binder跨进程通讯的原理

Android Binder之应用层总结与分析

而后这里特别提一下:

进程间通讯方式

因此咱们能够一个个具体来看实现进程间通讯方式。

使用Bundle

其实这个咱们平时用的不少,

我写个Demo你们就知道了。

MainActivity.java

User user = new User(10,"dongyaoping",true);
Intent intent = new Intent(MainActivity.this,MyService.class);
Bundle bundle = new Bundle();
bundle.putSerializable("data",user);
bundle.putInt("int",10);
bundle.putString("string","haha");
intent.putExtras(bundle);
startService(intent);
复制代码

MyService.java (记得在AndroidManifest.xml中设置android:process属性,让它在另一个进程)

public class MyService extends Service{

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        User user = (User) intent.getExtras().getSerializable("data");
        Log.v("dyp","user:"+user.toString());
        return super.onStartCommand(intent, flags, startId);
    }

}
复制代码

因此咱们看到Bundle能够put进去不少东西,由于Bundle自己实现了Parcelable

public final class Bundle extends BaseBundle implements Cloneable, Parcelable {}
复制代码

使用文件共享

其实这个就更简单了。咱们只须要把一个要传的数据写到一个文件,而后在另一个进程中去读取这个文件就能够了。

一个进程中取写入:

User user = new User(100,"dyp",true);
try {
     ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(path));
     out.writeObject(user);
     out.close();
} catch (Exception e) {
     e.printStackTrace();
}
复制代码

另一个进程中取读取:

try {
     ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(path));
     User user = (User ) inputStream.readObject();                   
} catch (Exception e) {
     e.printStackTrace();
}
复制代码

使用Socket

直接贴上大佬的文章:

Android:这是一份很详细的Socket使用攻略

使用Binder

  1. AIDL
  2. Messenger
  3. ContentProvider

咱们主要讲下AIDL,由于Messenger是AIDL的封装,使用起来也更方便。AIDL会了。Messenger也就会使用。ContentProvider的教程就更多了。说实话平时四大组件中ContentProvider使用的频率不多不多。因此我也不具体写了,网上的教程也不少。

贴上大佬的ContentProvider教程:

Android:关于ContentProvider的知识都在这里了!

贴上另外大佬的 Messenger的教程:

Android 进阶10:进程通讯之 Messenger 使用与解析

咱们主要来看AIDL的实现:

具体的细节你们能够看脑图,我就不细说了。

咱们能够看到在客户端跨进程访问服务端的时候,咱们分了五步。

第一步:建立AIDL文件。

这里咱们要注意一点。咱们在AS中建立AIDL,直接就能够右键 --> New --> AIDL便可。这时候会在这个目录下面。

这时候咱们会看到这样的界面。

interface IMyAidlInterface {

    String getInfor(String s);
    String getName(char name);

	//传递对象。
	String getBook(in Book book);

}

复制代码

若是咱们要传递一个Book对象,这时候这个Book.java应该是在java包里面,因此咱们同时还要再aidl文件夹中建立一个跟这个对象同名的aidl文件。因此变成了这样:

不过必定要切记,整个Book.java和Book.aidl的包名要同样。否则会提示找不到Book这个类。

第二步:声明一个 IBinder 接口实例(或者基于 AIDL 生成)。

而后咱们build下以后,AS就会根据咱们写的AIDL自动生成一个IBinder文件。(固然若是你第一步不写AIDL,彻底本身写一个IBinder文件也是能够的。)

第三步:实现 ServiceConnection
final ServiceConnection connection = new ServiceConnection() {
      @Override
      public void onServiceConnected(ComponentName name, IBinder service) {
         
      }

      @Override
      public void onServiceDisconnected(ComponentName name) {
         
      }
};
复制代码
第四步:调用 Context.bindService(),以传入您的 ServiceConnection 实现。
Intent intent = new Intent(MainActivity.this, MyService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
复制代码
第五步:onServiceConnected() 实现中实现相关操做

在你的 onServiceConnected() 实现中,你将收到一个 IBinder 实例(名为 service)。调用 YourInterfaceName.Stub.asInterface((IBinder)service),以将返回的参数转换为 YourInterface 类型。

final ServiceConnection connection = new ServiceConnection() {
      @Override
      public void onServiceConnected(ComponentName name, IBinder service) {
          Log.v("dyp", "已经链接上了");
          IMyAidlInterface iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
          try {
              String haha = iMyAidlInterface.getInfor("hello,我是activity");
              Log.v("dyp", "接受到Service发过来的字符串:" + haha);
          } catch (RemoteException e) {
              e.printStackTrace();
          }
      }

      @Override
      public void onServiceDisconnected(ComponentName name) {
           Log.v("dyp","断开了链接");
      }
};
复制代码
服务端第一步:实例化 YourInterfaceName.Stub对象
private IBinder binder = new IMyAidlInterface.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public String getInfor(String s) throws RemoteException {
            Log.v("dyp","接收到Activity的字符串:"+s);
            return "service传过去的字符串";
        }

        @Override
        public String getBook(Book book) throws RemoteException {
            return null;
        }

        @Override
        public String getName(char name) throws RemoteException {
            return null;
        }
};

复制代码

服务端第二步:onBind方法中返回上面生成的对象

@Nullable
@Override
public IBinder onBind(Intent intent) {
    return binder;
}
复制代码

各类通讯方式比较

直接复制别人网上的图片:

结尾

我仍是不知道说啥,你们轻点喷我就行。。。。。

相关文章
相关标签/搜索