文章目录

    • Android中的多进程模式
          • 1、开启多进程模式
          • 2、多进程模式的运行机制
    • IPC概念介绍
          • 1、Serializable接口
          • 2、Parcelable接口
          • 3、Binder
    • Android中的IPC方式
          • 1、Bundle
          • 2、使用文件共享
          • 3、使用Messenger
          • 4、使用AIDL(Android 接口定义语言)
          • 5、使用ContentProvider
          • 6、使用Socket

IPC是Inter-process communication的缩写,含义为进程间通信或者跨进程通信

Android中的多进程模式

1、开启多进程模式

  在Android中使用多进程只有一种方法,就是给四大组件在AndroidMenifest中指定android:process属性。(有一种非常规的方法:通过JNI在native层中去fock一个新的进程。)如下代码所示:

                        

上述代码创建了三个进程:

 进程1:com.ljh.mytodo
 进程2:com.ljh.mytodo:remote
 进程3:com.ljh.mytodo.remote

  ”:“的含义是指要在当前的进程名前面附加其包名,而”com.ljh.mytodo.remote“则是完整的进程名,如果不设置则在默认的进程(进程名为包名)中。以”:“开头的进程属于当前应用的私有进程,其他应用不可以和他跑在同一个进程中,而不以”:“开头的进程属于全局进程,其他应用可以通过shareUID方式可以和他跑在同一个进程中。

  由于Android系统会给每一个应用分配一个唯一的UID,具有相同UID的应用才能共享数据。(两个应用通过shareUID运行在同一个进程中需要两个应用都有相同的shareUID并且签名相同才行,只有这样,他们才能互相访问对方的私有数据,比如data目录、组件信息等,不管是否运行在同一个进程中。如果运行在同一个进程中,则继续可以共享内存数据)

2、多进程模式的运行机制

  由于Android系统会给每个进程都分配一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间。
  使用多进程会造成如下问题:

  1. 静态成元和单例模式完全失效
  2. 线程同步机制完全失效
     不在同一块内存中,锁对象或者锁全局类都不能保证线程同步,因为锁的对象不在同一进程中
  3. SharedPreferences的可靠性下降
     SharedPreferences不支持两个进程同时读写xml文件来实现,并发读/写可能出现问题
  4. Application会多次创建
     在创建新的进程时要分配独立的虚拟机,就是相当于重新启动了应用。

  为了解决上述问题,可以通过以下方式实现跨进程通信:通过Intent来传递数据,共享文件和SharedPreferences,基于Binder的Messager和AIDL以及Socket等。

IPC概念介绍

  当我们需要使用Intent和Binder传递数据时,就需要将数据对象进行序列化。

1、Serializable接口

  Serializable是Java提供的一个序列化接口,是一个空接口,为对象提供标准的序列化和反序列化,在类中有如下标识(serialVersionUID)就可以自动实现默认的序列化:

private static final long serialVersionUID = 11112151313515355L

  序列化和反序列化的过程也很简单,只要采用ObjectOutputStream和ObjectInputStream就可以实现:

public class User implements Serialzable{    private static final long serialVersionUID = 11112151313515355L;    public int userId;    ....}//序列化User user = new User(1);ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cache.txt"));out.writeObject(user);out.close();//反序列化ObjectinputStream in = new ObjectInputStream(new FileinputStream("cache.txt"));User newUser = (User) in.readObject();in.close();

  serialVersionUID的工作机制如下:序列化时系统把当前类的serialVersionUID写入序列化的文件中,当反序列化时系统会检测文件中的serialVersionUID,判断是否与当前类的serialVersionUID一致,如果一致则表示序列化的类与当前类的版本相同,这时可以成功反序列化,否则就说明当前类与序列类的相比发生了某些变换,比如成员变量的数量、类型发生了改变,那么就不能正常反序列化。

2、Parcelable接口

  使用方法:

