这边博客主要记录一下Android中Messenger的基本原理和用法。
简单来讲,Messenger其实就是Binder通信的包装器,是一种基于消息传递的进程间通信工具。
//Messenger实现了Parcelable接口,因此可以跨进程传输 public final class Messenger implements Parcelable { ............... }
通常情况下,我们可以在A进程中创建一个Messenger,然后将该Messenger传递给B进程。
于是,B进程就可以通过Messenger与A进程通信了。
Messenger通常与Handler一起使用,我们看看对应的源码:
public final class Messenger implements Parcelable { private final IMessenger mTarget; public Messenger(Handler target) { mTarget = target.getIMessenger(); } ........... }
跟进一下Handler的getIMessenger函数:
............. final IMessenger getIMessenger() { synchronized (mQueue) { if (mMessenger != null) { return mMessenger; } //返回的是Handler中定义的MessengerImpl mMessenger = new MessengerImpl(); return mMessenger; } } //此处MessengerImpl继承自IMessenger.Stub //容易看出MessengerImpl将作为Binder通信的接收端 private final class MessengerImpl extends IMessenger.Stub { public void send(Message msg) { msg.sendingUid = Binder.getCallingUid(); Handler.this.sendMessage(msg); } }
从上述代码可以看出,Messenger实际上作为了一个Binder服务端的wrapper。
当我们在A进程中创建Messenger,然后传递给B进程时,Messenger需要执行Parcelable接口定义的操作,于是:
//在A进程中将Binder信息写入到Parcel中 public void writeToParcel(Parcel out, int flags) { out.writeStrongBinder(mTarget.asBinder()); } public static final Parcelable.Creator<Messenger> CREATOR = new Parcelable.Creator<Messenger>() { //在B进程中,重新创建Binder public Messenger createFromParcel(Parcel in) { IBinder target = in.readStrongBinder(); //调用Messenger的另一个构造函数 return target != null ? new Messenger(target) : null; } public Messenger[] newArray(int size) { return new Messenger[size]; } };
跟进一下Messenger的另一个构造函数:
public Messenger(IBinder target) { //得到的是Binder通信的客户端 mTarget = IMessenger.Stub.asInterface(target); }
因此,当Messenger从进程A传递到进程B时,它就变为了Binder通信客户端的wrapper。
当在进程B中使用Messenger的接口时:
public void send(Message message) throws RemoteException { //mTarget为Binder通信的客户端,将消息发送给服务端的send函数 //即服务端Handler的MessengerImpl的send函数 //上文已经附上了对应代码,可以看到对应的消息将递交给Handler处理 mTarget.send(message); }
以上就是Messenger通信的原理,现在实际测试一下。
我们定义一个简单的demo,包含一个Activity和一个Service,其中Service与Activity处在不同的进程中。
..................... <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <service android:name=".RemoteService" android:enabled="true" android:exported="true" <!--指定服务运行在其它进程--> android:process=".remote"> </service> ..............
Activity的界面很简单,当点击时就会像Service发送消息,Activity代码如下:
public class MainActivity extends AppCompatActivity { private Button mButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //打印Activity的pid及所在进程名称 Log.d("ZJTest", "Activity, pid: " + Process.myPid() + ", name: " + Util.getProcessName(this)); //启动服务 startService(); //绑定服务 bindService(); mButton = (Button) findViewById(R.id.test_button); mButton.setEnabled(false); //点击按键后,利用Messenger向Service发送消息 mButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mMessenger != null) { try { Message msg = Message.obtain(); msg.what = 1; mMessenger.send(msg); } catch (RemoteException e) { Log.d("ZJTest", e.toString()); } } } }); } @Override public void onDestroy() { super.onDestroy(); unBindService(); stopService(); } private Intent mIntent; private void startService() { mIntent = new Intent(this, RemoteService.class); this.startService(mIntent); } private ServiceConnection mServiceConnection; private void bindService() { mServiceConnection = new LocalServiceConnection(); this.bindService(mIntent, mServiceConnection, BIND_AUTO_CREATE); } Messenger mMessenger; private class LocalServiceConnection implements android.content.ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { //绑定服务后,获得Messenger并激活Button mMessenger = new Messenger(service); mButton.setEnabled(true); } @Override public void onServiceDisconnected(ComponentName name) { mButton.setEnabled(false); } } private void stopService() { stopService(mIntent); } private void unBindService() { unbindService(mServiceConnection); } }
Service对应的代码如下:
public class RemoteService extends Service { private Messenger mMessenger; @Override public void onCreate() { super.onCreate(); //同样打印进程号及名称 Log.d("ZJTest", "Service, pid: " + Process.myPid() + ", name: " + Util.getProcessName(this)); LocalHandler mHandler = new LocalHandler(); mMessenger = new Messenger(mHandler); } private static class LocalHandler extends Handler{ @Override public void handleMessage(Message msg) { Log.d("ZJTest", "receive msg: " + msg.what); } } @Override public IBinder onBind(Intent intent) { //被绑定时,返回Messenger return mMessenger.getBinder(); } }
获取进程名的代码如下:
class Util { static String getProcessName(Context context) { int pid = Process.myPid(); ActivityManager am = (ActivityManager) context .getSystemService(Context.ACTIVITY_SERVICE); for (ActivityManager.RunningAppProcessInfo appProcessInfo: am.getRunningAppProcesses()) { if (appProcessInfo.pid == pid) { return appProcessInfo.processName; } } return null; } }
上述代码都是比较简单的,现在来看看运行结果:
02-20 21:25:15.760 D/ZJTest (30460): Activity, pid: 30460, name: stark.a.is.zhang.messengertest
02-20 21:25:15.769 D/ZJTest (30428): Service, pid: 30428, name: .remote
02-20 21:25:32.111 D/ZJTest (30428): receive msg: 1
从log可以看出,Activity与Servie运行在不同的进程中,Messenger确实可以在不同进程间传递消息。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。
本文向大家介绍Spring jackson原理及基本使用方法详解,包括了Spring jackson原理及基本使用方法详解的使用技巧和注意事项,需要的朋友参考一下 导入maven依赖 1、java对象转json 2、writeValue(参数1,obj)方法介绍 参数1 File:将obj对象转换为json字符串,并保存到指定的文件中 writer:将obj对象转换为json字符串,并将json数
本文向大家介绍Android HandlerThread的使用及原理详解,包括了Android HandlerThread的使用及原理详解的使用技巧和注意事项,需要的朋友参考一下 一、HandlerThread的含义 HandlerThread能够新建拥有Looper的线程。这个Looper能够用来新建其他的Handler。(线程中的Looper)需要注意的是,新建的时候需要被回调。 二、Hand
本文向大家介绍spring jpa ManyToMany原理及用法详解,包括了spring jpa ManyToMany原理及用法详解的使用技巧和注意事项,需要的朋友参考一下 1.java和jpa 中所有的关系都是单向的。这个关系数据库不同,关系数据库,通过外键定义并查询,使得反向查询总是存在的。 2.JPA还定义了一个OneToMany关系,它与ManyToMany关系类似,但反向关系(如果已定
本文向大家介绍Android Button的基本用法详解及简单实例,包括了Android Button的基本用法详解及简单实例的使用技巧和注意事项,需要的朋友参考一下 Android Button的基本用法详解 Button的Click点击事件处理: Button的Press状态的获取及调用: 说明: 图片资源是放在 /Android/assets/ 文件夹下。 没有用R资源引用图片。 感谢阅读,
本文向大家介绍Python函数基本使用原理详解,包括了Python函数基本使用原理详解的使用技巧和注意事项,需要的朋友参考一下 1.什么是函数 函数就相当于具备某一功能的工具 函数的使用必须遵循一个原则: 先定义 后调用 2.为何要用函数 1、组织结构不清晰,可读性差 2、代码冗余 3、可维护性、扩展性差 3、如何用函数 1.函数的定义 定义的语法 ''' def 函数名(参数1,参数2,...)
本文向大家介绍Java switch关键字原理及用法详解,包括了Java switch关键字原理及用法详解的使用技巧和注意事项,需要的朋友参考一下 这篇文章主要介绍了Java中 switch关键原理及用法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Switch语法 switch作为Java内置关键字,却在项目中真正使用的比较少。关于swi