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

来源:互联网 发布:sql数据库删除后恢复 编辑:程序博客网 时间:2024/05/21 16:55

进程间通信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开发笔记的完整目录
0 0