前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android开发笔记(五十一)通过Messenger实现进程间通信

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

作者头像
aqi00
发布2019-01-18 11:12:10
8110
发布2019-01-18 11:12:10
举报
文章被收录于专栏:老欧说安卓

进程间通信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"”,则下面这个代码在类型转换时就会抛出异常:

代码语言:javascript
复制
    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的示例代码:

代码语言:javascript
复制
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();
	}

}

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

代码语言:javascript
复制
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开发笔记的完整目录

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016年01月18日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 进程间通信IPC
  • 开启多进程
  • 信使Messenger
  • IBinder
  • 使用示例
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档