参考资料:

1、《Android开发艺术探索》第二章2.4.3

2、【Messenger完全解析】

1、Messenger概述

  Messenger,译为“信使”,是Android中一种基于Binder机制的IPC(Inter-Process Communication,进程间通信)方式。通过Messenger可以在不同进程中传递Message对象,在Message中放入我们需要传递的数据,就可以轻松的实现进程间数据的传递了。即:Messenger允许实现基于消息的进程间通信的方式。

  Messenger是一种轻量级的IPC方案,其实现底层是AIDL。

  Messenger一次只能处理一个请求,因此在服务端我们不用考虑线程同步的问题,这是因为服务端不存在并发执行的情形。

  使用Messenger进行进程间通信的具体流程是:客户端发送一个Message给服务端,在服务端的Handler中接收到客户端的消息,然后进行对应的处理,处理完成后再将结果等数据封装成Message对象,发送给客户端,客户端的Handler中会接收到服务端传过来的数据并进行处理。

2、Messenger通信实例

  在这个例子中,客户端的界面上有一个按钮,通过点击这个按钮,向服务端发送两个参数,服务端将这两个参数相加后回传给客户端,客户端收到答案之后显示到界面上。

2.1、服务端

  服务端的需要新建一个Service,命名为MessengerService,代码如下:

package my.itgungnir.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.support.annotation.Nullable; /**
* Messenger 服务端Service
* Created by ITGungnir on 2017/4/5.
*/
public class MessengerServer extends Service {
private static final int MSG_GETSUM = 0x001; // Messenger对象,其中的Handler用来接收从客户端传过来的Message,通过可以创建Message对象回传给客户端
Messenger mMessenger = new Messenger(new Handler() {
@Override
public void handleMessage(Message msgFromClient) {
// 要回传给客户端的Message对象
Message msgToClient = Message.obtain(msgFromClient);
switch (msgFromClient.what) {
case MSG_GETSUM:
msgToClient.what = MSG_GETSUM;
try {
Thread.sleep(2000); // 模拟耗时
msgToClient.arg2 = msgFromClient.arg1 + msgFromClient.arg2;
// Message的replyTo也是一个Messenger对象
msgFromClient.replyTo.send(msgToClient);
} catch (Exception e) {
e.printStackTrace();
}
break;
}
super.handleMessage(msgFromClient);
}
}); @Nullable
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder(); // 获取Messenger中的IBinder对象
}
}

  可以看到,服务端要做的工作是手动创建一个Messenger,然后在onBind()方法中返回这个Messenger对象的getBinder()对象。

  在创建的Messenger中,参数是一个Handler,即从客户端向服务端发送消息时,就是将消息发送到了这个Handler对象中。因此,我们需要在这个Handler对象中处理客户端传过来的消息,当然也可以创建一个新的消息,返回给客户端。

  因为服务端使用了Android四大组件中的Service,因此需要在Menifest文件中进行注册,代码如下:

<service android:name=".MessengerServer">
<intent-filter>
<action android:name="my.itgungnir.messenger.getsum" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>

2.2、客户端

  在客户端中需要做的事情比服务端多。首先,我们需要先绑定到服务端的服务上,然后才可以向服务端请求服务。下面是客户端首页MainActivity.java类中的代码:

