程序员人生 网站导航

Android 之 IPC 进程通信全解析

栏目:综合技术时间:2016-07-06 13:39:51

Android 之 IPC 进程通讯全解析

本篇博客的框架

这里写图片描述

甚么是IPC

IPC(Inter-Process Communication) 进程间通讯,是指两个不同进程之间数据交换的进程。

在明确其之前,需要先弄懂几个概念:

  • 线程:CPU可调度的最小单位,是程序履行流的最小单元;线程是进程中的1个实体,是被系统独立调度和分派的基本单位,线程自己不具有系统资源,只具有1点儿在运行中必不可少的资源,但它可与同属1个进程的其它线程同享进程所具有的全部资源。
  • 进程: 1个履行单元,在PC 和移动装备上1般指1个程序或利用,1个进程可以包括多个线程。每个进程都有它自己的地址空间,1般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。

在Android程序中,1般情况下1个程序就是1个进程(在无特别的代码实现下),UI线程即主线程。如果有耗时操作,则会致使主线程堵死。而在Android中主线程负责UI,和用户交互,如果堵死肯定影响用户的使用度。所以Android要求不能再主线程中有耗时操作,这时候就要将耗时操作放在子线程中。

IPC 的使用处景

  • 程序由于本身缘由,需要采取多进程模式来实现。
    • 有些模块由于特殊缘由需要运行运行在单独的进程中。
    • 为了加大1个利用可以使用的内存所以需要通过量进程来获得内存空间。
  • 当前利用需要向其他利用获得数据。由因而两个利用,即两个进程。

在Android 中,每个利用可以使用的内存大小有限制,早起的1些版本在16M左右,不同的装备有不同的大小。可以通过量进程获得多分内存空间。

Android中的多进程

如何开启多进程

Android中开启多进程只有1种方法,便是给4大组件(ActivityReceiverContentProviderService)指定android
:process
属性,初次以外没有其他方法。请注意,不能指定某1个线程或实体类指定其所运行的进程

通过jni调用底层去开启多进程也是1种方法,但属于特殊情况,不进行斟酌;

首先编写3个Activity,并在清单文件中做以下注册:

<activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="com.example.ipc.SecondActivity" android:process=":remote" /> <activity android:name="com.example.ipc.ThirdActivity" android:process=".remote" />

MainActivity不进行指定,则默许为当前进程。

SecondActivity指定属性android:process=":remote"

ThirdActivity指定属性android:process=".remote"

注意SencodActivityThirdActivity的进程参数不同。

把3个页面都打开,通过DDMS可以看到3个进程的开启

这里写图片描述

启动了3个进程:分别是

  • com.example.ipc:默许的程序进程。和包名相同。
  • com.example.ipc:remoteSecondActivity所在的进程。
  • .remoteThirdActivity所在的进程。

那末2和3 ,进程名不同有甚么区分吗;

  • 如果进程名以:开始,表示是要在当前的进程名前附加上当前的包名,表示该进程是本利用的私有进程,其他利用不可以和其跑在同1个进程。
  • 如果进程名不以:开始,表示不需附加包名信息,是1个完全的命名。同时该进程是全局进程,其他利用可以通过ShareUID和其跑在同1个进程中。

开启多进程存在的问题

通过如上方式,很简单的变开启了多进程,但是,如果仅仅这样的话,会有大问题。

看下面1个例子:

添加1个公有的类,添加静态字段:

/** * 类似平常开发中的工具类等 * @author MH * */ public class PublicContant { public static int m = 1; }

MainActivity中Log1下并修改字段

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.i("info", PublicContant.m+""); PublicContant.m++; }

SecondActivity中Log1下:

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); Log.i("info", PublicContant.m+""); }

根据上面的逻辑,Log信息应当是1,和2 。但是呢,不是这样的。

这里写图片描述
两个都是1,我靠。。先不问缘由,看结果就知道是错的。多进程这么不靠谱,肿么回事??

Android 为每个进程都分配1个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,这就致使在不同虚拟机中访问同1个类对象会产生多个副本。

