概要

  • 多进程概念及多进程常见注意事项
  • IPC基础:Android序列化和Binder
  • 跨进程常见的几种通信方式:Bundle通过Intent传递数据,文件共享,ContentProvider,基于Binder的AIDL和Messenger以及Socket。
  • Binder连接池
  • 各种进程间通信方式的优缺点及适用场景

IPC是 Inter-Process Communication的缩写,意为进程间通信跨进程通信,是指两个进程之间进行数据交换的过程。

线程是CPU调度的最小单元,同时线程是一种有限的系统资源。进程一般指一个执行单元,在PC和移动设备上指一个程序或者一个应用。一个进程可以包含多个线程,因此进程和线程是包含与被包含的关系。最简单的情况下,一个进程中只可以有一个线程,即主线程,在Android中也叫UI线程。

IPC不是Android中所独有的,任何一个操作系统都需要相应的IPC机制,比如Windows上可以通过剪贴板等来进行进程间通信。Android是一种基于Linux内核的移动操作系统,它的进程间通信方式并不能完全继承自Linux,它有自己的进程间通信方式。

    1. Android中的多进程模式

通过给四大组件指定android:processs属性可以开启多进程模式,我们无法给一个线程或一个实体类指定其运行所在的进程。

<activity
android:name=".SecondActivity"
android:process=":remote"/>
<activity
android:name=".ThirdActivity"
android:process="com.example.remote"/>

应用默认的进程是当前包名,以上分别给两个Activity指定了进程,意味着当前应用又增加了两个新进程。假设当前应用包名为com.ipc.example,那么SecondActivity所在的进程为com.ipc.example.remote,“:”的含义是要在当前进程名前面附上当前的包名,而且已“:”开头的进程属于当前应用的私有进程,其他应用的组件不可以和它跑在同一进程。不以“:”开头的进程是全局进程,如指定了ThirdActivity所在的进程为全局进程。

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

新建UserManager类:

public class UserManager {
public static int sUserId = 1;
}

在MainActivity中将sUserId的值修改为2,由于MainActivity在应用默认进程,SecondActivity在指定进程,在SecondActivity中打印sUserId的值会发现sUserId值并没有变,还是1。

出现这样的原因是SecondActivity运行在单独的进程,Android为每个应用分配了一个独立的虚拟机或者说为每个进程都分配了一个独立的虚拟机,不同的虚拟机在内存上都有不同的地址空间,这就导致在不同的虚拟机中访问同一个对象会产生多分副本。上面示例中MainActivity修改了sUserId的值只会影响当前进程,对其他进程不会造成 任何影响。

所有运行在不同进程的四大组件,只要它们之间需要通过内存来共享数据都会失败。一般来说多进程会造成以下几个问题:

  • 静态成员和单例模式完全失效
  • 线程同步机制完全失效(不在同一进程,,锁的不是同一对象,所以不管锁对象还是锁全局类都无法保证线程同步)
  • SharePreferences的可靠性下降
  • Application会多次创建(在Application的onCreate()中打印当前进程名证实了同一应用不同进程下,Application会多次创建。这也说明了多进程模式下,不同进程的组件的确会拥有独立的虚拟机、Application以及内存空间)

 3. Serializable和Parcelable接口

Serializable和Parcelable可以完成对象的序列化过程,当我们需要通过Intent和Binder传输数据时就需要进行序列化,Serializable是Java提供的一个空接口,为对象提供标准的序列化和反序列化操作。使用简单但是序列化和反序列都会做大量的I/O读写操作,内存开销较大。

Parcelable是Android中的序列化方式,使用稍微复杂,但是效率高。

 4.  Android中的IPC方式:

     4.1: 使用Bundle

四大组件中的其中三个(Activity、Service、Receiver)都是支持在Intent中传递Bundle数据,由于Bundle实现了Parcelable接口,所以它可以方便的在不同进程间传输。

     4.2:使用文件共享

