进程间通信IPC

IPC是“Inter-Process Communication”的缩写,即进程间通信。Android为APP提供了多进程工作模式,这是因为多线程存在若干局限:
1、多线程共存于一个进程中,而该进程可用的内存容量是固定的,多线程不会拓展app可用的内存大小。所以如果app的性能瓶颈在内存,那么多线程并不能提高处理速度。
2、app在响应用户操作之外,还想完成某些系统管理的任务,比如说双守护进程防止被意外杀掉、比如说app集成第三方插件要定期推送消息,以及其他类似服务端系统管理的功能。
另外,进程间通信不局限于两个APP进程直接通信,也包括APP与系统进程通信,以及进程间通过文件、广播等手段间接通信。

开启多进程

APP开启多进程需要同时修改配置和代码。

配置修改
在AndroidManifest.xml给service节点增加process属性,表示该服务运行在指定进程上。process属性有两种赋值方式:
1、使用相对路径,在进程名前加冒号:android:process=":another"。该方式表示指定服务运行于名称为“当前包名:another”。如果当前包名为“com.example.exmprocess”,那么服务运行的进程名是“com.example.exmprocess:another”
2、使用绝对路径:com.example.exmprocess.another。该方式表示指定服务运行于名称为“com.example.exmprocess.another”。
这两种方式除了命名上的区别,还有权限上的区别。前一种方式表示该进程是私有的,只有本APP的其它进程才能访问它。后一种方式表示该进程是公共的,其他APP只要声明拥有它的权限,那么其他APP也可以与之通信。

代码修改
多进程模式下启动服务,只能通过bindService来启动,不能通过startService来启动。在《 Android开发笔记(四十一)Service的生命周期》中,我们知道bindService是先启动一个服务,然后再绑定它;而startService是直接在主线程中开启服务,所以start方式不能用于多进程模式。
进程间通信除了借助于Handler,还得叫来信使Messenger来帮忙,Messenger担负着传递请求消息与应答消息的重任。

信使Messenger

在之前的《 Android开发笔记(四十八)Thread类实现多线程》,博主提到Message的replyTo字段只用于跨进程通信,下面再具体说明Message在多线程和多进程模式下的区别:
1、obj字段:只可用于线程间通信,不可用于进程间通信。因为Messenger是个Parcelable对象,而obj是Object类型,无法进行序列化。
2、replyTo字段:只用于进程间通信。存放的是应答信使的对象。
3、setData和getData方法:进程间通信只能通过setData发送消息、getData获取消息,因为Bundle继承自Parcelable。线程间通信也可使用这两个方法。

下面是Messenger的常用方法:
Messenger(Handler target) : 构造函数,传入当前进程的Handler对象。该方式创建了一个持有当前进程实例的本地信使,本地信使会收到并处理消息。
Messenger(IBinder target) : 构造函数,传入对方进程的IBinder对象。该方式创建了一个持有对方进程实例的远程信使,远程信使只能向对方进程发送消息。
send : 发送消息。用于客户端向服务端发送请求消息,以及服务端向客户端发送应答消息。
getBinder : 获得当前信使的IBinder,一般用在服务的onBind方法中返回IBinder对象。

为方便记忆Messenger的工作流程,博主经过测试得出了下列三个场景的消息传递流程:
绑定信使的流程: 客户端bindService->服务端onCreate(根据Handler构造接收信使)->onBind(调用getBinder方法返回IBinder)->客户端onServiceConnected(根据IBinder构造发送信使)
请求信息发送/接收的流程:客户端准备(根据Handler构造应答信使)->发送信使send(传入信息内容与应答信使)->服务端handleMessage(根据replyTo构造反馈信使与数据处理)
应答信息返回/完成的流程:服务端反馈信使send->客户端handleMessage(数据处理)

IBinder

