Android--Service之绑定服务交互

简介:

使用IBinder接口

  如果看了之前关于Service博客的人,应该对IBinder接口有所了解,这里简单介绍一下IBinder。

  IBinder是远程对象的基本接口,是为高性能而设计的轻量级远程调用机制的核心部分。但它不仅用于远程调用,也可以用于进程内调用。这个接口定义了与远程对象交互的协议,一般不直接实现这个接口,而是从它的实现类Binder中继承。

  通过IBinder进行服务的交互一般有两种方式,一种方式是使用IBinder.transact()方法向远端的IBinder对象发送一个发出调用,会回调远端的Binder.onTransact()方法,这个方法传递的数据是Parcel。Parcel是一种缓冲区,除了数据外还有有一些描述它内容的元素,如果查看源码的话会发现,Parcel本质上是一个Serialize,只是它在内存中完成了序列化和反序列化,利用的是连续的内存空间,因此效率会更高,并且AIDL的数据也是通过Parcel来交互的。另外一种方法就是抛弃IBinder中原生的方法,使用自定义的接口方法进行数据交互,这也是Android官方推荐绑定服务的一种数据交互方式。当然,不管是使用transact()给远程服务交互,还是使用自定义的接口交互,都是同步执行的,直到远程服务执行完并返回结果才会继续向下执行。

  其他关于适应IBinder服务的内容,在博客Android--Service之基础中已经讲解过了,这里不再累述。下面使用一个例子来演示一下使用自定义接口与服务进行交互的例子。

  服务:IBinderSer.java

复制代码
 1 package cn.bgxt.servicebinddatedemo;
 2 
 3 import android.app.Service;
 4 import android.content.Intent;
 5 import android.os.Binder;
 6 import android.os.IBinder;
 7 import android.os.Parcel;
 8 import android.os.RemoteException;
 9 import android.util.Log;
10 
11 public class IBinderSer extends Service {
12     private final String TAG="main";
13     private final int MULTIPLE=1024;    
14     public  final IBinder mBinder=new LocalBinder();
15     
16     public class LocalBinder extends Binder{
17         // 在Binder中定义一个自定义的接口用于数据交互
18         // 这里直接把当前的服务传回给宿主
19         public IBinderSer getService(){
20             return IBinderSer.this;
21         }                
22     }
23     
24     @Override
25     public IBinder onBind(Intent intent) {
26         Log.i(TAG, "The service is binding!");
27         // 绑定服务,把当前服务的IBinder对象的引用传递给宿主
28         return mBinder;
29     }
30     
31     public int getMultipleNum(int num){
32         // 定义一个方法 用于数据交互
33         return MULTIPLE*num;
34     }
35 }
复制代码

  调用服务的Activity:IBinderActivity.java

复制代码
 1 package cn.bgxt.servicebinddatedemo;
 2 
 3 import android.app.Activity;
 4 import android.app.Service;
 5 import android.content.ComponentName;
 6 import android.content.Intent;
 7 import android.content.ServiceConnection;
 8 import android.os.Bundle;
 9 import android.os.IBinder;