文件共享也是一种不错的进程间通信方式,两个进程通过读写同一个文件来交换数据,比如A进程中把数据写入文件,B进程通过读取这个文件来获取数据。但是这种方式也是又一定局限性的,比如并发读写的问题会导致我们读出的数据不是最新的,因此要避免并发读写的发生或者考虑使用线程同步来限制多个线程的读写操作。基于这样的问题,文件共享方式适合对数据同步要求不高的进程之间进行通信。

4.3:使用Messenger

Messenger是一种轻量级的IPC方案,它的底层实现是AIDL,通过Messenger可以在不同进程中传递Message对象,在Message中放入我们需要传递的数据就可以轻松实现数据在进程间传递了。

Messenger的使用很简单,由于它一次处理一个请求,因此在服务端不用考虑线程同步的问题。Messenger实现进程间通信大致可以分为以下几步,分为服务端和客户端。

  • 服务端进程
    首先需要在服务端新建一个Service来处理客户端发起的请求,同时创建一个Handler并通过它来创建一个Messenger对象,然后在Service的onBind中返回这个Messenger对象底层的Binder即可。
  • 客户端进程
    客户端进程中首先要绑定服务端的Service,绑定成功后用服务端返回的IBinder对象创建一个Messenger,通过这个Messenger就可以向服务端发送类型为Message的消息了。

注册Service,使其运行在单独的进程,AndroidManifest.xml中配置:

Log日志:

在Messenger中进行数据传递必须将数据放入Message中,而Messenger和Message都实现了Parcelable接口,因此可以跨进程传输。上面的实例只是介绍了如何在服务端接受客户端中发送的请求,但是有时候还需要回应客户端。每当客户端发来一条消息,服务端就自动回复一条“来自服务端的自动回复:消息已收到”。如果需要客服端能够回应客户端,那么和服务端一样,在客户端还需要创建一个新的Messenger,并把这个Messenger对象通过Message的replyTo参数传递给服务端,服务端通过这个replyTo参数就可以回应客户端。

修改后的服务端代码:

        

修改后的客户端代码:

       

4.4: 使用AIDL

Messenger是以串行的方式处理客户端的请求,如果有大量的请求同时发送到服务端,服务端仍然只能一个一个处理,此时Messenger就不大合适了,同时Messenger的作用是为了传递消息,且只能传递Bundle支持的数据类型,很多时候需要跨进程调用服务端的方法,这时候可以实用AIDL来实现跨进程的方法调用。AIDL也是Messenger的底层实现,因此Messenger本质上也是AIDL,只不过系统作了封装。实用AIDL进行进程间通信的流程分为服务端和客户端两个方面,下面分别介绍:

    • 服务端
      服务端首先要创建一个Service用来监听客户端的连接请求,然后创建一个AIDL文件将暴露给客户端的接口在这个AIDL文件中声明,最后在Service中实现这个AIDL接口即可
    • 客户端
      客户端要做的事情稍微简单些,首先绑定服务端的Service,绑定成功后将服务端返回的Binder对象转成AIDL接口所属的类型,接着就可以调用AIDL中的方法了
    • AIDL接口的创建
      详情参考:https://www.zhihu.com/question/21581761

      需要注意的是,如果AIDL文件中用到了自定义的Parcelable对象,那么必须新建一个和它同名的AIDL文件,并在其中声明为Parcelable类型。上面代码中我们用到了BookEntity这个类,所以必须创建BookEntity.aidl,然后将BookEntity定义为Parcelable类型。

    • 服务端的实现


运行在独立进程:

<service
android:name=".service.BookManagerService"
android:process="com.ipc.example.remote"/>
  • 客户端实现

这是一次完整的使用AIDL实现进程间通信,但是远远还没有完,AIDL的复杂性远不止这些。下一篇着重总结AIDL的使用。

