Android中Messenger原理及基本用法详解
作者:ZhangJianIsAStark
这边博客主要记录一下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处在不同的进程中。
AndroidManifest.xml中的定义如下:
..................... <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确实可以在不同进程间传递消息。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。