Android Messenger实现进程间通信及其原理

前言

之前分析Android消息机制的源码时,曾遇到过replyTo、IMessenger等属性字段,当时只是说这些字段用于进程间通信,并未作深入分析。今天这篇文字就来演示一下使用Messenger如何进行进程间通信并分析其源码实现。

Messenger进程间通信的流程

Messenger顾名思义,即信使,那么它的作用就是满足不同进程两边的通信需要了。通常我们会写AIDL来实现进程间通信,其实简单的IPC可以用Messenger来实现,需要知道的是Messenger也是基于AIDL的,只不过Messenger帮我们做了封装而已,其进程间通信框架是这样的:

如上图,假设两个进程分别为Client Process和Server Process,首先Server端需要将自己这边的Messenger引用传给Client,然后Client使用Server端传过来的Messenger来发消息给Server端,这样就实现了一个单向通信。同理,如果想要实现双向通信,则需要Client端也发送一个自己的Messenger到Server端,那么Server端也就可以利用该Messenger向Client发消息了。虽然Messenger是基于AIDL的,但它们最底层都是基于Binder的。

Messenger进程间双向通信示例

创建一个Service模拟Server进程

一般的进程间通信多是在两个App之间,但一个App中也可以有多进程,这个很常见,如应用中的推送服务一般位于单独的进程。当然我们可以把这个Service创建到另一个App中,但为了方便测试,这里只是将该Service注册为另一个进程,但还是在同一个应用中。

该Service的实现很简单,如下:

public class RemoteService extends Service {

private WorkThread mWorkThread = new WorkThread();

private Messenger mMessenger;

@Override

public void onCreate() {

super.onCreate();

mWorkThread.start();

}

@Override

public void onDestroy() {

super.onDestroy();

mWorkThread.quit();

}

@Nullable

@Override

public IBinder onBind(Intent intent) {

return mMessenger.getBinder();

}

private void prepareMessenger() {

mMessenger = new Messenger(mWorkThread.mHandler);

}

private class WorkThread extends Thread {

Handler mHandler;

@Override

public void run() {

Looper.prepare();

mHandler = new Handler() {

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

switch (msg.what) {

case MessageConstant.CLIENT_TO_SERVER:

Toast.makeText(RemoteService.this, "Hello Server:" + msg.arg1 + "," + msg.arg2, Toast.LENGTH_SHORT).show();

if (msg.replyTo != null) {

try {

msg.replyTo.send(Message.obtain(null, MessageConstant.SERVER_TO_CLIENT, 0, msg.arg1 + msg.arg2));

} catch (RemoteException e) {

e.printStackTrace();

}

}

break;

default:

break;

}

}

};

prepareMessenger();

Looper.loop();

}

public void quit() {

mHandler.getLooper().quit();

}

}

上述代码虽然简单,但有几点需要注意:

1、为什么Service中要开一个工作线程?因为Service作为四大组件之一,它是运行在主线程的,所以不能执行耗时操作,一旦进程间交互是耗时操作,那么Service所在进程就会阻塞,而Client端进程则不会阻塞。

2、该Service中创建了一个Messenger对象,并在onBind中返回了IBinder对象,这里是进程间通信的关键,在后面会详细分析。

3、该Service的子线程中创建了一个Handler,并关联给Messenger,用于进程间通信的消息处理。Handler消息处理跟我们平时用的一样,但有一点提一下,子线程是没有默认Looper的,因此需要自己创建并启动,否则子线程的Handler无法收到Message。

4、Server端收到消息后,Toast一下“hello server”并显示Cient传过来的两个整数值。如果Client端也将自己的Messenger传过来了,则向Client端回复消息,将两个整数之和返回。

另外该Service在AndroidManifest.xml中的注册如下:

<service

android:name=".messenger.RemoteService"

android:enabled="true"

android:exported="true"

android:process=":remote">

<intent-filter>

<action android:name="com.aspook.remote.ACTION_BIND" />

<category android:name="android.intent.category.DEFAULT" />

</intent-filter>

</service>

核心一句为android:process=":remote",将该Service置于另一个进程之中,从而可以在同一个App中模拟进程间通信。

创建一个Activity模拟Client进程

该Activity默认就是该App所在进程了,具体实现如下:

/**

* demo for IPC by Messenger

*/

public class MessengerActivity extends AppCompatActivity {

private Button btn_start;

private Button btn_bind;

private Button btn_send;

private boolean mBound = false;

private Messenger mRemoteMessenger = null;

private ServiceConnection mRemoteConnection = new ServiceConnection() {

@Override

public void onServiceConnected(ComponentName name, IBinder service) {

mRemoteMessenger = new Messenger(service);

mBound = true;

}

@Override

public void onServiceDisconnected(ComponentName name) {

mRemoteMessenger = null;

mBound = false;

}

};

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_messenger);

findViews();

setListeners();

}

@Override

protected void onDestroy() {

super.onDestroy();

unbindService(mRemoteConnection);

}

public void findViews() {

btn_start = (Button) findViewById(R.id.btn_start);

btn_bind = (Button) findViewById(R.id.btn_bind);

btn_send = (Button) findViewById(R.id.btn_send);

}