下面这段介绍翻译自Android的开发文档:IBinder是远程对象的基本接口,是为高性能而设计的轻量级远程调用机制的核心部分。但它不仅用于远程调用,也用于进程内调用。这个接口定义了与远程对象交互的协议。不要直接实现这个接口,而应该从Binder派生。简而言之,Android的跨进程通信是通过IBinder实现的。
使用Messenger传达IBinder对象的目的之一,是onServiceConnected方法中如果服务运行于另外一个进程,则不能对IBinder对象直接强制转换类型,否则会报错“java.lang.ClassCastException: android.os.BinderProxy cannot be cast to...”。如果FirstService声明了运行于单独进程“android:process=":message"”,则下面这个代码在类型转换时就会抛出异常:

    private FirstService mFirstService;private ServiceConnection mFirstConn = new ServiceConnection() {public void onServiceConnected(ComponentName name, IBinder service) {mFirstService = ((FirstService.LocalBinder) service).getService();}public void onServiceDisconnected(ComponentName name) {mFirstService = null;}};

IBinder的主要API是transact(),与它对应另一方法是Binder.onTransact()。第一个方法使你可以向远端的IBinder对象发送请求,第二个方法使你自己的远程对象能够接收响应。IBinder的API都是同步执行的,比如transact()直到对方的Binder.onTransact()方法调用完成后才返回。

在操作远程对象时,若要查看它们是否有效,有三种方法可以使用:
1、transact:该方法将在IBinder所在的进程不存在时抛出RemoteException异常。
2、pingBinder:如果目标进程不存在,那么调用该方法时将返回false。
3、linkToDeath:通过该方法向IBinder注册一个IBinder.DeathRecipient,在IBinder代表的进程退出时被调用。

Messenger方式一般不需要重写IBinder。

使用示例

下面是多进程模式MessageService的示例代码:

import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;public class MessageService extends Service {private static final String TAG = "MessageService";private Messenger mRecvMsg;private Messenger mReplyMsg;private Bundle mBundle;Handler mServerHandler = new Handler(){public void handleMessage(Message msg) {mReplyMsg = msg.replyTo;mBundle = msg.getData();new ReplyThread().start();}};class ReplyThread extends Thread {public void run() {String desc = String.format("请求参数为%s,应答参数为%s", mBundle.getString("msg"), "bbb");Bundle bundle = new Bundle();bundle.putString("msg", desc);Message msg = Message.obtain();msg.setData(bundle);try {mReplyMsg.send(msg);} catch (RemoteException e) {e.printStackTrace();}}}@Overridepublic void onCreate() {mRecvMsg = new Messenger(mServerHandler);super.onCreate();}@Overridepublic IBinder onBind(Intent intent) {return mRecvMsg.getBinder();}}

下面是主进程调用服务的代码:

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;public class MessengerActivity extends Activity implements OnClickListener {private static final String TAG = "MessengerActivity";private TextView tv_process;private Messenger mSendMsg;private Messenger mReplyMsg;Handler mClientHandler = new Handler(){public void handleMessage(Message msg) {Bundle bundle = msg.getData();tv_process.setText(bundle.getString("msg"));}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_messenger);tv_process = (TextView) findViewById(R.id.tv_process);Button btn_messenger_start = (Button) findViewById(R.id.btn_messenger_start);btn_messenger_start.setOnClickListener(this);mReplyMsg = new Messenger(mClientHandler);Intent intent = new Intent(this, MessageService.class);bindService(intent, mMessageConn, Context.BIND_AUTO_CREATE);}private ServiceConnection mMessageConn = new ServiceConnection() {public void onServiceConnected(ComponentName name, IBinder binder) {mSendMsg = new Messenger(binder);}public void onServiceDisconnected(ComponentName name) {}};@Overridepublic void onClick(View v) {if (v.getId() == R.id.btn_messenger_start) {Bundle bundle = new Bundle();bundle.putString("msg", "aaa");Message msg = Message.obtain();msg.setData(bundle);msg.replyTo = mReplyMsg;try {mSendMsg.send(msg);} catch (RemoteException e) {e.printStackTrace();}}}}

点此查看Android开发笔记的完整目录