Android IPC机制全解析<一>的更多相关文章

  1. Android IPC机制全解析&lt;二&gt;

    在AIDL文件中并不是所有的数据类型都可以使用,AIDL支持的数据类型如下: 基本数据类型(int.long.char.boolean.double等) String和CharSequence Lis ...

  2. Android异步载入全解析之使用多线程

    异步载入之使用多线程 初次尝试 异步.异步,事实上说白了就是多任务处理.也就是多线程执行.多线程那就会有各种问题,我们一步步来看.首先.我们创建一个class--ImageLoaderWithoutC ...

  3. Android异步载入全解析之IntentService

    Android异步载入全解析之IntentService 搞什么IntentService 前面我们说了那么多,异步处理都使用钦定的AsyncTask.再不济也使用的Thread,那么这个Intent ...

  4. Android IPC机制(三)在Android Studio中使用AIDL实现跨进程方法调用

    在上一篇文章Android IPC机制(二)用Messenger进行进程间通信中我们介绍了使用Messenger来进行进程间通信的方法.可是我们能发现Messenger是以串行的方式来处理client ...

  5. Android异步载入全解析之大图处理

    Android异步载入全解析之大图处理 异步载入中很重要的一部分就是对图像的处理,这也是我们前面用异步载入图像做示例的原因. 一方面是由于图像处理不好的话会很占内存,并且easyOOM,还有一方面,图 ...

  6. Android异步载入全解析之使用AsyncTask

    Android异步载入全解析之使用AsyncTask 概述 既然前面提到了多线程,就不得不提到线程池,通过线程池,不仅能够对并发线程进行管理.更能够提高他们运行的效率.优化整个App.当然我们能够自己 ...

  7. Handler消息机制与Binder IPC机制完全解析

    1.Handler消息机制 序列 文章 0 Android消息机制-Handler(framework篇) 1 Android消息机制-Handler(native篇) 2 Android消息机制-H ...

  8. 深入理解Android IPC机制之Binder机制

    Binder是Android系统进程间通信(IPC)方式之一.Linux已经拥有的进程间通信IPC手段包括(Internet Process Connection): 管道(Pipe).信号(Sign ...

  9. Android IPC机制基础

    概要 多进程概念及多进程常见注意事项 IPC基础:Android序列化和Binder 跨进程常见的几种通信方式:Bundle通过Intent传递数据,文件共享,ContentProvider,基于Bi ...

随机推荐

  1. flask_sqlalchemy 乱码问题

    简言之, /etc/my.conf default_character_set=utf8 配置成了 default_character_set=utf-8 继而 sqlalchemy 创建表使用的字符 ...

  2. SQL2008中的Sequence

        CREATE TABLE dbo.GlobalSequence ( id INT IDENTITY(1, 1) ) GO     CREATE PROC seq ( @id INT OUTPU ...

  3. Qt之qSetMessagePattern

    简述 改变默认的消息处理输出. 允许改变qDebug().qWarning().qCritical().qFatal()的输出. 简述 占位符 示例 qSetMessagePattern QT_MES ...

  4. Linux 内核 链表 的简单模拟(1)

    第零章:扯扯淡 出一个有意思的题目:用一个宏定义FIND求一个结构体struct里某个变量相对struc的编移量,如 struct student { int a; //FIND(struct stu ...

  5. POJ2225+BFS

    简单的BFS   1a /* 从起点到终点 */ #include<stdio.h> #include<string.h> #include<stdlib.h> # ...

  6. [国嵌攻略][092][UDP网络程序设计]

    server.c #include <sys/socket.h> #include <netinet/in.h> #include <strings.h> #inc ...

  7. openlayers3 基础(常见方法,类及实现)

    ol3接口大全1.ol.Map类:(地图容器类) 实现: ol.Map(参数) 参数说明:1.1 target,说明地图所在的html元素. 如果没有指定,必须调用ol.Map类的setTarget( ...

  8. aglio报错解决

    Cannot write or read cache for themes (ENOENT on cache folder) aglio -i ./api.md -o api.html >> ...

  9. MYSQL 如何完全卸载数据库

    有时候MySQL不能完全卸载,这时候必须通过一些途径删除掉注册表和一些残余的文件,然后才能重新安装才可以成功! 1.控制面板——>所有控制面板项——>程序和功能,卸载mysql serve ...

  10. HttpWebRequest 模拟浏览器访问网站

    最近抓网页时报错: 要么返回 The remote server returned an error: (442)要么返回: 非法访问,您的行为已被WAF系统记录! 想了想,就当是人家加了抓网页的东西 ...