package my.itgungnir.client;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
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.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView; public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final int MSG_GETSUM = 0x001; private LinearLayout container; // 盛放所有算术题的TextView的容器(LinearLayout)
private TextView state; // 显示服务器连接状态的TextView
private Button require; // 向服务端请求算术题答案的按钮 private Messenger mService; // 客户端的Messenger对象
private boolean isConnected; // 指示是否连接到服务端
private int mArg1 = 0; // 算术题的被加数,同时也是TextView的id @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
bindToServer();
} // 初始化界面中的布局,并绑定相应的事件
private void initViews() {
container = (LinearLayout) findViewById(R.id.client_ly_container);
state = (TextView) findViewById(R.id.client_tv_connection);
require = (Button) findViewById(R.id.client_btn_require);
require.setOnClickListener(this);
} // 获取服务端绑定状态的ServiceConnection对象
private ServiceConnection connection = new ServiceConnection() {
@Override // 绑定服务端成功时回调的方法
public void onServiceConnected(ComponentName name, IBinder service) {
mService = new Messenger(service);
isConnected = true;
state.setText("Server Connected!");
} @Override // 绑定服务端失败时回调的方法
public void onServiceDisconnected(ComponentName name) {
mService = null;
isConnected = false;
state.setText("Server Not Connected!");
}
}; // 绑定到服务端
private void bindToServer() {
Intent intent = new Intent();
intent.setAction("my.itgungnir.messenger.getsum");
// Android 5.0 及以上的设备需要为intent设置package,否则会报错:Service Intent must be explicit
intent.setPackage("my.itgungnir.server");
bindService(intent, connection, Context.BIND_AUTO_CREATE);
} // 客户端的Messenger对象
private Messenger mMessenger = new Messenger(new Handler() {
@Override
public void handleMessage(Message msgToClient) {
switch (msgToClient.what) {
case MSG_GETSUM:
// 接收服务端回传的数据并在Handler中更新UI
TextView tv = (TextView) findViewById(msgToClient.arg1);
tv.setText(tv.getText() + " ==> " + msgToClient.arg2);
break;
}
super.handleMessage(msgToClient);
}
}); // 每点击一次按钮,就向服务端发送一条消息
@Override
public void onClick(View v) {
try {
int arg1 = ++mArg1;
int arg2 = (int) (Math.random() * 90 + 10);
TextView tv = new TextView(MainActivity.this);
tv.setTextSize(18f);
tv.setText(arg1 + " + " + arg2 + " = Calculating... ");
tv.setId(arg1);
container.addView(tv);
// 要发送到服务端的Message对象
Message msgFromClient = Message.obtain(null, MSG_GETSUM, arg1, arg2);
msgFromClient.replyTo = mMessenger;
if (isConnected) {
// 将消息发送给服务端Messenger的Handler对象
mService.send(msgFromClient);
}
} catch (RemoteException e) {
e.printStackTrace();
}
} @Override
protected void onDestroy() {
super.onDestroy();
unbindService(connection);
}
}

  从代码中可以看到,调用bindService()方法之后,在ServiceConnection对象的回调方法中的onServiceConnected()方法(连接服务端成功的回调方法)中,会携带一个IBinder类型的参数,这个参数就是服务端的服务对象,我们可以通过 new Messenger(service) 方法获取到服务端的Messenger对象,然后就可以通过这个Messenger对象,向服务端的Handler发送消息了。

  布局文件activity_main.xml文件中的代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/client_ly_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="15.0dip"> <TextView
android:id="@+id/client_tv_connection"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/black"
android:textSize="20.0sp"
android:textStyle="bold" /> <Button
android:id="@+id/client_btn_require"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="15.0dip"
android:layout_marginTop="15.0dip"
android:text="Send Message To Server" /> </LinearLayout>

2.3、运行结果

  先运行服务端,再运行客户端,如果连接服务端成功,客户端的状态TextView中会显示 connected 字样;此时我们点击客户端中的按钮,向服务端发送算术题,然后等待服务端计算后将结果返回给客户端。

  运行结果如下图所示:

3、 总结

  从上面的例子中,我们大致可以看出Messenger的工作机制:

  主要是客户端向服务端发送消息请求服务,服务端接收到客户端的请求后提供相应的服务,然后将服务结果封装成消息对象回传给客户端,客户端收到服务端的回传结果对其进行处理使用(如更新UI界面)。

  这里需要注意的有三点:

  • Messenger中的send()方法:这个方法的作用是将一个Message对象发送给调用这个方法的Messenger的Handler对象(就是new Messenger的时候参数中传入的Handler对象);
  • Message对象有一个Messenger类型的replyTo属性,这个属性指定了这条消息的回传对象。在上面的例子中,我们在客户端指定了发送给服务端的Message的replyTo属性是客户端的Messenger,这样在服务端才更容易的拿到客户端的具体的Messenger对象并进行消息的回传;
  • Messenger的底层实现是AIDL,其机制也和AIDL相同。

