分类:C#、Android、VS2015;

创建日期:2016-03-03

一、Messager类简介

本章前面曾经说过,要在Android上执行带服务的进程间通信(IPC),既可以用Messenger类来实现,也可以用更高级的AIDL技术来实现(AIDL:Android接口定义语言)。

这一节我们学习首选的方案:用Messenger实现IPC的基本设计思路。

IPC:进程间通信(Inter-process Communication)。

IPC with Service:带服务的进程间通信。

实际上,IPC就是C/S模式的一个简单的实现。当用Messenger实现进程间通信时,它用一个单独的进程将从客户端发送的消息全部放入一个消息队列中,然后再从该队列中每次取出一个消息来处理。此外,Messenger并不是给客户端公开一个服务接口,而是让客户端发送消息对象给继承自Handler的类的实例,然后让该实例去处理消息对象来实现服务。

二、示例4功能和运行截图

1、例子功能

该例子演示如何利用消息服务实现不同进程间的消息传递,假定有进程A和进程B同时运行,并准备从进程B中向进程A发送消息。因此,在这个例子中,例子需要分别设计服务器端(进程A)和客户端(进程B)。

服务端(MessengerServiceDemo):表示接收消息的进程,该进程必须提供消息服务才能接收消息。

客户端(MessengerClientDemo):发送消息的进程,在该进程中,可发送消息给提供服务的进程。

分别实现服务器端代码和客户端代码后,就可以实现不同进程间的消息传递了。

2、运行截图

说明:本例子无法在Android 4.4.2(API 19)上运行,原因未知。

下面的截图是在Android 6.0(API 23)模拟器下运行的结果。先运行服务器端程序(效果见左侧图),单击服务器的【启动服务】后,再单击下方的小方块运行客户端程序(效果见右侧图)。

 

单击客户端程序的【发送消息】按钮,再单击下方的小方块切换到服务器程序,就会看到服务端已经接收到了客户端发送的消息,如下面的界面所示:

单击【停止服务】终止运行。

实际上,应用程序中使用最多的还是通过Messenger和Android系统服务或者Web服务进行消息传递,这个例子虽然已经尽可能简化了,但仍然搞的有点复杂,这样做的目的仅仅是为了让你看出这确实是两个进程之间在传递消息。

三、设计步骤

下面是相关的实现代码。

1、服务端代码

(1)添加ch1704_Main.xml文件

<?xml version="1.0" encoding="utf-8" ?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Button
android:id="@+id/startService"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="启动服务" />
<Button
android:id="@+id/stopService"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="停止服务" />
<ListView
android:minWidth="25px"
android:minHeight="25px"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/listView1" />
</LinearLayout>

(2)添加ch1704MessengerService.cs文件

using Android.App;
using Android.Content;
using Android.OS; namespace MyDemos.SrcDemos
{
[Service]
[IntentFilter(new string[] { action })]
public class ch1704MessengerService : Service
{
public const string action = "com.mydemos.ch1704MessengerService";
private Messenger messenger; public ch1704MessengerService()
{
messenger = new Messenger(new MessengerServiceHandler());
} public override IBinder OnBind(Intent intent)
{
ch1704MainActivity.adapter.Add("消息客户端已绑定到本消息服务");
return messenger.Binder;
} private class MessengerServiceHandler : Handler
{
public override void HandleMessage(Message msg)
{
string text = msg.Data.GetString("InputText");
string s = string.Format("来自消息客户端的消息:\nWhat={0}\nInputText={1}",
msg.What, text);
ch1704MainActivity.adapter.Add(s);
}
}
}
}

(3)添加ch1704MainActivity.cs文件

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Widget; namespace MyDemos.SrcDemos
{
[Activity(Label = "ch1704MainActivity")]
public class ch1704MainActivity : Activity
{
public static ArrayAdapter<string> adapter;
Intent serviceIntent; protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.ch1704_Main); var listView1 = FindViewById<ListView>(Resource.Id.listView1);
adapter = new ArrayAdapter<string>(this, Android.Resource.Layout.TestListItem);
listView1.Adapter = adapter; var start = FindViewById<Button>(Resource.Id.startService);
start.Click += delegate
{
serviceIntent = new Intent(ch1704MessengerService.action);
StartService(serviceIntent);
adapter.Add("消息服务已启动");
}; var stop = FindViewById<Button>(Resource.Id.stopService);
stop.Click += delegate
{
StopService(serviceIntent);
adapter.Add("消息服务已停止");
};
}
}
}

