IPC机制

作者: shuixingge | 来源:发表于2016-05-11 15:04 被阅读569次

    2.1 Android IPC机制
    任何一个操作系统都需要IPC机制,Linux可以通过共享内存,管道,信号量来进行IPC通信。Android中Binder和Socket,Socket还支持任意两个终端之间的通信,支持同一个设备之间的两个进程通过Socket通信。
    多进程使用场景:加大一个应用可以使用的内存;有些模块需要独立的运行在一个进程之中;

    2.2 Android中多进程模式:
    Android创建多进程的方法:
    (1)给四大组件在Manifest中指定android:process属性。
    (2)通过JNI在native层fork一个新的进程
    :remote和包名.remote的区别:
    (1):remote=包名+:remote。
    (2)以:开头的进程属于当前应用私有进程,其他应用的组件不可以和它跑在一个进程中,而不以:开头的进程可以属于全局进程,其它应用的组件可以ShareUID的方式可以和它跑在同一应用中。
    UID和ShareUID:
    (1)Andorid系统会为每个应用分配唯一的UID,当两个应用UID相同而且签名也要相同,不管跑不跑在同一个进程,可以共享data目录,组件信息(不跑在同一进程)
    (2)两个应用跑在同一个进程;不仅共享data目录,组件信息,还可以共享内存数据。
    直接指定android:process使用多进程的缺陷
    (1)所有运行在不同进程中的四大组件,只要它们之间通过内存来共享数据,都会共享失败。原因:Android会为每个进程都分配一个独立的虚拟机实例,不同的虚拟机在内存分配上有不同的地址空间,这会导致在不同的虚拟机中访问同一个类的对象会产生多份副本。
    多进程造成的影响:
    1.静态成员和单例模式失效;
    2 线程同步机制失效。
    3 SharePrefences的可靠性下降。
    4 Application多次创建。
    不同进程的组件会拥有不同的Application,内存空间,虚拟机。

    2.3 IPC基础概念介绍
    Serializable和Parcelable:
    (1)Serializable接口是Java中为对象提供的标准的序列化和反序列化操作的接口,而Parcelable接口是Android提供的序列化方式的接口。
    (2)serializableUID是一连串的数字,序列化后的数据中serialVersionUID只有和当前类的serialVersionID相同才能够正常的被反序列化。静态成员变量属于类而不属于对象,不参与序列化,使用transient修饰的变量不参与序列化。
    serializableUID的详细工作机制:序列化的时候会把当前类的serialVersionUID写入文件中,当反序列化的时候会检查文件中的serialVersionUID是否和当前类的serialVersionUID一致。
    (3)Parcelable主要用在内存序列化上,可以直接序列化的有Intent、Bundle、Bitmap以及List和Map等等。

    Parcelable和Serializable的选择:
    (1)Parcelable主要用在内存的序列化,Serializable适合将对象序列化到存储设备或者将对象序列化后通过网络设备传输。
    (2)Parcelable高效但是使用起来麻烦,Serializable使用起来简单,但是效率较低,需要大量的I/O操作。(读写ObjectInputStream,ObjectOutputStream)。

    Binder概念:
    (1)从API的来说:Binder是一个类,实现了Ibinder接口。
    (2)从IPC的来说:它是一种跨进程的通信方式。
    (3)从FrameWork来说:是ServiceManager连接各种Manager和ManagerService(AMS)的桥梁。
    (4)从应用层来说:Binder是客户端和服务端进行通信的媒介,当BinderService时,服务端会返回一个包含服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端的服务或者数据。
    Binder主要用在Service中,包括AIDL和Messenger.

    AIDL (Android Interface Definition Language) :用于生成可以在两个进程之间进行进程间通信(interprocess communication, IPC)的代码。如果在一个进程中(例如ActivityThread)要调用另一个进程中(例如AMS)对象的方法,就可以使用AIDL生成可序列化的参数,AIDL会生成一个服务端对象的代理类,通过这个代理类就客户端可以间接调用服务端对象的方法;

    三个重要的类或者接口:
    (1)AIDL接口:继承自IInterface
    (2)Stub: AIDL接口的内部类,抽象类,继承Binder,实现接口,但是没有实现接口的方法,所以是一个抽象类。
    (3)Proxy:代理类,Stub的内部类,实现了AIDL接口,并且实现了接口的方法。
    代理模式:

    代理模式

    AIDL原理:
    客户端:Activity
    服务端:Service
    如果客户端和服务端不在同一进程,客户端BindService后,客户端调用asInterface返回的是Proxy对象,它是远程服务端的Stub对象在客户端的本地代理,客户端通过Proxy调用方法时,都会在相应方法的内部通过transact()发起远程请求,然后服务端会根据onTransact()的code参数来调用相应的服务端方法,这样看起来,好像就是客户端直接调用的是服务端的方法,实际上经过了一个transact()和onTransact()的过程。

    客户端和服务端:
    不在同一进程的通信双方看成C/S模式的Client和Server

    四个重要的方法:
    (1)asInterface():将服务端的返回的Binder对象,转换成客户端所需要的AIDL接口类型对象,这种转换是区分进程的,如果服务端和客服端处于同一进程,那么此方法返回服务端Stub对象本身,否则返回的是Proxy对象(AIDL接口);
    (2)onTransact():这个方法运行服务端的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。code:服务端可以根据此确定客户端所请求的目标方法。data:服务端可以取出目标方法所需要的参数,然后执行目标方法。reply:目标方法执行完毕后,向其写入返回结果。
    (3)transact():运行在客户端,发起远程请求,同时将当前线程挂起,然后服务端的onTransact方法会被调用,直到远程请求返回,当前线程继续执行。
    (4)asBinder():返回当前Binder对象;Stub

    Binder断裂处理:
    Binder运行在服务端,如果由于某种原因服务端异常终止了的话会导致客户端的远程调用失败,所以Binder提供了两个配对的方法linkToDeath和unlinkToDeath,通过linkToDeath方法可以给Binder设置一个死亡代理,当Binder死亡的时候客户端就会收到通知,然后就可以重新发起连接请求从而恢复连接了。

    Binder断裂的处理:设置死亡代理,在onServicedConnected充连服务(注意区别)
    Binder的两个重要方法linktoDealth和unlinktoDealth,DeathRecipient;
    (1)声明一个DeathRecipient。是一个接口,内部只有一个方法,binderDied(),在这个方法中移除之前的绑定的binder代理并重新绑定远程服务。移除:binder.unlinkToDeath;
    (2)在客户端绑定远程服务成功后,给binder设置死亡代理:binder.linkToDealth;

    Binder.isBinderAlive()可以判断Binder是否死亡。

    AIDL的使用流程:
    首先创建一个Service和一个AIDL接口,接着在Service中创建一个类继承自AIDL接口中的Stub类,并实现Stub中抽象方法,在onBind方法中返回这个类的对象,然后客户端就可以绑定服务端的Service,建立连接后就可以访问远程服务端的方法了。

    AIDL支持的数据类型:
    (1)基本数据类型;
    (2)String和CharSequence
    (3)ArrayList,HashMap里面的每个元素都必须被AIDL支持;
    (4)Parcelable
    (5)AIDL

    AIDL的使用注意事项:
    (1)自定义的Parcelable对象和AIDL对象必须显示的import进来,不管它们是否和当前的AIDL文件在一个包内;
    (2如果用到了自定义Parcelable对象,那么必须新建一个和它同名的AIDL文件,并在其中声明它为Parcelable类型;
    (3)AIDL中除了基本数据类型,其它的类型参数都必须标上方向;in,out,inout;
    (4)AIDL接口中只支持方法,不支持静态变量;

    AIDL解注册失败原因:
    Binder会把客户端传递过来的对象重新转化并生成一个新的对象,虽然在注册和解注册的过程中使用的是同一个客户端对象,但是经过Binder传到服务端后会生成两个不同的对象;
    对象跨进程传输的实质都是对象反序列化的过程,反序列化生成的对象和序列化之前的对象不是同一个对象;
    虽然多次跨进程传输的同一个客户端对象会在服务端生成不同的对象,但是这些新生成的对象底层的Binder对象是相同的;
    所以我们只需要遍历服务端所有的Listener,找到和解注册Listener具有相同的Binder对象的服务端Listener,删掉即可;
    AIDL的权限验证:
    (1)permission验证:
    1:在服务端的Manifest文件中定义一个权限;
    2:当一个应用来绑定我们的服务时,在onBind方法中验证权限。验证不通过返回null;
    (2)在onTransact中验证 :
    1 验证权限
    2 验证包名

    RemoteCallBackList :
    (1)作用:系统专门提供的用于删除跨进程listener的接口;客户端进程终止后,RemoteCallBackList会自动删除客户端所注册的listener,内部自动实现了线程同步的功能;
    (2)原理:RemoteCallBackList使用了一个ArrayMap来保存所有的<IBinder,CallBack>键值对,CallBack是listener的封装;

    使用文件共享来进行进程间通信:
    使用文件共享可能导致读出的内容不是最新的;
    文件共享的方式适合用于对数据同步要求不高的进程间通信场景,并且要妥善处理并发读写的问题;
    SharedPreferences:底层采用XML文件来存储键值对,SharedPreferences也属于文件的一种,但是由于SharedPreferences在内存中会有一份缓存,所以在多进程模式下,不可靠;

    2.4 Android开发中IPC方式

    IPC方式

    相关文章

      网友评论

        本文标题:IPC机制

        本文链接:https://www.haomeiwen.com/subject/milurttx.html