public class User implements Parcelable{    public int userId;    public String userName;    public boolean isMale;    public Book book;    public User(int userId, String userName, boolean isMale){        this.userId = userId;        this.userName = userName;        this.isMale = isMale;     }    public int describeContents(){    //返回当前对象的内容描述        return 0;    }        public void writeToParcel(Parcle out, int flags){   //将当前对象写入序列化结构中        out.writeInt(userId);        out.writeString(userName);        out.writeInt(isMale ? 1 : 0);        out.writeParcelable(book, 0);     }    public static final Parcelable.Creator CREATOR = new Parcelable.Creator(){         public User createFromParcel(Parcel in){    //从序列化后的对象中创建原始对象              return new User(in);          }                  public User[] newArray(int size){  //创建指定长度的原始对象数组               return new User[size];             }};private User(Parcle in){   //从序列化后的对象中创建原始对象    userId = in.readInt();    userName = in.readString();    isMale = in.readInt() == 1;    book = in.readParcelable(Thread..currentThread().getContextClass - Loader());}

  Serialzable开销大,序列化与反序列化都需要大量的IO操作,而Parcelable使用比较麻烦,但是效率高。

3、Binder

  Binder是Android中的一种跨进程通信方式。从Android Framework角度来说,Binder是ServiceManager连接各种Manager(ActivityManager、WindowManager等)和相应的ManagerService的桥梁;从Android应用层来说Binder是客户端和服务器进行通信的媒介,当bindService时,服务器会返回一个包含了服务端业务调用Binder对象,通过这个Binder对象,客户端可以获取服务器端提供的服务或者数据(这里的服务包括普通服务和基于AIDL的服务)

  在Android开发中,Binder主要是用在service中,包括AIDL和Messenger,其中普通的Service中的Binder不涉及进程间通信,无法设计Binder的核心,而Messenger的底层其实是AIDL。
  在Binder的实现类中有如下属性与方法:
  1. DESCRIPTOR:binder的唯一表示,一般用当前Binder的类名来表示。
  2. asInterface(android.os.IBinder obj):用于将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象(这种转换过程是区分进程的,如果客户端和服务端位于同一进程,那么此方法返回的就是服务端的stub对象本身,否则返回的是系统封装后的Stub.proxy对象)
  3. asBinder:此方法用于返回当前Binder对象
  4. onTransact:这个方法运行在服务端中的Binder线程池中,当客户端发起跨进程请求时,远程调用会通过系统底层封装后交由该方法来处理:服务端通过code可以确定客户端所请求的目标方法是什么,接着从data中取出目标方法所需的参数,然后执行目标方法。当目标方法执行完毕后,就向reply中写入返回值。(如果该方法返回false,那么客户端请求就会失败,因此我们可以利用这个特性来做权限验证。)
  上述就是Binder的工作机制,但是有两点需要注意:
  1. 当客户端发起远程请求时,由于当前线程会被挂起,直至服务端进程返回数据,所以如果一个远程方法是耗时的,那么就不能在UI线程上发起该远程请求。
  2. 由于服务端的binder方法运行在Binder的线程池中,所以Binder方法不管是否消耗都应该给采用同步的方式去实现,因为她已经运行在一个线程中。
【Android】IPC(进程间通信)_第1张图片
按照上述思想,我们也可以手动写一个Binder。

Android中的IPC方式

 跨进程通信方式有:
 1. 通过Intent中附加extras来传递信息
 2. 通过共享文件来共享数据
 3. 采用Binder方式来是想跨进程通信
 4. 采用ContentProvider
 5. 采用socket

1、Bundle

 Activity、Service、Receiver都是支持在Intent中传递Bundle数据的,由于Bundle实现了Parcelable接口,因此可以在Bundle中附加我们需要给远程进程的信息并通过Intent在不同的进程中传输。
 除了直接传递数据之外,还有一种特殊的使用场景:比如A进程在进行一个计算,计算完成后需要启动B进程的一个组件,并把计算结果传递给B进程,但是这个计算结果并不支持放入到Bundle中,因此无法通过Intent来传输,这时用IPC方法会显得有些复杂,可以考虑使用如下方式:通过Intent启动B进程的一个Service组件(如IntentService)让Service在后台进行计算,计算完毕后再启动B进程中真正要启动的目标组件,由于Service也运行在B进程中,所以目标组件就可以直接获取计算结果,就轻松的解决了跨进程的问题。

2、使用文件共享

 共享文件是一个不错的进程间通信方式,两个进程通过读/写同一个文件来交换数据。在Window上,一个文件被加了排斥锁就会导致其他线程无法对其进行访问,而在Android中,并发读/写文件没有任何限制,因此可能会导致数据出现问题。通过文件传递文本信息外,可以通过序列化一个对象到文件系统中,同时在另一个线程中恢复该对象。综上文件共享方式适合于对数据同步要求不高的进程之间进行通信,并且要妥善解决并发读写的问题。

3、使用Messenger