对当前来讲,进程com.example.ipccom.example.ipc:remote都存在1个PublicContant类,并且这两个类是相互不干扰的,1个进程中修改了该值的对象,对其他进程中的该值不会造成任何影响。

运行在同1个进程中的组件是属于同1个虚拟机和同1个Application的。同理,运行在不同进程中的组件是属于两个不同的虚拟机和Application的。

根据如上所述,多进程所酿成的问题分为以下几个方面:

  • 静态成员和单例模式完全失效
    • 如上分析,创建了不同的内存,多个对象,固然单例甚么的都无效了。
  • 线程同步机制完全失效
    • 不是1块内存区域,线程锁固然无效了。
  • SharedPreference的可靠性降落
    • sharedPreference的底层实现是通过读写XML文件,两个进程去读写,并发明显是可能出现问题的。
  • Application会屡次创建

序列化和反序列化

在了解多进程通讯之前,我们需要了解两个基础的概念,序列化和反序列化。

  • 序列化:将对象转化为可保存的字节序列。(注意,是对象)。
  • 反序列:将字节序列恢复为对象的进程。

序列化和反序列的用处:

  • 以某种存储情势使自定义对象序列化。
  • 将对象从1个地方传递到另外一个地方。
  • 通过序列化在进程间传递对象。

Android中实现序列化的方式有两种,SerializableParcelable

Serializable

Serializable是Java提供的1个序列化接口,他是1个空接口,是类实现该接口便可实现序列化。

/** * Serializable 序列化对象 * @author MH * */ public class Book implements Serializable { /** * 序列化和反序列的关键 */ private static final long serialVersionUID = 1L; public int bookId; public String bookName; }

在实现Serializable时候,编译器会提示,让我们添加serialVersionUID字段,该字段是1个关键的字段,后面会说。

相应的实现好了,那末如何写入和读取呢?

  • 写入