public void setListeners() {

btn_start.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

// start Remote Service first

Intent intent = new Intent(MessengerActivity.this, RemoteService.class);

startService(intent);

btn_start.setEnabled(false);

}

});

btn_bind.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

// bind the Remote Service, if the Remote service run in another App, you should run the App and start the service first

try {

bindRemoteService();

btn_bind.setEnabled(false);

} catch (Exception e) {

e.printStackTrace();

}

}

});

btn_send.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

if (mBound) {

Handler mClientHandler = new Handler() {

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

switch (msg.what) {

case MessageConstant.SERVER_TO_CLIENT:

Toast.makeText(MessengerActivity.this, "Hello Client:" + msg.arg2, Toast.LENGTH_SHORT).show();

break;

default:

break;

}

}

};

try {

Message msg = Message.obtain(null, MessageConstant.CLIENT_TO_SERVER, 66, 88);

// Messenger of client sended to server is used for sending message to client

msg.replyTo = new Messenger(mClientHandler);

mRemoteMessenger.send(msg);

} catch (RemoteException e) {

e.printStackTrace();

}

} else {

Toast.makeText(MessengerActivity.this, "Service not bind", Toast.LENGTH_SHORT).show();

}

}

});

}

/**

* bind service

*/

public void bindRemoteService() {

// Method one

Intent intent = new Intent("com.aspook.remote.ACTION_BIND");// 5.0+ need explicit intent

intent.setPackage("com.aspook.androidnotes"); // the package name of Remote Service

bindService(intent, mRemoteConnection, BIND_AUTO_CREATE);

}

}

代码逻辑也很简单,界面有3个按钮,操作如下:

1、先启动Server端的Service,暂且叫做启动远程Service

2、绑定远程Service

3、Client向Servcie端发送消息,并接收返回的消息

需要注意的有如下几点:

1、绑定远程Service后,Client端才拿到了Server端的Messenger引用。

2、Client端的Messenger需要关联自己的Handler,用来处理从Server端收到的消息。这里也需要注意,理论上如果Server端与Client端交互也是耗时的话,也需要开子线程,这个例子中由于只是显示下消息,直接放在UI线程了。

3、如果需要双向通信,Client端需要通过Message的replyTo参数将自己的Messenger发到Server端。

4、Android 5.0+要求绑定Service时必须使用显式Intent,可以通过设置包名的方式来解决,注意我是在同一个App中开的两个进程,因此包名相同,但如果远程Service位于另一个App,则应该填写其所在App的包名。

5、Client端收到回复消息后,Toast“Hello client”及两个整数之和。

示例效果演示

以上示例的进程间通信效果演示如下:

Messenger进程间通信原理分析

关于Service的启动、绑定不必多说,先从Client端通过绑定远程Service获取Server端的Messenger入手,代码如下:

private ServiceConnection mRemoteConnection = new ServiceConnection() {

@Override

public void onServiceConnected(ComponentName name, IBinder service) {

mRemoteMessenger = new Messenger(service);

mBound = true;

}

@Override

public void onServiceDisconnected(ComponentName name) {

mRemoteMessenger = null;

mBound = false;

}

};

接着来看mRemoteMessenger = new Messenger(service);的源码实现:

/**

* Create a Messenger from a raw IBinder, which had previously been

* retrieved with {@link #getBinder}.

*

* @param target The IBinder this Messenger should communicate with.

*/

public Messenger(IBinder target) {

mTarget = IMessenger.Stub.asInterface(target);

}

注意到该构造方法的参数IBinder,就是远程Service中onBind返回的,具体代码如下:

@Nullable

@Override

public IBinder onBind(Intent intent) {

return mMessenger.getBinder();

}

再来看这一句代码:

mTarget = IMessenger.Stub.asInterface(target);

mTarget是IMessenger对象,看起来越来越像AIDL的写法了,其实不能说像,本来就是AIDL。于是猜想源码必定中有一个名为IMessenger.aidl的文件,它应该定义了发送消息的相关接口。果然在源码目录 “/frameworks/base/core/java/android/os/”下找到了IMessenger.aidl文件,其内容如下:

package android.os;

import android.os.Message;

/** @hide */

oneway interface IMessenger {

void send(in Message msg);

}

因此可知Messenger只是帮我们省去了写AIDL的工作而已,底层还是AIDL。

再来看Messenger是如何发送消息的,即Messenger的send方法:

/**

* Send a Message to this Messenger's Handler.

*

* @param message The Message to send. Usually retrieved through

* {@link Message#obtain() Message.obtain()}.

*

* @throws RemoteException Throws DeadObjectException if the target

* Handler no longer exists.

*/

public void send(Message message) throws RemoteException {

mTarget.send(message);

}

通过注释可知,Messenger会将消息发送到其关联的Handler,且Handler不存在时会报异常,这就是我们无论是创建客户端还是服务端Messenger时都为其创建了一个Handler的原因。

另外上述示例中为了简便,只是在进程间传递了基本类型的值,其实类似单进程的消息机制,也可以传递Bundle数据,但注意需要序列化,具体说明可参考Message源码的基本字段支撑。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

以上是 Android Messenger实现进程间通信及其原理 的全部内容, 来源链接: utcz.com/p/243567.html

回到顶部