 Messenger可以翻译为信使,通过他可以在不同进程中传递Message对象,在message中放入我们需要传递的数据,就可以轻松实现数据的进程间传递。Messenger是一种轻量级的IPC方案,底层实现是AIDL。
 因为Messenger是以串行的方式处理客户端发来的消息,因此在服务端中不存在并发执行的情形,不需要考虑进程同步的问题。使用方法如下:
 1. 服务端进程
  创建一个service来处理客户端的连接请求,同时创建一个Handler并通过它来创建一个Messenger对象,然后在Service的onBind中返回这个Messenger对象底层的Binder即可。
 2. 客户端进程
  在客户端进程中首先绑定服务端的service,绑定成功后用服务器返回的IBinder对象创建一个Messenger,通过这个Messenger就可以向服务端发送消息了,发消息类型为message对象。如果需要服务端能够回应客户端,就需要创建一个Handler并创建一个新的messenger,并将该messenger对象通过Message的replyTo参数传递给服务端,服务端通过这个replyTo参数就可以回应客户端了。
【Android】IPC(进程间通信)_第2张图片

4、使用AIDL(Android 接口定义语言)

 为了解决Messenger中无法处理并发请求和实现跨进程的方法调用,要使用AIDL处理,而不使用Messenger。实现流程:
 1. 服务端
  服务端首先要创建一个service来监听客户端的连接请求,然后创建一个AIDL文件,将暴露给客户端的接口在这个AIDL文件中声明,最后在Serivce中实现这个AIDL接口即可。
 2. 客户端
  首先要绑定服务端的Service,绑定成功后,将服务端返回的Binder对象转成AIDL接口所需的类型,接着就可以调用AIDL中的方法。
 3. AIDL接口的创建
  ①创建实体类,实现Parcelable接口,以便序列化和反序列化
  ②新建AIDL文文件夹,在其创建接口AIDL文件以及实体类的映射文件AIDL文件
  ③make project,生成Binder的Java文件

  AIDL文件支持的数据类型如下:
  ①基本数据类型(int、long、char、Boolean、double等)
  ②String和CharSequence
  ③list:只支持ArrayList,里面每个元素都必须能够被AIDL支持
  ④Map:只支持HashMap,里面每个元素都必须能被AIDL支持,包括key和value
  ⑤parcelabel:实现了Parcelable接口的对象(需要显示import)
  ⑥AIDL:所有的AIDL接口本身就是可以在AIDL文件中使用。
 4. 远程服务端Service的实现
 5. 客户端的实现

5、使用ContentProvider

 ContentProvider主要是以表格的形式来组织数据,并且包含多个表,对于每个表格来说,它们都具有行和列的层次性,行往往对应一条记录,而列对应一条记录中的一个字段,与数据库类似。除了表格的形式,ContentProvider还支持文件数据,比如图片、视频等。

6、使用Socket

 Socket分为流式套接字和用户数据报套接字两种,分别对应于网络传输控制层中的TCP和UDP协议。服务端设计:当service启动时,会在线程中建立TCP服务。然后等待客户端的连接请求,当有客户端链接时,就会生成一个心动 socket,通过每次新创建的socket就可以分别和不同的客户端通信了,当客户端断开连接时,服务端就会相应的关闭对应的socket来结束通话线程。

更多相关文章

  1. Android 服务端开发之开发环境配置
  2. 使用AIDL实现Android的跨进程通信
  3. android跨进程通信(IPC):使用AIDL
  4. Android获得全局进程信息以及进程使用的内存情况
  5. android Aidl 实现进程间通讯
  6. FregServer进程,获取ServiceManager代理对象
  7. 2011.07.19——— android intent 传递list或者对象

随机推荐

  1. 碎纸机for android
  2. Android(安卓)Wi-Fi 设置country code的
  3. Android获取本机IP地址
  4. Android(安卓)文件浏览控件
  5. Android(安卓)内存泄露分析
  6. 第一行代码笔记 获取拍照照片和相册选取
  7. 禁止ScrollView内的控件改变之后自动滚动
  8. Android(安卓)动态切换全屏和非全屏模式
  9. 最新[Android地图sdk] 踩坑经验分享 Dist
  10. adb 指令uninstall卸载android app 处理