前面我们介绍了 进程通信 AIDL 的学习和使用,这篇文章来介绍下 Android 中另一种 IPC 方式:Messenger。
Messenger
“信使”,顾名思义,通过它可以在不同的进程中传递Message
对象,在Message
中放入我们要传递的内容,就可以实现进程间通信了。Messenger
是一种轻量级的IPC 方案。
Messenger 有两个构造函数:
private final IMessenger mTarget;
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target); //和前面的 AIDL 很相似吧
}
不管是 IMessenger
还是 Stub.asInterface
,这种使用方法都表明底层就是 AIDL
实现 Messenger
有如下步骤:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private TextView receiveText;
private EditText input;
/**
* 客户端的 Messenger
*/
private Messenger mClientMessenger = new Messenger(new Handler(){
@Override
public void handleMessage(Message msg) {
if (msg!=null && msg.arg1 == Constants.MSG_FROM_SERVER){
if (msg.getData() == null){
return;
}
String content = (String) msg.getData().get(Constants.MSG_CONTENT);
receiveText.setText(content);
}
}
});
/** 服务端的 Messenger */
private Messenger mServerClient;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mServerClient = new Messenger(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mServerClient = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
bindMessengerService();
}
private void bindMessengerService() {
/** 如下方式调用另外一个进程的 service ,但是这个service必须在当前工程清单文件中声明 */
Intent intent = new Intent(this,MessengerService.class);
bindService(intent,conn, Context.BIND_AUTO_CREATE);
}
private void initView(){
input = (EditText) findViewById(R.id.inputText);
Button sendBtn = (Button) findViewById(R.id.sendBtn);
receiveText = (TextView) findViewById(R.id.receiveText);
sendBtn.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.sendBtn){
String msgContent = input.getText().toString();
msgContent = TextUtils.isEmpty(msgContent) ? "默认消息" : msgContent;
Message message = Message.obtain();
message.arg1 = Constants.MSG_FROM_CLIENT;
Bundle bundle = new Bundle();
bundle.putString(Constants.MSG_CONTENT, msgContent);
message.setData(bundle);
message.replyTo = mClientMessenger; //指定回信人是客户端定义的
try {
mServerClient.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
服务端的 Client
,通过客户端的 ServiceConnection
绑定成功之后,通过new Messenger(service);
传入service
,拿到客户端服务的对象 mServerClient
在initView
中初始化界面,重要的就算点击按钮之后,通过服务端的 Messenger
发送消息这块了。
Message message = Message.obtain();
message.arg1 = Constants.MSG_FROM_CLIENT; // 设置标记
Bundle bundle = new Bundle();
bundle.putString(Constants.MSG_CONTENT, msgContent); // 传递的消息内容
message.setData(bundle);
message.replyTo = mClientMessenger; //指定回信人是客户端定义的
最后通过mServerClient.send(message);
将消息发送到服务端。
bindMessengerService()
中请在工程中配置一下对应的 service
让这个 service
运行在单独的进程中 。
public class MessengerService extends Service {
private Messenger mMessenger = new Messenger(new Handler() {
@Override
public void handleMessage(final Message msg) {
if (msg != null && msg.arg1 == Constants.MSG_FROM_CLIENT) {
if (msg.getData() == null) {
return;
}
String content = (String) msg.getData().get(Constants.MSG_CONTENT); //接收客户端的消息
//回复消息给客户端
Message replyMsg = Message.obtain();
replyMsg.arg1 = Constants.MSG_FROM_SERVER;
Bundle bundle = new Bundle();
bundle.putString(Constants.MSG_CONTENT, "收到啦,你不就是说:"+content);
replyMsg.setData(bundle);
Messenger serverClient = msg.replyTo;
try {
serverClient.send(replyMsg); //回信
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
可以看到Handler
来处理客户端发送的消息,从消息中取出客户端的文本信息,而mMessenger
是一个 Messenger
的对象,它和handler关联起来,在onBind
方法中返回IBinder
对象,可以看到这里 是服务端来处理客户端的消息。代码比较简单易懂。
最后实现效果,app启动后 有两个进程,一个是 主工程4631 ,一个是 服务端4669
最后实现效果
当然我们可以把客户端和服务端和上次讲的 进程通信 AIDL 的学习和使用 一样,搞成两个app,这样的话看起来更加真实。
在客户端这里绑定的时候要使用
private void bindMessengerService() {
Intent intent = new Intent();
//5.0 以后要求显式调用 Service,所以我们无法通过 action 或者 filter 的形式调用 Service
intent.setComponent(new ComponentName("com.zx.serviceclient","com.zx.serviceclient.MessengerService"));
bindService(intent,conn, Context.BIND_AUTO_CREATE);
}
把对应 com.zx.serviceclient
下的服务进行绑定,指明绑定的具体服务所在包名和服务类名,同时需要在服务端的Module
中启动对应的service
,并在清单文件中声明。这样就和上一篇文章中讲到的效果一样了,有两个app
,客户端 和 服务端。
文章参考 :https://blog.csdn.net/u011240877/article/details/72836178