10 import android.os.Messenger;
11 import android.os.Parcel;
12 import android.os.RemoteException;
13 import android.view.View;
14 import android.widget.Button;
15 import android.widget.Toast;
16 
17 public class IBinderActivity extends Activity {
18     private Button btnStart, btnInvoke, btnStop;
19     IBinderSer mService=null;
20     private ServiceConnection mConnection = new ServiceConnection() {
21 
22         @Override
23         public void onServiceDisconnected(ComponentName name) {
24             mService = null;
25         }
26 
27         @Override
28         public void onServiceConnected(ComponentName name, IBinder service) {
29             // 获取服务上的IBinder对象,调用IBinder对象中定义的自定义方法,获取Service对象
30             IBinderSer.LocalBinder binder=(IBinderSer.LocalBinder)service;
31             mService=binder.getService();
32         }
33     };
34     
35     @Override
36     protected void onCreate(Bundle savedInstanceState) {
37         super.onCreate(savedInstanceState);
38         setContentView(R.layout.layout_service);
39         btnStart = (Button) findViewById(R.id.btnStartSer);
40         btnInvoke = (Button) findViewById(R.id.btnInvokeMethod);
41         btnStop = (Button) findViewById(R.id.btnStopSer);
42         
43         btnStart.setOnClickListener(onclick);
44         btnInvoke.setOnClickListener(onclick);
45         btnStop.setOnClickListener(onclick);
46     }
47 
48     View.OnClickListener onclick = new View.OnClickListener() {
49 
50         @Override
51         public void onClick(View v) {
52             switch (v.getId()) {
53             case R.id.btnStartSer:
54                 Toast.makeText(getApplicationContext(), "绑定服务成功", Toast.LENGTH_SHORT).show();
55                 bindService(new Intent(IBinderActivity.this,IBinderSer.class),mConnection,Service.BIND_AUTO_CREATE);                
56                 break;
57             case R.id.btnInvokeMethod:
58                 if(mService==null){
59                     Toast.makeText(getApplicationContext(), "请先绑定服务", Toast.LENGTH_SHORT).show();
60                     return;
61                 }
62                 // 调用绑定服务上的方法,进行数据交互
63                 int iResult=mService.getMultipleNum(10);
64                 Toast.makeText(getApplicationContext(), "服务计算结果为:"+iResult, Toast.LENGTH_SHORT).show();
65                 break;
66             case R.id.btnStopSer:
67                 Toast.makeText(getApplicationContext(), "服务解除绑定", Toast.LENGTH_SHORT).show();
68                 unbindService(mConnection);
69                 mService=null;
70                 break;
71             default:
72                 break;
73             }
74         }
75     };
76 }
复制代码

  执行结果:

 

 

使用Messenger类

  除了使用IBinder之外,还可以使用Messenger,那么先来聊聊什么是Messenger。

  Messenger引用了一个Handler独享,可以使用Messenger.send(Message msg)方法跨进程向服务发送消息,只需要在服务中使用Handler创建一个Messenger,宿主持有这个Messenger就可以与服务进行通信。之前介绍的handler+Message的通信方式不同,那都是在同一个进程中的,从工作线程持有一个主线程的Handler对象,从而向主线程发送消息,这里不了解的可以看看之前的博客:Android--多线程之Handler。而上面介绍过了,Android可以使用IBinder实现跨进程通信,并且也将Handler与IBinder结合起来实现跨进程发送消息。

  当然这里提一下,Messenger管理的是一个消息队列,它会依据消息进入的先后次序予以执行,所以也不需要把服务设计为线程安全是。

   实现Messenger实现进程通信,主要有以下几点注意:

  1. 在服务中实现一个Handler类,并实例化它,在handleMessage()方法中接收客户端的请求。
  2. 在服务中使用这个Handler对象创建一个Messenger对象。
  3. 使用Messenger对象的getBinder()方法返回一个IBinder对象作为onBind()的返回值返回给客户端。
  4. 在客户端使用IBinder实例化一个Messenger对象,并使用它向服务端发送信息。

  下面通过一个简单的例子来演示一下利用Messenger在服务与客户端进行的通信。

  服务:MessengerSer.java

复制代码
 1 package cn.bgxt.servicebinddatedemo;
 2 
 3 import android.app.Service;
 4 import android.content.Intent;
 5 import android.os.Handler;
 6 import android.os.IBinder;
 7 import android.os.Message;
 8 import android.os.Messenger;
 9 import android.util.Log;