(4)运行服务端程序

运行后不要关闭它。

2、客户端代码

(1)新建客户端项目

项目和解决方案名:ch1704MessengerClientDemo

模板:Blank App(Android)

(2)Main.axml文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Button
android:id="@+id/callMessenger"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="发送消息" />
</LinearLayout>

(3)MainActivity.cs文件

using Android.App;
using Android.Content;
using Android.Widget;
using Android.OS;
namespace MessengerClientDemo
{
[Activity(Label = "MessengerClientDemo", MainLauncher = true, Icon = "@drawable/icon")]
public class MainActivity : Activity
{
bool isBound = false;
Messenger messenger;
ServiceConnection conn; protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main); var button = FindViewById<Button>(Resource.Id.callMessenger);
button.Click += delegate {
if (isBound)
{
Message message = Message.Obtain();
Bundle b = new Bundle();
b.PutString("InputText", "text from client");
message.Data = b;
messenger.Send(message);
}
};
} protected override void OnStart()
{
base.OnStart();
var intent = new Intent("com.mydemos.ch1704MessengerService");
conn = new ServiceConnection(this);
BindService(intent, conn, Bind.AutoCreate);
} protected override void OnDestroy()
{
base.OnDestroy();
if (isBound)
{
UnbindService(conn);
isBound = false;
}
} class ServiceConnection : Java.Lang.Object, IServiceConnection
{
MainActivity activity; public ServiceConnection(MainActivity activity)
{
this.activity = activity;
} #region 实现IServiceConnection接口
public void OnServiceConnected(ComponentName name, IBinder service)
{
activity.messenger = new Messenger(service);
activity.isBound = true;
}
public void OnServiceDisconnected(ComponentName name)
{
activity.messenger.Dispose();
activity.messenger = null;
activity.isBound = false;
}
#endregion
}
}
}

(4)运行客户端程序

运行后单击【发送消息】,再单击下方的小方块切换到服务器程序,就会看到截图所示的界面。

四、基本实现思路

1、在服务器端绑定消息服务

利用消息传递实现服务时(别忘了刚说过Messenger就是C/S模式的一个简单实现),需要在服务器端做两件事:

1. 创建一个继承自Service的类(例如:ch1704MessengerService),并在该类中创建消息服务类的实例提供服务。

2. 创建一个继承自Handler的消息服务类(例如:HandlerDemo)。

下面的代码演示了如何在服务器端实现这两件事:

[Service]

[IntentFilter(new String[]{"com.mydemos.ch1704MessengerService"})]

public class MessengerServiceDemo : Service

{

Messenger messengerDemo;

public MessengerServiceDemo ()

{

messengerDemo = new Messenger (new StockHandler());

}

public override IBinder OnBind (Intent intent)

{

return messengerDemo.Binder;

}

class HandlerDemo : Handler

{

public override void HandleMessage (Message msg)

{

...

}

}

}

继承者Handler的HanderDemo用于客户端每次调用时接收回调。此Handler用于创建一个Messenger对象(它是一个对Handler的引用)。

请注意Handler中的 HandleMessage() 方法,这里是服务接收输入Message 的地方,也是根据what数字来决定要执行什么操作的地方。

详细点说,你的实现代码必须重写下面的两个回调方法:

  • OnServiceConnected():系统需要调用该方法将参数传递给服务中的OnBind()方法从而得到它返回的IBinder。
  • OnServiceDisconnected():当客户端与服务的连接发生意外中断时,比如服务崩溃或者被杀死时,Android系统将会自动调用该方法。

通过调用BindService(),传入已实现的ServiceConnection。

当系统调用你的OnServiceConnected()回调方法时,你就可以利用接口中定义的方法调用服务了。

2、客户端绑定到服务

客户端用IBinder将Messenger(引用服务的Handler)实例化,客户端用它向服务发送Message对象。

