Android中Messenger原理及基本用法详解

这边博客主要记录一下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确实可以在不同进程间传递消息。

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

时间: 2024-09-14 05:02:34

Android中Messenger原理及基本用法详解的相关文章

Android中使用HTTP服务的用法详解_Android

在Android中,除了使用Java.NET包下的API访问HTTP服务之外,我们还可以换一种途径去完成工作.Android SDK附带了Apache的HttpClient API.Apache HttpClient是一个完善的HTTP客户端,它提供了对HTTP协议的全面支持,可以使用HTTP GET和POST进行访问.下面我们就结合实例,介绍一下HttpClient的使用方法. 我们新建一个http项目,项目结构如图: 在这个项目中,我们不需要任何的Activity,所有的操作都在单元测试类H

Android中使用HTTP服务的用法详解

在Android中,除了使用Java.NET包下的API访问HTTP服务之外,我们还可以换一种途径去完成工作.Android SDK附带了Apache的HttpClient API.Apache HttpClient是一个完善的HTTP客户端,它提供了对HTTP协议的全面支持,可以使用HTTP GET和POST进行访问.下面我们就结合实例,介绍一下HttpClient的使用方法. 我们新建一个http项目,项目结构如图: 在这个项目中,我们不需要任何的Activity,所有的操作都在单元测试类H

IOS开发中NSURL的基本操作及用法详解_IOS

NSURL其实就是我们在浏览器上看到的网站地址,这不就是一个字符串么,为什么还要在写一个NSURL呢,主要是因为网站地址的字符串都比较复杂,包括很多请求参数,这样在请求过程中需要解析出来每个部门,所以封装一个NSURL,操作很方便. 1.URL URL是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址.互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它. URL可能包含远程服务器上的资源的位置,本地磁盘上的文件的路径,甚

JavaScript中SetInterval与setTimeout的用法详解_javascript技巧

setTimeout 描述 setTimeout(code,millisec) setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式. 注:调用过程中,可以使用clearTimeout(id_of_settimeout)终止 参数 描述 code 必需,要调用的函数后要执行的 JavaScript 代码串. millisec 必需,在执行代码前需等待的毫秒数. setTimeinterval setInterval(code,millisec[,"lang"]) 参数

Android中asset和raw的区别详解_Android

*res/raw和assets的相同点: 1.两者目录下的文件在打包后会原封不动的保存在apk包中,不会被编译成二进制. *res/raw和assets的不同点: 1.res/raw中的文件会被映射到R.java文件中,访问的时候直接使用资源ID即R.id.filename:assets文件夹下的文件不会被映射到 R.java中,访问的时候需要AssetManager类. 2.res/raw不可以有目录结构,而assets则可以有目录结构,也就是assets目录下可以再建立文件夹 *读取文件资源

C++中auto_ptr智能指针的用法详解_C 语言

智能指针(auto_ptr) 这个名字听起来很酷是不是?其实auto_ptr 只是C++标准库提供的一个类模板,它与传统的new/delete控制内存相比有一定优势,但也有其局限.本文总结的8个问题足以涵盖auto_ptr的大部分内容. auto_ptr是什么? auto_ptr 是C++标准库提供的类模板,auto_ptr对象通过初始化指向由new创建的动态内存,它是这块内存的拥有者,一块内存不能同时被分给两个拥有者.当auto_ptr对象生命周期结束时,其析构函数会将auto_ptr对象拥有

Android 中Lambda表达式的使用实例详解

Android 中Lambda表达式的使用实例详解 Java8 中着实引入了一些非常有特色的功能,如Lambda表达式.streamAPI.接口默认实现等等.Lambda表达式在 Android 中最低兼容到 Android2.3 系统,兼容性还是不错的,Lambda表达式本质上是一种匿名方法,它既没有方法名,也没有访问修饰符和返回值类型,使用它编写的代码将更加简洁易读. 1.Lambda表达式的基本写法 如果想要在 Android 项目中使用 Lambda表达式 或者 Java8 的其他新特性

Android中asset和raw的区别详解

*res/raw和assets的相同点: 1.两者目录下的文件在打包后会原封不动的保存在apk包中,不会被编译成二进制. *res/raw和assets的不同点: 1.res/raw中的文件会被映射到R.java文件中,访问的时候直接使用资源ID即R.id.filename:assets文件夹下的文件不会被映射到 R.java中,访问的时候需要AssetManager类. 2.res/raw不可以有目录结构,而assets则可以有目录结构,也就是assets目录下可以再建立文件夹 *读取文件资源

Android开发之自定义View(视图)用法详解_Android

本文实例讲述了Android开发之自定义View(视图)用法.分享给大家供大家参考,具体如下: View类是Android的一个超类,这个类几乎包含了所有的屏幕类型.每一个View都有一个用于绘图的画布,这个画布可以进行任意扩展.在游戏开发中往往需要自定义视图(View),这个画布的功能更能满足我们在游戏开发中的需要.在Android中,任何一个View类都只需重写onDraw 方法来实现界面显示,自定义的视图可以是复杂的3D实现,也可以是非常简单的文本形式等. 为了实现自定义View,需要创建