【Android】IPC(进程间通信)

xiaoxiao2025-04-03  9

文章目录

Android中的多进程模式1、开启多进程模式2、多进程模式的运行机制 IPC概念介绍1、Serializable接口2、Parcelable接口3、Binder Android中的IPC方式1、Bundle2、使用文件共享3、使用Messenger4、使用AIDL(Android 接口定义语言)5、使用ContentProvider6、使用Socket IPC是Inter-process communication的缩写,含义为进程间通信或者跨进程通信

Android中的多进程模式

1、开启多进程模式

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

<activity android:name=".task.TasksActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".addedittask.AddEditTaskActivity" android:process=":remote"/> <activity android:name=".taskdetail.TaskDetailActivity" android:process="com.ljh.mytodo.remote"/>

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

 进程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系统会给每个进程都分配一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间。   使用多进程会造成如下问题:

静态成元和单例模式完全失效线程同步机制完全失效  不在同一块内存中,锁对象或者锁全局类都不能保证线程同步,因为锁的对象不在同一进程中SharedPreferences的可靠性下降  SharedPreferences不支持两个进程同时读写xml文件来实现,并发读/写可能出现问题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<User> CREATOR = new Parcelable.Creator<user>(){ 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方法不管是否消耗都应该给采用同步的方式去实现,因为她已经运行在一个线程中。 按照上述思想,我们也可以手动写一个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参数就可以回应客户端了。

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来结束通话线程。

转载请注明原文地址: https://www.6miu.com/read-5027388.html

最新回复(0)