服务接收Handler中的每个Message——确切的说,是在handleMessage()方法中接收。

通过这种方式,客户端不需要调用服务中的“方法”。取而代之的是,客户端通过发送“消息”(Message对象)给服务,服务则接收位于Handler中的这个消息。

当客户端绑定到服务并将消息发送到包含服务的Messenger对象中时,ch1704MessengerService类就会自动调用HandleMessage方法。

(1)在客户端实现ServiceConnection

应用程序组件(客户端)可以通过调用 BindService() 来绑定服务,此时Android系统会调用服务的 OnBind() 方法,返回一个用于和服务进行交互的 IBinder。

绑定是异步进行的。 调用BindService()以后该方法将立即返回,但是并不会向客户端返回 IBinder 。客户端为了接收 IBinder,必须创建一个 ServiceConnection 的实例,并把它传给 BindService()。 ServiceConnection 包含了一个回调方法,系统将会调用该方法来传递客户端所需的那个 IBinder。

注意:只有activity、服务和content provider才可以绑定到服务——你不能从广播接收器(broadcast receiver)中绑定服务。

客户端要断开与服务的联接,请调用UnbindService()。当客户端被销毁时,与服务的绑定也将解除。但与服务交互完毕后,或者你的activity进入pause状态时,你都应该确保解除绑定,以便服务能够在执行完毕后及时关闭。

利用这个ServiceConnection ,客户端就能够把它传入 BindService() 完成与服务的绑定。

以下是有关绑定服务的一些重要注意事项:

你应该确保捕获DeadObjectException异常,当客户端与服务的连接中断时会抛出该异常。这是远程方法唯一会抛出的异常。

对象的引用计数是跨进程的。你通常应该成对地进行绑定和解除绑定,并与客户端生命周期的启动和结束过程相呼应。比如:

(1)如果仅当你的activity可见时才需要与服务进行交互,则你应该在OnStart()中进行绑定,并在OnStop()中解除绑定。

(2)如果你的activity需要在Stopped后并进入后台期间仍然能接收响应,则你可以在OnCreate()中进行绑定,并在(1)中解除绑定。请注意这表明你的activity在整个运行期间都需要使用服务(即使在后台),因此假如服务位于其它进程中,则你会增加进程的重量级,进程也会更容易被系统杀死。

注意:你通常不应该在activity的OnResume()和OnPause()中绑定和解除绑定,因为这两个回调方法在每次切换生命周期状态时都会发生,这时你应该让处理工作最少化。而且,如果应用程序中有多个activity都绑定到同一个服务上,则在两个activity间切换时都会发生状态转换,因为当前activity解除绑定(在pause时)后,紧接着下一个activity又会进行绑定(resume时),所以服务也许在销毁后马上就重建。

更多展示绑定服务的示例代码,请参阅ApiDemos中的RemoteService类。

一旦服务被所有客户端解除绑定,则Android系统将会销毁它(除非它同时又通过调用OnStartCommand()被启动了)。因此,如果你的服务就是一个纯粹的bound服务,那你就不需要管理它的生命周期——Android系统会替你管理,根据是否还有客户端对其绑定即可。

不过,如果你选择实现OnStartCommand()回调方法,那么你就必须显式地终止服务,因为此服务现在已经被视为started Service了。这种情况下,无论是否还存在客户端与其绑定,此服务都会运行下去,直至自行用StopSelf()终止或由其它组件调用StopService()来终止。

此外,如果你的服务是started Service且允许被绑定,那么系统调用你的OnUnbind()方法时,你可以选择返回true。这样做的结果就是,下次客户端绑定时将会收到OnRebind()调用而不是收到OnBind()调用,不过,虽然OnRebind()返回void,但客户端仍然能在它的OnServiceConnected()回调方法中收到IBinder。

关于started服务生命周期的更多信息,请参阅服务文档。

3、从客户端发送消息

客户端若要用Messenger来调用服务端提供的服务,客户端需要创建一个Messenger对象,然后调用该对象的Send方法发送消息。换言之,客户端需要做的事有:

  • 通过创建Messenger对象实现IServiceConnection接口。
  • 创建Messenger对象并将数据添加到该对象中。
  • 调用Messenger对象的Send方法发送消息。