public void writeSerializable() { try { // 构造对象 Book book = new Book(); // 构造序列化输出字节流 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("xxx.txt")); // 序列化对象 oos.writeObject(book); // 关闭流 oos.close(); } catch (Exception e) { e.printStackTrace(); } }
  • 读取
public void readSerializable() { try { // 创建序列化读取字节流 ObjectInputStream ois = new ObjectInputStream(new FileInputStream( "xxx.txt")); // 反序列化(读取)对象 Book book = (Book) ois.readObject(); // 关闭流 ois.close(); } catch (Exception e) { e.printStackTrace(); } }

在序列化时,如果我们序列化对象以后,改变了我们的类结构(添加或改变字段),乃至是修改了字段的类型,修改了类名,那末我们能反序列化成功吗。

那末关键就在于serialVersionUID字段。

如果我们不指定的话。在序列化时,会计算当前类结构的hash值并将该值赋给serialVersionUID,当反序列时,会比对该值是不是相同,如果不相同,则没法序列化成功。

我们也能够手动指定,手动指定的好处是在类结构产生变化时,能够最大程度的反序列,固然条件是只是删除或添加了字段,如果是变量类型产生了变化,则仍然没法反序列成功。

serialVersionUID 的工作机制:序列化时系统会把当前类的serialVersionUID写入序列化文件中,当反序列化时候系统会去检测文件中的serialVersionUID,看它是不是和当前类的serialVersionUID1致,如果1致说明序列化类的版本和当前类的版本是相同的,这个时候可以成功反序列化,否则就说明当前类和序列化的类相比产生了某些变化。所以,我们最好指定serialVersionUID,避免他自定生成。

Parcelable

Parcelable是Android中独有的1种序列化方式,在intent传值时,通常使用该方式。

该方式实现序列化,仍然实现Parcelable,然后实现1些该接口的方法。

/** * Parcelable 对象的使用方式 * @author MH * */ public class Book implements Parcelable { public int bookId; public String bookName; @Override public int describeContents() { // 返回当前对象的内容描写。几近所有情况下都是返回0 return 0; } @Override public void writeToParcel(Parcel dest, int flags) { // 将当前对象写入到序列化结构中 dest.writeInt(bookId); dest.writeString(bookName); } public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() { @Override public Book createFromParcel(Parcel source) { //从序列化后的对象中创建原始的值 Book book = new Book(); book.bookId = source.readInt(); book.bookName = source.readString(); return book; } @Override public Book[] newArray(int size) { //创建指定长度的原始对象数组 return new Book[size]; } }; }

Parcelable实现两个方法,创建1个字段:

  • 实现describeContents():返回当前对象的内容描写。几近所有情况下都是返回0。
  • 实现public void writeToParcel(Parcel dest, int flags):// 将当前对象写入到序列化结构中
  • 构造Parcelable.Creator字段,该对象需要实现两个方法:
    • public Book createFromParcel(Parcel source):从序列化后的对象中创建原始的值。
    • public Book[] newArray(int size):创建指定长度的原始对象数组

Serializable和Parcelable的比较

  • Serializable是Java中的序列化接口,其使用起来简单但是开消较大,序列化和反序列化需要大量的I/O操作。
  • Parcelable是Android中的序列化方式,更适用于Android的平台上,他的缺点是使用起来略微麻烦,但是效力很高。
  • Parcelable合适进程间的通讯,运行期。Serializable合适文件存储即网络传输。

Android 进程间通讯的方式

使用Bundle 传输数据

Android中的4大组件中,其中有3大组件(Activity,Service,Receiver)都支持Intent中传递Bundle数据,如果看其源码,会发现其也是实现了Parcelable接口,所以其能够在不同进程中传输。

固然在传输的进程中,其所传输的数据必须支持序列化。比如基本数据类型,字符串,Parcelable的实现类,Serializable的实现类。由于该方法非常经常使用,不在多说。

文件同享

文件同享: 将对象序列化以后保存到文件中,在通过反序列,将对象从文件中读取。

MainActvity中写写入对象

/** * 写入序列化对象 */ public void wirte() { Book book = new Book(); book.bookId = 1; book.bookName = "si"; try { // 构造序列化输出字节流 ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream(PATH)); // 序列化对象 oos.writeObject(book); // 关闭流 oos.close(); } catch (Exception e) { e.printStackTrace(); } System.out.println(book); }

SecondActivity中,读取文件(反序列化)

public void read() { Book book = null; try { // 创建序列化读取字节流 ObjectInputStream ois = new ObjectInputStream(new FileInputStream( MainActivity.PATH)); // 反序列化(读取)对象 book = (Book) ois.readObject(); // 关闭流 ois.close(); } catch (Exception e) { e.printStackTrace(); } System.out.println(book); }

LOG 结果

06-28 09:20:47.916: com.example.ipc(进程名) I/System.out(12399): Book [bookId=1, bookName=si] 06-28 09:20:53.376: com.example.ipc:remote(进程名) I/System.out(12866): Book [bookId=1, bookName=si]

分属不同的进程成功的获得到了同享的数据。

通过同享文件这类方式来同享数据对文件的格式是没有具体的要求的。比如可以是文件,也能够是Xml,JSON 等。只要读写双方约定1定的格式便可。

同文件同享方式也存在着很大的局限性。即并发读/ 写的问题。读/写会造成数据不是最新。同写很明显会出现毛病。

文件同享合适在对数据同步要求不高的进程之间进行通讯。并且要妥善处理并发读写的问题。

SharedPreference 底层文件的方式。不合适在多进程中同享数据。

Messenger

Messenger 可以翻译为信使,通过该对象,可以在不同的进程中传递Message对象。注意,两个单词不同。

下面就通过服务端(Service)和客户端(Activity)的方式进行演示。

客户端向服务端发送消息,可分为以下几步。

服务端

  • 创建Service
  • 构造Handler对象,实现handlerMessage方法。
  • 通过Handler对象构造Messenger信使对象。
  • 通过ServiceonBind()返回信使中的Binder对象。

客户端

  • 创建Actvity
  • 绑定服务
  • 创建ServiceConnection,监听绑定服务的回调。
  • 通过onServiceConnected()方法的参数,构造客户端Messenger对象
  • 通过Messenger向服务端发送消息。

实现服务端

/** * Messenger 的使用 服务端 * @author MH * */ public class MessengerService extends Service { /** * 构建handler 对象 */ public static Handler handler = new Handler(){ public void handleMessage(android.os.Message msg) { // 接受客户端发送的消息 String msgClient = msg.getData().getString("msg"); Log.i("messenger","接收到客户真个消息--"+msgClient); }; }; // 通过handler 构建Mesenger 对象 private final Messenger messenger = new Messenger(handler); @Override public IBinder onBind(Intent intent) { // 返回binder 对象 return messenger.getBinder(); } }

注册服务别忘了 ,同时对服务修改其进程。

实现客户端

/** * Messenger 的使用 客户端 * * @author MH * */ public class MessengerActivity extends AppCompatActivity { /** * Messenger 对象 */ private Messenger mService; private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { // IBinder 对象 // 通过服务端返回的Binder 对象 构造Messenger mService = new Messenger(service); Log.i("messenger", "客户端以获得服务端Messenger对象"); } @Override public void onServiceDisconnected(ComponentName name) { } }; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_messenger); // 启动服务 Intent intent = new Intent(this, MessengerService.class); bindService(intent, conn, BIND_AUTO_CREATE); } /** * 布局文件中添加了1个按钮,点击该按钮的处理方法 * @param view */ public void send(View view) { try { // 向服务端发送消息 Message message = Message.obtain(); Bundle data = new Bundle(); data.putString("msg", "lalala"); message.setData(data); // 发送消息 mService.send(message); Log.i("messenger","向服务端发送了消息"); } catch (Exception e) { e.printStackTrace(); } } }

看1下结果:

这里写图片描述

注释很清楚,不在多说,依照流程实现便可。 其中有1点需要注意:

我们是通过Message作为媒介去携带数据的。但是,Message的obj 并没有实现序列化(实现SerializableParcelable),也就是其不能保存数据。必须使用message.setData()方法去传入1个Bundle对象,Bundle中保存需要传入的数据。

传递时使用的是Messenger.send(Message)方法。

服务端向客户端发送了消息,那末服务端向客户端发送消息也类似:

关键点: 客户端向服务端发送消息是,通过msg.replyTo将客户端Messenger对象传给服务端。

客户端代码进行修改:

  • 创建客户端HandlerMessenger对象。
  • 修改send()方法。
/** * 构建handler 对象 */ public static Handler handler = new Handler(){ public void handleMessage(android.os.Message msg) { // 接受服务端发送的消息 String msgService = msg.getData().getString("msg"); Log.i("messenger","接收到服务真个消息--"+msgService); }; }; // 通过handler 构建Mesenger 对象 private final Messenger messengerClient = new Messenger(handler); /** * 布局文件中添加了1个按钮,点击该按钮的处理方法 * @param view */ public void send(View view) { try { // 向服务端发送消息 Message message = Message.obtain(); Bundle data = new Bundle(); data.putString("msg", "lalala"); message.setData(data); // ----- 传入Messenger 对象 message.replyTo = messengerClient; // 发送消息 mService.send(message); Log.i("messenger","向服务端发送了消息"); } catch (Exception e) { e.printStackTrace(); } }

服务端代码修改

/** * 构建handler 对象 */ public static Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { // 接受客户端发送的消息 String msgClient = msg.getData().getString("msg"); Log.i("messenger", "接收到客户真个消息--" + msgClient); // 获得客户端Messenger 对象 Messenger messengetClient = msg.replyTo; // 向客户端发送消息 Message message = Message.obtain(); Bundle data = new Bundle(); data.putString("msg", "ccccc"); message.setData(data); try { // 发送消息 messengetClient.send(message); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } }; };

结果不在演示了。

AIDL

AIDL是1种接口定义语言,用于束缚两个进程间的通讯规则,供编译器生成代码,实现Android装备上的两个进程间通讯(IPC)。

进程之间的通讯信息,首先会被转换成AIDL协议消息,然后发送给对方,对方收到AIDL协议消息后再转换成相应的对象。

AIDL的关键便是Binder,关于Binder,后面的博客会分析。在这里之将如何使用它。

由于需要服务端和客户端共用aidl文件,所以最好单独建1个包,合适拷贝到客户端。

服务端:

  • 添加以下包名:com.example.ipc.aidl
  • 创建BookAidl.java,该对象需要作为传输。所以需要实现Parcelable
public class BookAidl implements Parcelable { public int bookId; public String bookName; public BookAidl() { super(); } public BookAidl(int bookId, String bookName) { super(); this.bookId = bookId; this.bookName = bookName; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(bookId); dest.writeString(bookName); } public static final Parcelable.Creator<BookAidl> CREATOR = new Creator<BookAidl>() { @Override public BookAidl[] newArray(int size) { return new BookAidl[size]; } @Override public BookAidl createFromParcel(Parcel source) { BookAidl book = new BookAidl(); book.bookId = source.readInt(); book.bookName = source.readString(); return book; } }; @Override public String toString() { return "BookAidl [bookId=" + bookId + ", bookName=" + bookName + "]"; } }

Parcelable在前面已说过,不在多说。

  • 创建.aidl文件。由于需要用到BookAidl对象,所以需要先声明。

创建BookAidl.aidl文件,并手动添加

package com.example.ipc.aidl; parcelable BookAidl;

创建IBookManager.aidl文件,接口文件,面向客户端调用

package com.example.ipc.aidl; import com.example.ipc.aidl.BookAidl; interface IBookManager{ List<BookAidl> getBookList(); void addBook(in BookAidl book); }

写完以后clean1下工程,以后会在gen目录下生成对应的java文件。此java中的具体含义后面会解释,在此不做多述。

  • 继续编写服务端,创建Service类。
public class BookService extends Service { /** * 支持线程同步,由于其存在多个客户端同时连接的情况 */ private CopyOnWriteArrayList<BookAidl> list = new CopyOnWriteArrayList<>(); /** * 构造 aidl中声明的接口的Stub对象,并实现所声明的方法 */ private Binder mBinder = new IBookManager.Stub() { @Override public List<BookAidl> getBookList() throws RemoteException { return list; } @Override public void addBook(BookAidl book) throws RemoteException { list.add(book); Log.i("aidl", "服务端添加了1本书"+book.toString()); } }; @Override public void onCreate() { super.onCreate(); //加点书 list.add(new BookAidl(1, "java")); list.add(new BookAidl(2, "android")); } @Override public IBinder onBind(Intent intent) { // 返回给客户真个Binder对象 return mBinder; } }

Service中,主要干了两件事情:

  • 实现aidl文件中的接口的Stub对象。并实现方法。
  • Binder对象通过onBinder返回给客户端。

为了省事,在这里不在另起1个工程了,直接将Service在另外一个进程中运行。

<service android:name="com.example.ipc.BookService" android:process=":remote" />

开始编写客户端

由于在同1个工程中,不需要拷贝aidl包中的文件。如果不在同1个工程,需要拷贝。

public class BookActivity extends AppCompatActivity{ /** * 接口对象 */ private IBookManager mService; /** * 绑定服务的回调 */ private ServiceConnection conn = new ServiceConnection(){ @Override public void onServiceConnected(ComponentName name, IBinder service) { // 获得到书籍管理的对象 mService = IBookManager.Stub.asInterface(service); Log.i("aidl", "连接到服务端,获得IBookManager的对象"); } @Override public void onServiceDisconnected(ComponentName name) { } }; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_book); // 启动服务 Intent intent = new Intent(this,BookService.class); bindService(intent, conn, BIND_AUTO_CREATE); } /** * 获得服务端书籍列表 * @param view */ public void getBookList(View view){ try { Log.i("aidl","客户端查询书籍"+mService.getBookList().toString()); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 添加书籍 */ public void add(View view){ try { // 调用服务端添加书籍 mService.addBook(new BookAidl(3,"ios")); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }

客户真个代码和之前的Messenger很类似:

  • 绑定服务,监听回调。
  • 将回调中的IBinder service通过IBookManager.Stub.asInterface()转化为借口对象。
  • 调用借口对象的方法。

效果

这里写图片描述

总结来讲可分为以下几步

服务端:

  • 服务端创建.aidl文件和声明接口
  • 创建类,继承Service,并实现onBind方法
  • 在Service类中定义aidl中声明接口的Stub对象,并实现aidl接口中声明的方法
  • 在onBind方法中返回Stub对象
  • 在AndroidManifest.xml中注册Service并声明其Action

客户端

  • 使用服务端提供的aidl文件
  • 在Activity定义aidl接口对象
  • 定义ServiceConnection对象,监听绑定服务的回调
  • 回调中通过方法获得借口对象

ContentProvider

作为android 4大组件之1,虽然用的地方不是太多。但是其确切是多进程通讯的1种方式。例如,获得通讯录信息,这明显跨利用了,肯定是多进程通讯啊。

其底层实现和Messenger1样,都是通过Binder,后面会专门分析Binder对象。

ContentProvider很多介绍,在这不在多提。

Socket实现

Socket也称为“套接字”,是网络通讯中的概念,它分为流式套接字和用户数据报套接字,分别对应于网络传输中的传输控制层的TCP和UDP。

该方面使用的是JAVA 方面的知识。该举例只是说明1个思路。不做细致的实现。

服务端

public class SocketService extends Service { /** * 连接的状态 */ private boolean isConnState = true; @Override public void onCreate() { super.onCreate(); // 启动TCP 服务 new Thread(new TCPServer()).start(); } @Override public IBinder onBind(Intent intent) { return null; } @Override public void onDestroy() { // 结束TCP 服务 isConnState = false; super.onDestroy(); } /** * 服务端TCP 服务,相当于服务器,接受Socket 连接 * @author MH * */ class TCPServer implements Runnable{ @Override public void run() { try { // 监听本地的12345 端口 ServerSocket ss = new ServerSocket(12345); while(isConnState){ // 获得客户真个Socket 对象 Socket socket = ss.accept(); // 获得输入流 --- BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); // 通过输入流读取客户真个消息 //String line = br.readLine(); // 输出流 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); // 通过输出流向客户端发送消息 //bw.write("...."); // 关闭连接 socket.close(); } } catch (Exception e) { e.printStackTrace(); } } } }

服务启动时,在onCreate方法中启动了TCPServer,该线程时刻接受客户真个要求。

客户端

public void conn(){ try { // 指定ip和端口 Socket s = new Socket("localhost", 12345); // ----- 和服务端类似 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); //String line = br.readLine(); // 输出流 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); //bw.write("...."); // 关闭连接 s.close(); } catch (Exception e) { e.printStackTrace(); } }

关于Socket,在此只是1个简单的示范,具体的使用博大精深。知道能实现便可。

Android 进程间通讯不同方式的比较

  • Bundle:4大组件间的进程间通讯方式,简单易用,但传输的数据类型受限。
  • 文件同享: 不合适高并发场景,并且没法做到进程间的及时通讯。
  • Messenger: 数据通过Message传输,只能传输Bundle支持的类型
  • ContentProvider:android 系统提供的。简单易用。但使用受限,只能根据特定规则访问数据。
  • AIDL:功能强大,支持实时通讯,但使用略微复杂。
  • Socket:网络数据交换的经常使用方式。不推荐使用。

TO DO

Binder 的细致分析

在实现多进程通讯时,其中Messenger,ContentProvider,AIDL的底层实现都是Binder,很有必要对其进行继续分析。

该博客中的代码以同享到github。 https://github.com/AlexSmille/Android-IPC-Example

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