【Android - IPC】之Messenger简介的更多相关文章

  1. android IPC及原理简介

    什么是Android操作系统,所谓的Android:是基于Linux内核的软件平台和操作系统,早期由Google开发,后由开放手机联盟Open Handset Alliance)开发.   Linux ...

  2. android IPC通信(上)-sharedUserId&amp;&amp;Messenger

    看了一本书,上面有一章解说了IPC(Inter-Process Communication,进程间通信)通信.决定结合曾经的一篇博客android 两个应用之间的通信与调用和自己的理解来好好整理总结一 ...

  3. Android Programming: Pushing the Limits -- Chapter 7:Android IPC -- Messenger

    Messenger类实际是对Aidl方式的一层封装.本文只是对如何在Service中使用Messenger类实现与客户端的通信进行讲解,对Messenger的底层不做说明.阅读Android Prog ...

  4. 【Android - IPC】之Binder机制简介

    参考资料: 1.<Android开发艺术探索>第二章2.3.3 Binder 2.[Android Binder设计与实现-设计篇] 3.[Android Binder机制介绍] 1. 什 ...

  5. Android Programming: Pushing the Limits -- Chapter 7:Android IPC -- ApiWrapper

    前面两片文章讲解了通过AIDL和Messenger两种方式实现Android IPC.而本文所讲的并不是第三种IPC方式,而是对前面两种方式进行封装,这样我们就不用直接把Aidl文件,java文件拷贝 ...

  6. Android IPC 结篇

    一.概述 Android 的 IPC 方式有 Bundle .共享文件.AIDL .Messenger .ContentProvider .Socket ,我们在实现进程间通信时要选择哪一种方式来实现 ...

  7. Android IPC机制(三)在Android Studio中使用AIDL实现跨进程方法调用

    在上一篇文章Android IPC机制(二)用Messenger进行进程间通信中我们介绍了使用Messenger来进行进程间通信的方法.可是我们能发现Messenger是以串行的方式来处理client ...

  8. Android IPC(inter-process Communitcation)

    Android IPC(inter-process Communitcation) http://www.cnblogs.com/imlucky/archive/2013/08/08/3246013. ...

  9. 【转】Android 防破解技术简介

    http://www.cnblogs.com/likeandroid/p/4888808.html Android 防破解技术简介 这几年随着互联网的不断发展,Android App 也越来越多!但是 ...

  10. GitHub Android Librarys Top 100 简介

    GitHub Android Librarys Top 100 简介 本项目主要对目前 GitHub 上排名前 100 的 Android 开源库进行简单的介绍, 至于排名完全是根据GitHub搜索J ...

随机推荐

  1. ArcGIS Engine空间分析之缓冲区分析的实现

    缓冲分析(BufferAnalysis)的结果是一个面状要素——即缓冲要素,点状要素.线状要素和面状要素,被缓冲分析功能处理过之后,它们的周围产生一个缓冲区域,该区域即新产生的面状要素. 在缓冲方向上 ...

  2. MIT线性代数:6.列向量和零空间

  3. Covenant cc 用法

    新出现的cc框架,之前看hack the box有人用过,不过还是用cs比较多, 这里把之前的笔记搬运过来 ---   设置covenant git clone --recurse-submodule ...

  4. python中函数名后面带()和不带()的区别。

    今天天气不冷,微热.9.18警钟长鸣,国人当自强不息. python中有时候会遇到一个函数名称后面没有带()被调用,这是为什么呢?看下面这个例子. def target(): #定义一个函数 prin ...

  5. java 打包web 项目

    1 选择你的web项目 2 右击,选择export 3 选择web下的war file 4 将打包好war包,保存在tomcat的webapps下 5 运行tomcat,tomcat会自动帮你解压这个 ...

  6. phpStudy中MySQL版本升级到5.7.17方法

    本文主要给大家介绍了关于phpStudy中升级MySQL版本到5.7.17的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起看看吧.希望能帮 ...

  7. Nebula 架构剖析系列(二)图数据库的查询引擎设计

    摘要 上文(存储篇)说到数据库重要的两部分为存储和计算,本篇内容为你解读图数据库 Nebula 在查询引擎 Query Engine 方面的设计实践. 在 Nebula 中,Query Engine ...

  8. 领扣(LeetCode)有效的括号 个人题解

    给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效. 有效字符串需满足: 左括号必须用相同类型的右括号闭合. 左括号必须以正确的顺序闭合. 注意空字符串可被认 ...

  9. 【dp】Bone Collector II

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2639 题意: 01背包第k优解, 背包九讲原题.“ 对于求次优解.第K优解类的问题,如果相应的最优解问 ...

  10. SpringSecurity退出功能实现的正确方式

    本文将介绍在Spring Security框架下如何实现用户的"退出"logout的功能.其实这是一个非常简单的功能,我见过很多的程序员在使用了Spring Security之后, ...