Android开发笔记(五十一)通过Messenger实现进程间通信相关推荐

  1. Android开发笔记(十一)自定义视图的构造方法

    自定义视图的用法 Android自带的视图常常不能满足实际开发的需求,这种情况下我们就得自定义视图(View). 首先在res\values目录下找到attrs.xml(如没有则创建之),在该属性定义 ...

  2. Android开发笔记(序)写在前面的目录

    知识点分类 一方面写写自己走过的弯路掉进去的坑,避免以后再犯:另一方面希望通过分享自己的经验教训,与网友互相切磋,从而去芜存菁进一步提升自己的水平.因此博主就想,入门的东西咱就不写了,人不能老停留在入 ...

  3. Android开发笔记(序)

    本开发笔记,借鉴与其他开发者整理的文章范例与心得体会.在这里作为开发过程中的一个总结与笔记式记录. 如有侵犯作者权益,请及时联系告知删除.俗话说:集百家成一言,去粕成金. ************** ...

  4. Android开发笔记(序)写在前面的目录大全

    转自  湖前琴亭 的博客https://blog.csdn.net/aqi00/article/details/50012511 知识点分类 一方面写写自己走过的弯路掉进去的坑,避免以后再犯:另一方面 ...

  5. Andriod开发之二十:Android开发笔记(序)写在前面的目录

    https://blog.csdn.net/aqi00/article/details/50038385 知识点分类 一方面写写自己走过的弯路掉进去的坑,避免以后再犯:另一方面希望通过分享自己的经验教 ...

  6. Android开发笔记(一百五十一)WebView与JavaScript交互的四种形式

    WebView如果作为简单的网页浏览器,对于一般的浏览行为来说,已经足够了.可做为企业开发者,你的App通常要嵌入自家公司的网页,如此一来,还得考虑App与Web之间的消息传递,这就涉及到App的原生 ...

  7. Android开发笔记(五十四)数据共享接口ContentProvider

    ContentProvider 前面几节介绍了进程间通信的几种方式,包括消息包级别的Messenger.接口调用级别的AIDL.启动页面/服务级别的Notification,还有就是本节这个数据库级别 ...

  8. Android开发笔记(五十三)远程接口调用AIDL

    AIDL概述 AIDL全称是"Android Interface Definition Language",即Android的接口定义语言.AIDL用来协助开发者来处理进程间通信, ...

  9. Android开发笔记(六十一)文件下载管理DownloadManager

    下载管理DownloadManager 文件下载其实是网络数据访问的一种特殊形式,使用普通的http请求也能完成,就是实现起来会繁琐一些.因为下载功能比较常用,而且业务功能相对统一,所以从Androi ...

最新文章

  1. Apache+PHP配置过程详解
  2. Qt添加对Android的OpenSSL支持
  3. Jfinal 显示欢迎页 index.jsp
  4. 体验VS2017的Live Unit Testing
  5. php静态数组变量初始化,为什么数组初始化时,赋值不能是常量?
  6. 济南计算机学校排名2015,济南高中排名前十名有哪些
  7. Android Studio 教程(1)----配置
  8. MySQL系列(三)
  9. Atitit 歌词成语提取项目 nlp 人工智能项目 目录 1.1. 流程 首先搜集3w成语词库 1 1.2. 歌词常用成语400个 按照拼音排序 1 1.1.流程 首先搜集3w成语词库 放入m
  10. 模拟tcp_TCP 半连接队列和全连接队列满了会发生什么?又该如何应对?
  11. 《linux c编程指南》学习手记2
  12. 博弈论基础知识--非合作博弈,零和博弈,负和博弈,主从博弈,Nash均衡
  13. 2021 OpenCV人工智能竞赛优秀项目团队介绍集锦(五)
  14. IE(11)浏览器清理缓存方法
  15. 惊艳爆了,这是我见过的最美Redis客户端
  16. 高匿代理,混淆代理,匿名代理,透明代理略解
  17. VRP系统——路由器配置之信息中心基础
  18. RGB与HSV颜色空间转换
  19. 测试普通话水平的软件,求测试普通话标准的软件?6款普通话软件推荐
  20. 【知识点】UDS刷写的一般流程介绍

热门文章

  1. 吴恩达机器学习作业 1线性回归
  2. P-GCN:Graph Convolutional Networks for Temporal Action Localization 2019 ICCV
  3. 西瓜书+实战+吴恩达机器学习(三)机器学习基础(多分类、类别不平衡)
  4. 分布式服务框架原来与实践 读书笔记一
  5. Kubernetes Resource QoS Classes介绍
  6. php本地文件包含漏洞,php文件包含漏洞利用小结
  7. attrib批量显示文件夹_Windows 下彻底隐藏文件和文件夹的方法
  8. 如何给数组用fill函数和memset函数给数组赋初值
  9. 安卓手机安装并使用自动化应用Tasker
  10. 页面背景图尺寸不随浏览器缩放而变化