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

进程间通信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();
	        }
	    }
	}

	@Override
	public void onCreate() {
		mRecvMsg = new Messenger(mServerHandler);
		super.onCreate();
	}

	@Override
	public 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"));
	    }
	};

	@Override
	protected 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) {
        }

    };

	@Override
	public 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,ipc,多进程,messenger,IBinder)