10 import android.widget.Toast;
11 
12 public class MessengerSer extends Service {
13     private final String TAG="main";
14     static final int MSG_SAY_HELLO = 1;
15 
16     public class IncomingHandler extends Handler {
17         @Override
18         public void handleMessage(Message msg) {
19             switch (msg.what) {
20             case MSG_SAY_HELLO:
21                 Toast.makeText(getApplicationContext(), "Service say hello!",
22                         Toast.LENGTH_SHORT).show();
23                 Log.i(TAG, "Service say hello!");
24                 break;
25             default:
26                 super.handleMessage(msg);
27             }
28         }
29     }
30 
31     IncomingHandler incomingHandler=new IncomingHandler();
32      final Messenger mMessenger=new Messenger(new IncomingHandler());
33      
34     @Override
35     public IBinder onBind(Intent arg0) {
36         return mMessenger.getBinder();
37     }
38 
39 }
复制代码

  服务绑定的Activity:MessengerActivity.java

复制代码
 1 package cn.bgxt.servicebinddatedemo;
 2 
 3 
 4 import android.app.Activity;
 5 import android.app.Service;
 6 import android.content.ComponentName;
 7 import android.content.Intent;
 8 import android.content.ServiceConnection;
 9 import android.os.Bundle;
10 import android.os.IBinder;
11 import android.os.Message;
12 import android.os.Messenger;
13 import android.os.RemoteException;
14 import android.view.View;
15 import android.widget.Button;
16 import android.widget.Toast;
17 
18 public class MessengerActivity extends Activity {
19     private Button btnStart, btnInvoke, btnStop;
20     private Messenger mService = null;
21 
22     private ServiceConnection mConnection = new ServiceConnection() {
23 
24         @Override
25         public void onServiceDisconnected(ComponentName name) {
26             mService = null;
27         }
28 
29         @Override
30         public void onServiceConnected(ComponentName name, IBinder service) {
31             // 使用服务端的IBinder对象实例化一个Messenger对象
32             mService = new Messenger(service);
33         }
34     };
35     @Override
36     protected void onCreate(Bundle savedInstanceState) {
37         // TODO Auto-generated method stub
38         super.onCreate(savedInstanceState);
39         setContentView(R.layout.layout_service);
40         btnStart = (Button) findViewById(R.id.btnStartSer);
41         btnInvoke = (Button) findViewById(R.id.btnInvokeMethod);
42         btnStop = (Button) findViewById(R.id.btnStopSer);
43         
44         btnStart.setOnClickListener(onclick);
45         btnInvoke.setOnClickListener(onclick);
46         btnStop.setOnClickListener(onclick);
47     }
48 
49     View.OnClickListener onclick = new View.OnClickListener() {
50 
51         @Override
52         public void onClick(View v) {
53             switch (v.getId()) {
54             case R.id.btnStartSer:
55                 Toast.makeText(getApplicationContext(), "绑定服务成功", Toast.LENGTH_SHORT).show();
56                 bindService(new Intent(getApplicationContext(),MessengerSer.class), mConnection, Service.BIND_AUTO_CREATE);
57                 break;
58             case R.id.btnInvokeMethod:
59                 if(mService==null){
60                     Toast.makeText(getApplicationContext(), "请先绑定服务",Toast.LENGTH_SHORT).show();
61                     return ;
62                 }
63                 // 实例化一个Message对象
64                 Message msg=Message.obtain(null, MessengerSer.MSG_SAY_HELLO, 0, 0);
65                 try{
66                     // 把Message独享传递给服务端处理
67                     mService.send(msg);
68                 }
69                 catch(RemoteException e){
70                     e.printStackTrace();
71                 }
72                 break;
73             case R.id.btnStopSer:
74                 Toast.makeText(getApplicationContext(), "服务解除绑定", Toast.LENGTH_SHORT).show();
75                 unbindService(mConnection);
76                 mService=null;
77                 break;
78             default:
79                 break;
80             }
81 
82         }
83     };
84 }
复制代码

  执行结果:

 

 

使用AIDL

  AIDL(Android Interface Definition Language),它可以实现跨进程间的通信。之前讲到的Messenger实现跨进程通信,其实也是基于AIDL作为底层结构。但是正如上面提到的,Messenger创建的一个消息队列是在一个单独的线程中,所以服务一次仅处理一个请求,然而,如果想要服务同时处理多个请求,就需要使用到AIDL,但是这种情况下就要考虑多线程和线程安全的问题了。这个不在本篇博客的范畴内,以后有机会在细细讲解。


