Android IPC机制利用Messenger实现跨进程通信
作者:lqh 发布时间:2023-01-02 10:57:49
写作原因:跨进程通信的实现和理解是Android进阶中重要的一环。下面博主分享IPC一些相关知识、操作及自己在学习IPC过程中的一些理解。这一章使用Messenger实现跨进程通信,其中bindService基础部分参见Android IPC机制绑定Service实现本地通信。
跨进程简介
在介绍使用Messenger跨进程通信之前先要了解以下问题:为什么需要跨进程通信?只有有了需求才有学习的价值。我个人将不同进程简单的理解为不同的应用程序(当然也有例外,比如可以在同一个应用程序中开启两个或多个进程)。由于进程之间不能像线程一样共享内存,所以数据通信不能像线程一般进行。在Android中可以使用bundle,广播,Messenger,AIDL和Socket进行跨进程通信。本章利用Messenger分别进行单应用程序多进程单向通信和多应用程序多进程双向通信的实现。
Messenger介绍
Messenger是通过使用Message来实现跨进程通信,一次实现一个请求的方式,这是它的优点也是缺点。其底层实现为AIDL(下章我将阐述)。Messenger的优点是:基于Message,方便使用;支持回调的方式,也就是服务端处理完成长任务可以和客户端交互;不需要编写aidl文件。
Messenger使用流程如下(转载):
单应用程序多进程单向通信
先介绍一下Android中单应用程序开启多进程的方法,实际上只要在mainfests中的你想开启新进程的组件的XML中添加<android:progress = ":remote(可以自定义)">或者<android:progress = "包.remote(可以自定义)">就行。如:
<service android:name=".CustomService" android:process=":remote"/>
这样就把Service放在新的线程中运行了。
Service实现
下面是服务端的代码实现,具体思路是:创建一个Handler对象用来处理客户端发送来的消息,再创建一个Messenger对象将上面的Handler对象作为参数传入,这样我们就获得了一个信使。下面就是通过getBinder()把这个信使创建的Binder对象返回给客户端(一旦客户端拿到这个Binder,又可以将它还原为Messenger)。Handler中处理信息为:当得到的Message的what值为MSG_SAY_HELLO时输出Toast。
public class CustomService extends Service{
static final int MSG_SAY_HELLO = 1;
//实现一个能够处理接收信息的Handler
class IncomingHandler extends Handler{
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch(msg.what){
case MSG_SAY_HELLO:
Toast.makeText(getApplicationContext(),"hello!",Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}
//被客户端接收的Messenger对象
final Messenger messenger = new Messenger(new IncomingHandler());
@Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(),"binding!",Toast.LENGTH_SHORT).show();
return messenger.getBinder();
}
}
Activity实现
在客户端中我们应该做的是:拿到服务端传来的Messenger对象(在ServiceConnection中取得,具体参见上一篇文章),然后创建一个Message对象,为Message写入数据,注意Message中的what要与服务端中Handler对象中的what一致。使用该Messenger通过send()将Message发送给服务端,这样就可以实现客户端与服务端的单向通信了。
具体代码如下:
public class MainActivity extends AppCompatActivity {
private Button mBtStart;
private Messenger messenger = null;
private boolean mBound;
private TextView mTvMsg;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
messenger = new Messenger(service);
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
messenger = null;
mBound = false;
}
};
public void sayHello(View v){
if(!mBound) return;
Message msg = Message.obtain(null,CustomService.MSG_SAY_HELLO,0,0);
try{
messenger.send(msg);
}catch (RemoteException e){
e.printStackTrace();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtStart = (Button) findViewById(R.id.bt_start);
mTvMsg = (TextView) findViewById(R.id.tv_msg);
mBtStart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,CustomService.class);
bindService(new Intent(MainActivity.this,CustomService.class),serviceConnection, Context.BIND_AUTO_CREATE);
}
});
}
@Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(serviceConnection);
mBound = false;
}
}
}
以上过程一开始理解可能会有些抽象,多动手才能加深理解。
多应用程序多进程双向通信
仍然以Activity和Service通信为例,不过这次我们需要新建两个Module(ClientApp和ServiceApp),ClientApp中就一个Activity,ServiceApp中一个Service(关于没有Activity的App的启动方式自行百度)。这样就是两个应用程序两个进程的情况了。
下面开始分析这种情况下的Messenger的用法:
Service实现
依然是先从Service开始。大致思路与上面的Service的创建类似,但又一点不同的是,这次我们需要新建一个clientMessenger来实现Service向客户端发送Message操作。在Service的Handler中有这样一段代码,
clientMessenger = msg.replyTo;//这个Message是在客户端中创建的
if(clientMessenger!=null){
Message msgToClient = Message.obtain();
msgToClient.what = SEND_MESSAGE_CODE;
Bundle bundle = new Bundle();
bundle.putString("msg","客户端,我接收到你的消息了,这是我回应给你的,看到了吗?");
msgToClient.setData(bundle);
try {
clientMessenger.send(msgToClient);
} catch (RemoteException e) {
e.printStackTrace();
}
}
上面代码用来接收到客户端发送来的数据后向客户端发送数据作出回应。注意这里的clientMessenger = msg.replyTo;,是指从客户端中取出与msg一道捆绑过来的clientMessenger对象。利用clientMessenger就可以实现向Activity中返回数据了。
详细代码如下:
public class MyService extends Service {
private static final int RECEIVE_MESSAGE_CODE = 0x0001;
private static final int SEND_MESSAGE_CODE = 0x0002;
private Messenger clientMessenger = null;
private Messenger serviceMessenger = new Messenger(new ServiceHandler());
@Override
public IBinder onBind(Intent intent) {
return serviceMessenger.getBinder();
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onDestroy() {
super.onDestroy();
clientMessenger = null;
}
private class ServiceHandler extends Handler{
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what == RECEIVE_MESSAGE_CODE){
Bundle data = msg.getData();
if(data != null){
String str = data.getString("msg");
Toast.makeText(getApplicationContext(),"Service:I received the message:"+str,Toast.LENGTH_SHORT).show();
}
clientMessenger = msg.replyTo;//这个Message是在客户端中创建的
if(clientMessenger!=null){
Message msgToClient = Message.obtain();
msgToClient.what = SEND_MESSAGE_CODE;
Bundle bundle = new Bundle();
bundle.putString("msg","客户端,我接收到你的消息了,这是我回应给你的,看到了吗?");
msgToClient.setData(bundle);
try {
clientMessenger.send(msgToClient);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
}
}
Activity实现:
Activity中主要是实现绑定发送数据和解除绑定两大块功能。绑定先发送显式Intent(5.0以上不支持隐式启动Service,具体操作见下面的启动过程)绑定Service,当绑定成功后获取Messenger对象并使用该对象发送Message对象msg给Service,具体操作与上面的一样。这里有一点不一样的是,为了能够使得Service能获得clientMessenger,我们必须手动将msg与clientMessenger关联,即:msg.replyTo = clientMessenger;。这样Service在获得Activity发送过来的Message的同时也可以取到clientMessenger。而clientMessenger必须先创建出来,方法与单向获取时一致。
详细代码如下:
public class MainActivity extends AppCompatActivity{
private Button mBtBind;
private Button mBtUnBind;
private TextView mTvMsg;
private static final int SEND_MESSAGE_CODE = 0x0001;
private static final int RECEIVE_MESSAGE_CODE = 0x0002;
private boolean isBound = false;
private String SERVICE_ACTION = "com.example.serviceapp.MyService";
private Messenger serviceMessenger = null;
private Messenger clientMessenger = new Messenger(new ClientHandler());
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtBind = (Button) findViewById(R.id.bt_bind);
mBtUnBind = (Button) findViewById(R.id.bt_unbind);
mTvMsg = (TextView) findViewById(R.id.tv_msg);
mBtUnBind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(isBound){
unbindService(serviceConnection);
}
}
});
mBtBind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(!isBound){
Intent intent = new Intent();
intent.setAction(SERVICE_ACTION);
intent.addCategory(Intent.CATEGORY_DEFAULT);
PackageManager pm = getPackageManager();
ResolveInfo info = pm.resolveService(intent,0);
if(info != null){
String packageName = info.serviceInfo.packageName;
String serviceName = info.serviceInfo.name;
ComponentName componentName = new ComponentName(packageName,serviceName);
intent.setComponent(componentName);
bindService(intent,serviceConnection,BIND_AUTO_CREATE);
}
}
}
});
}
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
serviceMessenger = new Messenger(service);
isBound = true;
Message msg = Message.obtain();
msg.what = SEND_MESSAGE_CODE;
Bundle data = new Bundle();
data.putString("msg","你好,MyService,我是客户端");
msg.setData(data);
msg.replyTo = clientMessenger;
try {
serviceMessenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
serviceMessenger = null;
isBound = false;
}
};
private class ClientHandler extends Handler{
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what == RECEIVE_MESSAGE_CODE){
Bundle data = msg.getData();
if(data != null){
String str = data.getString("msg");
mTvMsg.setText(str);
}
}
}
}
}
总结:
这一块理解起来可能比较吃力,所以希望读者多加尝试,为后面的AIDL跨通信方式学习做准备。


