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