本文转自承香墨影博客园博客,原文链接:http://www.cnblogs.com/plokmju/p/android_ServiceCommunication.html,如需转载请自行联系原作者


相关文章
|
5月前
|
开发工具 数据库 Android开发
0001Java安卓程序设计-基于Android多餐厅点餐桌号后厨前台服务设计与开发2
0001Java安卓程序设计-基于Android多餐厅点餐桌号后厨前台服务设计与开发
28 0
|
7月前
|
编解码 物联网 开发工具
Android平台内网RTSP网关和轻量级RTSP服务的区别和联系
我们在对接轻量级RTSP服务的时候,遇到客户这样的使用场景:客户是用于车载自组网环境,确保多辆车之间可以相互看到对方的实时视频,以期可以了解到前方路况等关注的信息。
100 0
|
7月前
|
编解码 监控 开发工具
如何同时启动Android平台GB28181设备接入模块和轻量级RTSP服务模块?
为什么要设计GB28181设备接入模块?GB28181接入SDK,实现不具备国标音视频能力的 Android终端,通过平台注册接入到现有的GB/T28181—2016服务,可用于如智能监控、智慧零售、智慧教育、远程办公、生产运输、智慧交通、车载或执法记录仪等场景。Android终端除支持常规的音视频数据接入外,还可以支持移动设备位置(MobilePosition)订阅和通知、语音广播和语音对讲、云台控制和预置位查询等。
|
7月前
|
编解码 开发工具 Android开发
Android平台RTSP轻量级服务|RTMP推送摄像头或屏幕之音频接口设计
好多开发者在做Android平台录像或者RTSP轻量级服务、RTMP推送相关模块时,对需要设计哪些常用接口会心存疑惑,本文主要以大牛直播SDK(官方)为例,简单介绍下Android平台直播推送SDK所有音频相关的接口,感兴趣的开发者可以看看。
|
4月前
|
XML Java Android开发
Android Studio App开发之服务Service的讲解及实战(包括启动和停止,绑定与解绑,推送服务到前台实现音乐播放器,附源码)
Android Studio App开发之服务Service的讲解及实战(包括启动和停止,绑定与解绑,推送服务到前台实现音乐播放器,附源码)
98 0
|
7月前
|
安全 网络协议 Linux
在安卓Termux上搭建apache服务创建个人站点并且结合cpolar内网穿透
在安卓Termux上搭建apache服务创建个人站点并且结合cpolar内网穿透工具实现公网访问。
在安卓Termux上搭建apache服务创建个人站点并且结合cpolar内网穿透
|
7月前
|
Web App开发 开发工具 Android开发
Android平台不需要单独部署流媒体服务如何实现内网环境下一对一音视频互动
我们在做内网环境的一对一音视频互动的时候,遇到这样的技术诉求:如智能硬件场景下(比如操控智能硬件),纯内网环境,如何不要单独部署RTMP或类似流媒体服务,实现一对一音视频互动。
|
7月前
|
编解码 开发工具 Android开发
Android平台如何实现外部RTSP|RTMP流注入轻量级RTSP服务模块(内网RTSP网关)
今天分享的是外部RTSP或RTMP流,拉取后注入到本地轻量级RTSP服务模块,供内网小并发场景下使用,这里我们叫做内网RTSP网关模块。
|
3月前
|
开发工具 Android开发
Android平台RTMP推送|轻量级RTSP服务|GB28181设备接入模块之实时快照保存JPG还是PNG?
Android平台RTMP推送|轻量级RTSP服务|GB28181设备接入模块之实时快照保存JPG还是PNG?
|
3月前
|
数据采集 编解码 图形学
Android平台Unity下如何通过WebCamTexture采集摄像头数据并推送至RTMP服务器或轻量级RTSP服务
Android平台Unity下如何通过WebCamTexture采集摄像头数据并推送至RTMP服务器或轻量级RTSP服务