猜你喜欢
- 记录单击、双击实现过程,进行简单的封装,便于复用,包括常用的软件双击退出。双击实现:记录第一次点击时间,在设定时间内再次点击,则返回监听事件
- 在进行详解之前,我想先声明一下,本次我们进行讲解说明的是 Kafka 消息存储的信息文件内容,不是所谓的 Kafka 服务器运行产生的日志文
- 泛型泛型的语法定义class 类名 <泛型标识,泛型标识,…>{ private 泛型标识1,变量名;常用
- Future和Promise执行回调Netty中的Future, 其实类似于jdk的Future, 用于异步获取执行结果Promise则相当
- 上移动端的测试课,老师和同学们用的都是eclipse, 只有我一个人用的是idea(用了两款软件之后觉得IDEA更好),真的太难了,配置
- 这是一个android开屏布局的实例,可以用于加载广告图片和倒计时的布局。程序中设置的LayoutParams,划分额外空间比例为6分之5,
- Javaweb开发环境的配置也是比较繁琐的一件事情,虽然理论上使用记事本,完全可以写出一个Javaweb工程,但是在团队大型开发的Javaw
- 使用@Tolerate实现冲突兼容使用Lombok能够减少程序员的重复工作提高工作效率,而Lombok的注解基本是基于标准的(如,标准的Bu
- 那些GC的默认值其实GC或者说JVM的参数非常非常的多,有控制内存使用的:有控制JIT的:有控制分代比例的,也有控制GC并发的:当然,大部分
- 教程展示了如何在Spring应用程序中使用GenericApplicationContext 。在该示例中,我们创建了一个Spring Bo
- 本文实例讲述了C#使用GDI绘制直线的方法。分享给大家供大家参考。具体实现方法如下:Point p1=new Point(200,200);
- 前言上一篇通过clusterservice对cluster做了一个简单的概述, 应该能够给大家一个初步认识。本篇将对cluster的代码组成
- 1、什么是 ThreadLocal:ThreadLocal,即线程本地变量,如果你创建了一个变量,那么访问这个变量的每个线程都会有这个变量的
- Result也是Struts2比较重要的一部分,在Result的配置中常用的有四种类型:dispatcher、redirect、chain和
- 最近修改线上bug的时候排查了一个十分隐藏的bug,直接上代码:Integer a = null;boolean flag = true;I
- 目录说明使用常见问题No such instance field: 'logger2'说明logback作为log4j的替代
- APK是Android系统的发布的工程包,很多时候我们想在电脑上而非Android手机上面运行它。下面就提供下Android APK文件在电
- 先从本地把图片上传到服务器,然后根据URL把头像处理成圆形头像。因为上传图片用到bmob的平台,所以要到bmob(http://www.bm
- 什么是Mapping同样的,我们先讲基本概念,什么是mapping,上节给大家简要的举了一个例子,还有印象吗?mapping是es中一个比较
- 目录I. 环境配置1. 项目配置2. 数据库表II. 传参类型确定1. 参数类型为整形2. 指定jdbcType3. 传参类型为String