少年击剑更吹箫,剑气箫心一例消。这篇文章主要讲述Android IPC之Messenger浅谈相关的知识,希望能为你提供帮助。
之前写过一篇有关 IPC之AIDL浅谈的文章,
详情请看Android IPC之AIDL浅谈。今天再来介绍另一种 IPC-Messenger。
一、概述。
首先看Messenger介绍,
Reference to a Handler, which others can use to send messages to it.
This allows for the implementation of message-based communication across
processes, by creating a Messenger pointing to a Handler in one process,
and handing that Messenger to another process.
大概意思是说, 通过Handler来发送消息。允许在进程中基于消息通信实现, 在一个进程中创建一个信使指向一个Handler, 并将该信使传递给另外一个进程。
换句话说, 就是Messenger通过Handler来发送消息, 可以在不同的进程间通信。
下面通过一张图来说明,
![Android IPC之Messenger浅谈](http://img.readke.com/220413/1KUVR4-0.jpg)
文章图片
该图基本说明了Messenger的上层工作流程。小结如下:
1.Messenger的使用需要有服务端和客户端;
2.客户端需要绑定远程连接, 绑定成功后, 就能获取远程的IBinder对象, 通过该IBinder就可以得到远程的Messenger对象;
3.向服务端发送消息时, 需要使用服务端的Messenger对象, 并且可以把客户端的Messenger对象赋值给消息的‘replyTo’属性, 一并传递到服务端;
4.向客户端发送消息, 需要使用客户端的Messenger对象, 该对象是从客户端发送的消息的‘replyTo’属性中获取的。
下面依旧通过一个实例来演示Messenger的整个流程。
二、实例。
需要有两个进程, 才能IPC, 单进程谈IPC是毫无意义的。可以在一个应用中使用Messenger实现IPC, 该应用中需要有多个进程; 当然也可以创建两个应用, 一个是服务端, 一个客户端。这两种方式在本质上都是一样的。本篇文章我们使用后者来实现IPC。
1.服务端应用实现。
创建一个Service,
/**
* 服务端
*/
public class MessengerService extends Service {
private static final String TAG =
"
MessengerService"
;
private static final int WHAT =
0x101;
Handler mHandler =
new Handler() {
@
Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case WHAT:
//接受从客户端传来的消息
Bundle bundle=
(Bundle) msg.obj;
String str =
(String) bundle.get("
str"
);
Log.e(TAG, "
服务端已接受到客户端的消息"
);
Log.e(TAG, "
消息是---->
"
+
str);
//发送数据给客户端
Message message =
Message.obtain();
Bundle bundle1=
new Bundle();
bundle1.putString("
str"
,"
我已经知道了,
加油,
看好你!
"
);
message.obj =
bundle1;
message.what=
0x102;
try {
//向客户端发送消息
msg.replyTo.send(message);
//首先获取从客户端传递的Messenger对象,
然后调用该Messenger对象的send()方法
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
};
private Messenger messenger =
new Messenger(mHandler);
@
Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "
onCreate: "
);
}@
Nullable
@
Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}}
实现该Service的onBind()方法, 调用‘messenger.getBinder()’返回一个IBinder, 返回的IBinder我们将会在客户端接收并使用。创建一个Handler, 用于接收客户端传递的消息, 当接收到消息后, 然后向客户端发送了一个消息。服务端就是这些内容!
一定要记得, 发送消息传递数据时不能把数据直接扔给消息, 否则会报错, 如下所示:
java.lang.RuntimeException: Can'
t marshal non-Parcelable objects across processes.
因为Binder事务传递的数据被称为包裹(Parcel), 必须实现 Parcelable接口, 否则无法在两个应用之间进行通信。所以, 如果需要在消息中携带数据, 请使用 Bundle, 因为 Bundle类已经实现了 Parcelable接口。
还要记得注册该服务。
...
<
service
android:name=
"
.MessengerService"
android:exported=
"
true"
>
<
intent-filter>
<
action android:name=
"
cn.xinxing.messengerservice"
>
<
/action>
<
category android:name=
"
android.intent.category.DEFAULT"
/>
<
/intent-filter>
<
/service>
...
其中android:exported的作用是“是否支持其它应用调用当前组件”, true允许被启动; false不允许被启动。并且设置了“action”属性, 便于我们隐式调用该服务。
一切Ok的话, 就可以运行该程序了!
PS:
IBinder是一个接口。 IBinder是远程对象的基本接口, 是为高性能而设计的轻量级远程调用机制的核心部分。但它不仅用于远程调用, 也用于进程内调用。这个接口定义了与远程对象交互的协议。不要直接实现这个接口, 而应该从Binder派生。
IBinder的主要API是transact(), 与它对应另一方法是Binder.onTransact()。第一个方法使你可以向远端的IBinder对象发送发出调用, 第二个方法使你自己的远程对象能够响应接收到的调用。IBinder的API都是同步执行的, 比如transact()直到对方的Binder.onTransact()方法调用完成后才返回。调用发生在进程内时无疑是这样的, 而在进程间时, 在IPC的帮助下, 也是同样的效果。
2.客户端应用实现。
public class MainActivity extends AppCompatActivity {private static final String TAG =
"
MainActivity"
;
private static final int WHAT =
0x102;
private boolean isConn;
Handler mHandler =
new Handler() {
@
Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case WHAT:
//接受从服务端发送的消息
Bundle bundle=
(Bundle) msg.obj;
String str =
(String) bundle.get("
str"
);
Log.e(TAG, "
客户端服已接受到务端的消息"
);
Log.e(TAG, "
消息是---->
"
+
str);
break;
}
}
};
private Messenger mClientMessenger =
new Messenger(mHandler);
private Messenger mServiceMessenger;
ServiceConnection serviceConnection =
new ServiceConnection() {
@
Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(TAG, "
连接Service成功---->
"
);
mServiceMessenger =
new Messenger(service);
isConn =
true;
Message message =
Message.obtain();
Bundle bundle=
new Bundle();
bundle.putString("
str"
,"
你好!
我是程序猿!
"
);
message.obj=
bundle;
message.what =
0x101;
message.replyTo =
mClientMessenger;
try {
mServiceMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}@
Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "
连接Service---->
disconnected"
);
mServiceMessenger =
null;
isConn =
false;
}
};
@
Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn_start_service).setOnClickListener(new View.OnClickListener() {
@
Override
public void onClick(View v) {
bindService();
}
});
}/**
* 绑定远程连接服务
*/
private void bindService() {
Intent intent =
new Intent();
intent.setAction("
cn.xinxing.messengerservice"
);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}@
Override
protected void onDestroy() {
super.onDestroy();
if (isConn) {
unbindService(serviceConnection);
}
}
}
屏幕中有一个按钮, 点击按钮时, 绑定远程连接服务; 绑定成功后, 发送消息; Handler用于接收消息; 当Activity销毁时, 解除绑定。
整个实现还是比较简单的。
运行客户端后, 点击按钮, 看Log输出,
![Android IPC之Messenger浅谈](http://img.readke.com/220413/1KURW3-1.jpg)
文章图片
远程连接服务绑定成功, 接着服务端输出Log,
![Android IPC之Messenger浅谈](http://img.readke.com/220413/1KUTX0-2.jpg)
文章图片
最后客户端Log输出,
![Android IPC之Messenger浅谈](http://img.readke.com/220413/1KUR9B-3.jpg)
文章图片
小结: 整个流程是, 点击按钮时, 绑定远程连接服务, 绑定成功后, 并且会拿到远程服务的Messenger对象, 然后使用Messenger对象发送消息, 并且将客户端的Messenger对象也发送到了服务端, 当服务端收到消息后, 会输出客户端发送的消息内容, 并且拿到客户端的Messenger对象, 然后使用该Messenger对象向客户端发送消息, 客户端收到消息后, 会输出Log。
整体上, 使用Messenger实现IPC, 相比AIDL来说, 还是比较简单的。下面我们通过Messenger的源码来分析, 它的内部实现。
三、源码解析。
先看Messenger的构造方法,
private final IMessenger mTarget;
public Messenger(Handler target) {
mTarget =
target.getIMessenger();
}
构造方法传递一个Handler, 然后将Handler的IMessenger对象传递给mTarget对象( MessengerImpl) 。下面是Handler的getIMessenger()具体实现,
final IMessenger getIMessenger() {
synchronized (mQueue) {
if (mMessenger !=
null) {
return mMessenger;
}
mMessenger =
new MessengerImpl();
return mMessenger;
}
}
实例化一个MessengerImpl对象, 然后返回了一个IMessenger对象。其中MessengerImpl的源码如下,
private final class MessengerImpl extends IMessenger.Stub {
public void send(Message msg) {
msg.sendingUid =
Binder.getCallingUid();
Handler.this.sendMessage(msg);
}
}
看到这里 IMessenger.Stub, 貌似很熟悉啊! 这不就是AIDL的实现吗! bingo! 是的! Messenger的底层其实就AIDL! 只不过, Messenger已经为我们封装了好了, 不需要我们自己关心AIDL的具体细节。 IMessenger的源码位于‘ \\base\\core\\java\\android\\os\\IMessenger.aidl’, 可以看到它是一个aidl文件, 下面是它的源码,
package android.os;
import android.os.Message;
/** @
hide */
oneway interface IMessenger {
void send(in Message msg);
}
内部只有一个方法, 用于发送消息。这里更加证实了, Messenger的底层其实就AIDL! 有关如何实现一个AIDL, 可以参考 Android IPC之AIDL浅谈。
当发送消息时, 调用Messenger的send()方法,
public void send(Message message) throws RemoteException {
mTarget.send(message);
}
调用了mTarget的 send()方法, 而mTarget是 MessengerImpl。通过上文可以得知 MessengerImpl类实现了send()方法, 该方法是发送一个消息, 可以看到它其实还是通过 Handler发送消息。有关Handler发送消息, 可以参考 Android 源码解析Handler处理机制( 二) 。
服务端有一个onBind()方法, 会返回一个IBinder对象, 该对象来自于Messenger,
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
继续跟下去,
public IBinder getBinder() {
return mTarget.asBinder();
}
返回mTarget对象的asBinder(), 而“mTarget.asBinder()”到底是什么呢?
我们先看看使用AIDL时, AAPT自动生成的AIDL类中的asBinder()方法,
@
Override public android.os.IBinder asBinder()
{
return this;
}
该方法返回的是自己, 即xxxx.Stub类。那么我们此时再看看mTarget对象, mTarget是MessengerImpl, 而MessengerImpl是继承自IMessenger.Stub, 那么mTarget.asBinder()返回的就是Messenger.Stub.asBinder(), 而Messenger.Stub.asBinder()返回的又是Messenger.Stub, 而MessengerImpl是继承自IMessenger.Stub, 所以绕了一圈, “mTarget.asBinder()”返回的就是MessengerImpl对象自己。也就是说 “mTarget.asBinder()”返回的是当前Messenger中的MessengerImpl对象。 当在服务端时, “mTarget.asBinder()”返回的是服务端的MessengerImpl对象, 使用它来向服务端发送消息; 当在客户端时, “mTarget.asBinder()”返回的是客户端的MessengerImpl对象, 使用它来向客户端发送消息。
客户端绑定远程连接成功后, 会获取到一个远程IBinder对象, 通过该IBinder对象, 就可以获取到服务端的Messenger对象, 即调用下面的方法,
public Messenger(IBinder target) {
mTarget =
IMessenger.Stub.asInterface(target);
}
这不是和AIDL的写法一模一样吗! 下面是AIDL的代码,
//远程连接
private ServiceConnection conn =
new ServiceConnection() {
@
Override
public void onServiceConnected(ComponentName name, IBinder service) {
calculateAidl =
CalculateAidl.Stub.asInterface(service);
isBindSuccess=
true;
}@
Override
public void onServiceDisconnected(ComponentName name) {
calculateAidl =
null;
isBindSuccess=
false;
}
};
没啥好说的了! 有关AIDL, 详情请看Android IPC之AIDL浅谈。
好了, 有关Messenger的源码分析就这么多! 四、小结。
1.Messenger的底层还是使用的AIDL;
2.服务端和客户端通信, 向服务端发现消息, 使用的是服务端的Messenger( 该对象来自于远程连接成功通过IBinder获取) , 向客户端发送消息, 使用的是客户端的Messenger( 该对象是向服务端发送的消息中携带的-replyTo属性携带的参数) ;
3.在IPC中, 要传递参数, 需要实现Parcelable接口, 建议使用Bundle封装参数。
4.Messenger会将所有Service请求入队列, 所以它不支持多线程通信。如果你要支持多线程, 那么请使用AIDL。
【Android IPC之Messenger浅谈】Demo下载链接
推荐阅读
- android开发开源宝贝——持续更新。。。
- Android 传感器开发 完全解析
- Android?Studio?NDK编程-环境搭建及Hello!
- Android?线程池(转)
- Android Studio22-NDK-LLDB调试
- Eclipse如何替换android应用图标
- 安卓的开端的相关配置
- Android 闹钟最终版
- 第一天安卓笔记