(1)在客户端创建Messenger对象

如果你需要服务进行响应,那你还需要在客户端创建一个 Messenger。然后,当客户端接收到 OnServiceConnected() 回调后,再通过Send()方法发送一个Message 给服务,Send()方法中的replyTo参数中包含了客户端的Messenger。

总之,客户端要和服务打交道,要做的全部工作就是根据服务返回的IBinder创建一个 Messenger ,并用Send() 方法发送一个消息。

当客户端调用BindService时,客户端就会在IServiceConnection的实现中(红字部分)连接到服务并创建Messenger对象。例如:

protected override void OnStart ()

{

base.OnStart ();

var demoServiceIntent = new Intent ("com.mydemos.MessengerServiceDemo");

demoServiceConnection = new DemoServiceConnection (this);

BindService (demoServiceIntent, demoServiceConnection, Bind.AutoCreate);

}

protected override void OnStop ()

{

base.OnStop ();

if (isBound)

{

UnbindService (demoServiceConnection);

isBound = false;

}

}

class DemoServiceConnection : Java.Lang.Object, IServiceConnection

{

DemoMessengerActivity activity;

public DemoServiceConnection (Activity1 activity)

{

this.activity = activity;

}

public void OnServiceConnected (ComponentName name, IBinder service)

{

activity.demoMessenger = new Messenger (service);

activity.isBound = true;

}

public void OnServiceDisconnected (ComponentName name)

{

activity.demoMessenger.Dispose ();

activity.demoMessenger = null;

activity.isBound = false;

}

}

此过程非常类似于本地绑定的服务(前面的示例中讲过了)。主要的区别是,此处的代码在OnServiceConnected中创建Messenger对象并将其绑定到Activity,然后,就可以用该对象来调用服务。

(2)创建和发送消息

客户端使用Message.Obtain方法创建一条消息后,该消息就会与Android.OS.Bundle捆绑在一起,然后就可以调用Send方法发送消息了。例如:

Message message = Message.Obtain ();

Bundle b = new Bundle ();

b.PutString ("InputText", "text from client");

message.Data = b;

messengerDemo.Send(message);

其中,Message.Data属性是一个可包含多种类型数据的“键/值”对的集合。在客户端发送的消息中,“键”是“InputText”,那么,服务器端在重写的HandleMessage方法中就可以利用该键获取对应的消息("text from client"),如下面对服务器端代码所示:

class StockHandler : Handler

{

public override void HandleMessage (Message msg)

{

Log.Debug ("MessengerServiceDemo", "What = " + msg.What.ToString());

string text = msg.Data.GetString ("InputText");

Log.Debug ("MessengerServiceDemo", "InputText = " + text);

}

}

如果消息仅包含整数值,客户端也可以用更高效的方式(不再用“键/值”对来发送了,而是直接发送“值”,但是这种方式仅适用于发送整数消息,不适用于发送字符串消息):

  • 两个int类型的属性:Arg1和Arg2
  • 一个名为“What”的int类型的属性:服务器利用它可区分发送的整数消息是干什么用的(what this message is about)。

客户端通过Arg1、Arg2发送不同的整数值,利用What和HandleMessage的实现区分该消息是发送给谁的,这样以来,就可以在服务器端实现不同的操作(Action)了。

