Android-Messenger跨进程通信
http://blog.csdn.net/lmj623565791/article/details/47017485
一.概述

我们可以在客户端发送一个Message给服务端,在服务端的handler中会接收到客户端的消息,然后进行对应的处理,处理完成后,再将结果等数据封装成Message,发送给客户端,客户端的handler中会接收到处理的结果。
有这么几个特点:
基于Message,相信大家都很熟悉
支持回调的方式,也就是服务端处理完成长任务可以和客户端交互
不需要编写aidl文件
此外,还支持,记录客户端对象的Messenger,然后可以实现一对多的通信;甚至作为一个转接处,任意两个进程都能通过服务端进行通信,这个后面再说。
二.应用
(1) Server端
package com.imooc.messenger_server; import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException; public class MessengerService extends Service
{ private static final int MSG_SUM = 0x110; //最好换成HandlerThread的形式
private Messenger mMessenger = new Messenger(new Handler()
{
@Override
public void handleMessage(Message msgfromClient)
{
Message msgToClient = Message.obtain(msgfromClient);//返回给客户端的消息
switch (msgfromClient.what)
{
//msg 客户端传来的消息
case MSG_SUM:
msgToClient.what = MSG_SUM;
try
{
//模拟耗时
Thread.sleep(2000);
msgToClient.arg2 = msgfromClient.arg1 + msgfromClient.arg2;
msgfromClient.replyTo.send(msgToClient);
} catch (InterruptedException e)
{
e.printStackTrace();
} catch (RemoteException e)
{
e.printStackTrace();
}
break;
} super.handleMessage(msgfromClient);
}
}); @Override
public IBinder onBind(Intent intent)
{
return mMessenger.getBinder();
}
}
服务端就一个Service,可以看到代码相当的简单,只需要去声明一个Messenger对象,然后onBind方法返回mMessenger.getBinder();
然后坐等客户端将消息发送到handleMessage想法,根据message.what去判断进行什么操作,然后做对应的操作,最终将结果通过 msgfromClient.replyTo.send(msgToClient);返回。
可以看到我们这里主要是取出客户端传来的两个数字,然后求和返回,这里我有意添加了sleep(2000)模拟耗时,注意在实际使用过程中,可以换成在独立开辟的线程中完成耗时操作,比如和HandlerThread结合使用。
注册文件
<service
android:name=".MessengerService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.zhy.aidl.calc"></action>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
(二)客户端
Activity
package com.imooc.messenger_client; import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView; public class MainActivity extends AppCompatActivity
{
private static final String TAG = "MainActivity";
private static final int MSG_SUM = 0x110; private Button mBtnAdd;
private LinearLayout mLyContainer;
//显示连接状态
private TextView mTvState; private Messenger mService;
private boolean isConn; private Messenger mMessenger = new Messenger(new Handler()
{
@Override
public void handleMessage(Message msgFromServer)
{
switch (msgFromServer.what)
{
case MSG_SUM:
TextView tv = (TextView) mLyContainer.findViewById(msgFromServer.arg1);
tv.setText(tv.getText() + "=>" + msgFromServer.arg2);
break;
}
super.handleMessage(msgFromServer);
}
}); private ServiceConnection mConn = new ServiceConnection()
{
@Override
public void onServiceConnected(ComponentName name, IBinder service)
{
mService = new Messenger(service);
isConn = true;
mTvState.setText("connected!");
} @Override
public void onServiceDisconnected(ComponentName name)
{
mService = null;
isConn = false;
mTvState.setText("disconnected!");
}
}; private int mA; @Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); //开始绑定服务
bindServiceInvoked(); mTvState = (TextView) findViewById(R.id.id_tv_callback);
mBtnAdd = (Button) findViewById(R.id.id_btn_add);
mLyContainer = (LinearLayout) findViewById(R.id.id_ll_container); mBtnAdd.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
try
{
int a = mA++;
int b = (int) (Math.random() * 100); //创建一个tv,添加到LinearLayout中
TextView tv = new TextView(MainActivity.this);
tv.setText(a + " + " + b + " = caculating ...");
tv.setId(a);
mLyContainer.addView(tv); Message msgFromClient = Message.obtain(null, MSG_SUM, a, b);
msgFromClient.replyTo = mMessenger;
if (isConn)
{
//往服务端发送消息
mService.send(msgFromClient);
}
} catch (RemoteException e)
{
e.printStackTrace();
}
}
}); } private void bindServiceInvoked()
{
Intent intent = new Intent();
intent.setAction("com.zhy.aidl.calc");
bindService(intent, mConn, Context.BIND_AUTO_CREATE);
Log.e(TAG, "bindService invoked !");
} @Override
protected void onDestroy()
{
super.onDestroy();
unbindService(mConn);
} }
代码也不复杂,首先bindService,然后在onServiceConnected中拿到回调的service(IBinder)对象,通过service对象去构造一个mService =new Messenger(service);然后就可以使用mService.send(msg)给服务端了。
这里插一句,大家通过代码可以看到服务端往客户端传递数据是通过msg.replyTo这个对象的。那么服务端完全可以做到,使用一个List甚至Map去存储所有绑定的客户端的msg.replyTo对象,然后想给谁发消息都可以。甚至可以把A进程发来的消息,通过B进程的msg.replyTo发到B进程那里去。相关代码呢,可以参考官方的文档:service,注意下拉找:Remote Messenger Service Sample。
三.原理
源码解析那篇文章里有非常详细的分析,这里给出我的个人理解:
先简单总结一下应用流程:
- 服务器端new Messenger(new Handler()),然后在Handler里定义handleMessage()接收和处理客户端传来的message,最后通过message.replyto.send()把结果发送回去
- 客户端new Messenger(new Handler()),然后在Handler里定义handleMessage()接收和处理服务器端传来的message。实现ServiceConnection类,并在onServiceConnected里用mService = new Messenger(service)得到远程接口,bindService连接后用mService.send发送数据给服务器端
(一)客户端向服务端通信
我们直接看new Messenger(new Handler())做了什么
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
可以看到调用了handler的一个方法
final IMessenger getIMessenger() {
synchronized (mQueue) {
if (mMessenger != null) {
return mMessenger;
}
mMessenger = new MessengerImpl();
return mMessenger;
}
}
private final class MessengerImpl extends IMessenger.Stub {
public void send(Message msg) {
msg.sendingUid = Binder.getCallingUid();
Handler.this.sendMessage(msg);
}
}
其实看到这就知道了,Messenger内部也是用AIDL实现的
下面就直接用AIDL来理解Messenger:
- 首先是AIDL文件,定义需要的方法
这个aidl位于:frameworks/base/core/java/android/os/IMessenger.aidl.,是这样的:
package android.os; import android.os.Message; /** @hide */
oneway interface IMessenger {
void send(in Message msg);
}
- 然后要在stub中实现这个方法,在onbind中返回stub
private final class MessengerImpl extends IMessenger.Stub {
public void send(Message msg) {
msg.sendingUid = Binder.getCallingUid();
Handler.this.sendMessage(msg);
}
}
和AIDL一样,服务端stub的方法是给客户端调用的,客户端利用AIDL把msg传来,再调用send方法交给服务器端的handleMessage处理
客户端
得到服务器端的IBinder对象: mService = new Messenger(service);
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
(二)服务端与客户端通信
客户端向服务器端发送消息:
Message msgFromClient = Message.obtain(null, MSG_SUM, a, b);
msgFromClient.replyTo = mMessenger;
if (isConn)
{
//往服务端发送消息
mService.send(msgFromClient);
}
客户端利用AIDL把msg传过去,再调用send方法交给服务器端的handleMessage处理
服务器接收并处理消息:
public void handleMessage(Message msgfromClient)
{
Message msgToClient = Message.obtain(msgfromClient);//返回给客户端的消息
switch (msgfromClient.what)
{
//msg 客户端传来的消息
case MSG_SUM:
msgToClient.what = MSG_SUM;
try
{
//模拟耗时
Thread.sleep(2000);
msgToClient.arg2 = msgfromClient.arg1 + msgfromClient.arg2;
msgfromClient.replyTo.send(msgToClient);
} catch (InterruptedException e)
{
e.printStackTrace();
} catch (RemoteException e)
{
e.printStackTrace();
}
break;
} super.handleMessage(msgfromClient);
}
取出客户端发过来的message,处理后用message.replyTo.send()就可以直接把结果发回去
这里的replyTo是客户端指定的
msgFromClient.replyTo = mMessenger;
mMessenger就是客户端的Messenger对象。服务器端在反序列化客户端传来的message时,会通过这个replyTo得到客户端的MessengerImpl对象。
换句话说,服务器端通过客户端的stub里的send方法,把message结果发送回去,最后在客户端的handlerMessage里处理
总结下:
客户端与服务端通信,利用的aidl文件,没什么特殊的
服务端与客户端通信,主要是在传输的消息上做了处理,让Messager.replyTo指向的客户端的Messenger,而Messenger又持有客户端的一个Binder对象(MessengerImpl)。服务端正是利用这个Binder对象做的与客户端的通信。
上面提到过通过Meaager跨进程不适合并发量大的情况,那么如果并发量大的话,我们用什么来处理呢?那就可以通过AIDL来进行,这里是Google的描述
Note: Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want
to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you
should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle
multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before
implementing an AIDL.
主要意思就是你可以用Messager处理简单的跨进程通信,但是高并发量的要用AIDL
Android-Messenger跨进程通信的更多相关文章
- 图文详解 Android Binder跨进程通信机制 原理
图文详解 Android Binder跨进程通信机制 原理 目录 目录 1. Binder到底是什么? 中文即 粘合剂,意思为粘合了两个不同的进程 网上有很多对Binder的定义,但都说不清楚:Bin ...
- 详解 CmProcess 跨进程通信的实现
CmProcess 是 Android 一个跨进程通信框架,整体代码比较简单,总共 20 多个类,能够很好的便于我们去了解跨进程实现的原理. 个人猜测 CmProcess 也是借鉴了 VirtualA ...
- 跨进程通信之Messenger
1.简介 Messenger,顾名思义即为信使,通过它可以在不同进程中传递Message对象,通过在Message中放入我们需要的入局,就可以轻松实现数据的跨进程传递了.Messenger是一种轻量级 ...
- Android随笔之——跨进程通信(一) Activity篇
在Android应用开发中,我们会碰到跨进程通信的情况,例如:你用QQ通讯录打电话的时候会调用系统的拨号应用.某些新闻客户端可以将新闻分享到QQ.微信等应用,这些都是跨进程通信的情况.简而言之,就是一 ...
- Android中的跨进程通信方法实例及特点分析(二):ContentProvider
1.ContentProvider简单介绍 在Android中有些数据(如通讯录.音频.视频文件等)是要供非常多应用程序使用的.为了更好地对外提供数据.Android系统给我们提供了Content P ...
- 【朝花夕拾】Android性能篇之(七)Android跨进程通信篇
前言 只要是面试高级工程师岗位,Android跨进程通信就是最受面试官青睐的知识点之一.Android系统的运行由大量相互独立的进程相互协助来完成的,所以Android进程间通信问题,是做好Andro ...
- android 远程Service以及AIDL的跨进程通信
在Android中,Service是运行在主线程中的,如果在Service中处理一些耗时的操作,就会导致程序出现ANR. 但如果将本地的Service转换成一个远程的Service,就不会出现这样的问 ...
- Android中的跨进程通信方法实例及特点分析(一):AIDL Service
转载请注明出处:http://blog.csdn.net/bettarwang/article/details/40947481 近期有一个需求就是往程序中增加大数据的採集点,可是由于我们的Andro ...
- 【朝花夕拾】跨进程通信,你只知道AIDL,就OUT了
一.前言 提起跨进程通信,大多数人首先会想到AIDL.我们知道,用AIDL来实现跨进程通信,需要在客户端和服务端都添加上aidl文件,并在服务端的Service中实现aidl对应的接口.如果还需要服务 ...
随机推荐
- 通过Yum快速部署LAMP与LNMP
LAMP动态网站部署架构是由一套 Linux+Apache+MySQL+PHP 组成的动态网站系统解决方案. LNMP动态网站部署架构是由一套 Linux+Nginx+MySQL+PHP 组成的动态网 ...
- java里面main函数为什么要用static修饰
这学期刚开java,因为之前只写过C++和Python没接触过java,有些写法挺不习惯的,今天写完一个程序,run的时候发现提示the selection can't be launched.... ...
- 浅析XSS与CSRF
浅析XSS与CSRF 在 Web 安全方面,XSS 与 CSRF 可以说是老生常谈了. XSS XSS,即 cross site script,跨站脚本攻击,缩写原本为 CSS,但为了和层叠样式表(C ...
- 前端代码质量保障之代码review
经验丰富的程序员和一般程序员之间的最大区别,不仅体现在解决问题的能力上, 还体现在日常代码的风格上.掌握一门技术可能需要几月,甚至几周就够了. 好的习惯风格养成却需数年. 团队成员之间需要合作,代码需 ...
- MySQL命令行登陆,远程登陆MySQL
注: MySQL图形界面管理工具[navicat 10.1.8中文绿色版] 下载地址:http://www.t00y.com/file/18393836 备用地址:http://ProCircle.q ...
- 深入浅出SQL Server中的死锁(实战篇)
简介 死锁的本质是一种僵持状态,是多个主体对于资源的争用而导致的.理解死锁首先需要对死锁所涉及的相关观念有一个理解. 一些基础知识 要理解SQL Server中的死锁,更好的方式是通过类比从更大的面理 ...
- StreamSets学习系列之StreamSets的Core Tarball方式安装(图文详解)
不多说,直接上干货! 前期博客 StreamSets学习系列之StreamSets支持多种安装方式[Core Tarball.Cloudera Parcel .Full Tarball .Full R ...
- PHP多进程系列笔记(二)
上一篇文章讲解了pcntl_fork和pcntl_wait两个函数的使用,本篇继续讲解PHP多进程相关新知识. 僵尸(zombie)进程 这里说下僵尸进程: 僵尸进程是指的父进程已经退出,而该进程de ...
- CentOS7安装sogou输入法
centos7中自带的ibus用起来相当不爽,就决定自己换个搜狗,遇到阻力不少,在此记下,防止下次不会也给大家提供一些参考.(参见<kali下安装中文输入法>) 准备阶段: 安装fcitx ...
- 一次完整的HTTP接口请求过程及针对优化
客户端发起http请求,基本的经历过程如下: 域名解析 -> TCP三次握手 -> 建立TCP连接后发起HTTP请求 -> Nginx反向代理 -> 应用层 -> 服务层 ...