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中使用socket使底层和framework通信的实现方法
native和framework的通信是通过jni,但是这一般只是framework调用native,native如果有消息要怎样通知上层 呢?android中GSP模块提供一种解决思路,但是实现有些复杂,这里介绍一种使用socket通信的方法可以使native和framework自由通信,感兴趣的朋友一起看看吧2016-11-11保持Android Service在手机休眠后继续运行的方法
下面小编就为大家分享一篇保持Android Service在手机休眠后继续运行的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧2018-03-03Android ListView中headerview的动态显示和隐藏的实现方法
这篇文章主要介绍了Android ListView中headerview的动态显示和隐藏的实现方法的相关资料,这里提供两种方法帮助实现这样的功能,需要的朋友可以参考下2017-08-08Flutter使用AnimatedSwitcher实现场景切换动画
在应用中,我们经常会遇到切换组件的场景。本文将利用Flutter中提供的AnimatedSwitcher这一动画组件来实现页面内的场景切换,需要的可参考一下2022-03-03
最新评论