【Android】17.5 利用Messenger实现进程间通信(IPC)的更多相关文章

  1. Messenger实现进程间通信(IPC)

    messenger内部也是实现aidl通信,所以可以看做一个轻量级aidl,但相对比较简单.首先开启一个服务并实现一个Handler用来处理消息,在onbind方法中返回IBinder对象,通过Ser ...

  2. 【Android】进程间通信IPC——AIDL

    AIDL官网定义AIDL(Android 接口定义语言)与您可能使用过的其他 IDL 类似. 您可以利用它定义客户端与服务使用进程间通信 (IPC) 进行相互通信时都认可的编程接口. 在 Androi ...

  3. Android进程间通信IPC

    一.IPC的说明 IPC是Inter-Process Communication的缩写,含义为进程间通信或跨进程通信,是指两个进程之间进行数据交换的过程. IPC不是Android独有的,任何一个操作 ...

  4. 【Android】进程间通信IPC——Binder

    Binder是Android中的跨进程通信方式,bindService的时候,服务端返回Binder对象,通过该对象客户端可以从服务端获取数据.在进程间通信IPC——AIDL中创建了ICustomAi ...

  5. android:使用Messenger进行进程间通信(二)

    //继续完善音乐播放器demo 相关文章: android:使用Messenger进行进程间通信(一):http://www.cnblogs.com/happyhacking/p/5318418.ht ...

  6. Android艺术开发探索——第二章:IPC机制(下)

    Android艺术开发探索--第二章:IPC机制(下) 我们继续来讲IPC机制,在本篇中你将会学习到 ContentProvider Socket Binder连接池 一.使用ContentProvi ...

  7. Android开发艺术探索——第二章:IPC机制(中)

    Android开发艺术探索--第二章:IPC机制(中) 好的,我们继续来了解IPC机制,在上篇我们可能就是把理论的知识写完了,然后现在基本上是可以实战了. 一.Android中的IPC方式 本节我们开 ...

  8. Android开发艺术探索——第二章:IPC机制(上)

    Android开发艺术探索--第二章:IPC机制(上) 本章主要讲解Android的IPC机制,首先介绍Android中的多进程概念以及多进程开发模式中常见的注意事项,接着介绍Android中的序列化 ...

  9. [原创]chromium源码阅读-进程间通信IPC.消息的接收与应答

    chromium源码阅读-进程间通信IPC.消息的接收与应答   chromium源码阅读-进程间通信IPC.消息的接收与应答 介绍 chromium进程间通信在win32下是通过命名管道的方式实现的 ...

随机推荐

  1. 优秀web资源

    http://www.filewatcher.com 一步一步asp.net_页面静态化管理 http://www.cnblogs.com/ylwn817/articles/2006923.html ...

  2. Struts2(八)访问Servlet API

    一.Struts2中的Servlet API 1.1.struts2的Action实现了MVC中C层的作用 针对请求用户显示不同的信息 登录后段保存用户信息 ----session 保存当前在线人数等 ...

  3. 使用JSP实现输出(web基础学习笔记二)

    Jsp:Java Server Page 服务器端的Java页面,动态网页技术 jsp注释 显式注释:这种注释客户端是允许看见的;<!--html注释--> 隐式注释:这种注释客户端是看不 ...

  4. Strom优化指南

    摘要:本文主要讲了笔者使用Strom中的一些优化建议 1.使用rebalance命令动态调整并发度 Storm计算以topology为单位,topology提交到Storm集群中运行后,通过storm ...

  5. SVN diff 笔记

    SVN diff命令在实际中经常使用,在此记录使用点滴. #对比工作文件与缓存在.svn的“原始”拷贝: svn diff #显示工作文件和服务器版本2的不同: svn diff -r 2 #显示分支 ...

  6. 小程序target与currentTarge区别

        文章来源:刘俊涛的博客 欢迎关注,有问题一起学习欢迎留言.评论

  7. sublime text3及插件安装过程

    本人安装的是sublime text3 1.安装 这个过程下一步下一步即可 2.激活 在help菜单中选择输入验证码,例如以下整个都是: ----- BEGIN LICENSE ----- Andre ...

  8. 对TCP性能的考虑

    #xiaodeng #对TCP性能的考虑 #HTTP权威指南 86 #对TCP性能的考虑 #HTTP紧挨着TCP,位于其上层.所以HTTP事务的性能很大程度上取决于底层tcp通道的性能. #4.2.1 ...

  9. JavaScript实现碰撞检测(分离轴定理)

    概述 分离轴定理是一项用于检测碰撞的算法.其适用范围较广,涵盖检测圆与多边形,多边形与多边形的碰撞:缺点在于无法检测凹多边形的碰撞.本demo使用Js进行算法实现,HTML5 canvas进行渲染. ...

  10. HOJ 1402 整数划分

    HOJ1402 整数划分 http://acm.hit.edu.cn/hoj/problem/view?id=1402 [题目描述] 整数划分是一个经典的问题.希望这道题会对你的组合数学的解题能力有所 ...