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,如需转载请自行联系原作者


相关文章
|
3月前
|
Java Linux Android开发
移动应用开发与操作系统的交互:深入理解Android和iOS
在数字时代,移动应用成为我们日常生活的一部分。本文将深入探讨移动应用开发的核心概念、移动操作系统的工作原理以及它们如何相互作用。我们将通过实际代码示例,展示如何在Android和iOS平台上创建一个简单的“Hello World”应用,并解释其背后的技术原理。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和知识。
|
7月前
|
调度 Android开发
43. 【Android教程】服务:Service
43. 【Android教程】服务:Service
69 2
|
3月前
|
安全 Java 网络安全
Android远程连接和登录FTPS服务代码(commons.net库)
Android远程连接和登录FTPS服务代码(commons.net库)
46 1
|
4月前
|
JavaScript 前端开发 Android开发
让Vite+Vue3项目在Android端离线打开(不需要起服务)
让Vite+Vue3项目在Android端离线打开(不需要起服务)
157 10
|
4月前
|
调度 Android开发 UED
Android经典实战之Android 14前台服务适配
本文介绍了在Android 14中适配前台服务的关键步骤与最佳实践,包括指定服务类型、请求权限、优化用户体验及使用WorkManager等。通过遵循这些指南,确保应用在新系统上顺畅运行并提升用户体验。
316 6
|
4月前
|
安全 API 开发工具
Android平台RTMP推送|轻量级RTSP服务如何实现麦克风|扬声器声音采集切换
Android平台扬声器播放声音的采集,在无纸化同屏等场景下,意义很大,早期低版本的Android设备,是没法直接采集扬声器audio的(从Android 10开始支持),所以,如果需要采集扬声器audio,需要先做系统版本判断,添加相应的权限。
|
4月前
|
编解码 开发工具 Android开发
Android平台实现屏幕录制(屏幕投影)|音频播放采集|麦克风采集并推送RTMP或轻量级RTSP服务
Android平台屏幕采集、音频播放声音采集、麦克风采集编码打包推送到RTMP和轻量级RTSP服务的相关技术实现,做成高稳定低延迟的同屏系统,还需要有配套好的RTMP、RTSP直播播放器
|
5月前
|
数据处理 开发工具 数据安全/隐私保护
Android平台RTMP推送|轻量级RTSP服务|GB28181接入之文字、png图片水印的精进之路
本文探讨了Android平台上推流模块中添加文字与PNG水印的技术演进。自2015年起,为了满足应急指挥及安防领域的需求,逐步发展出三代水印技术:第一代为静态文字与图像水印;第二代实现了动态更新水印内容的能力,例如实时位置与时间信息;至第三代,则优化了数据传输效率,直接使用Bitmap对象传递水印数据至JNI层,减少了内存拷贝次数。这些迭代不仅提升了用户体验和技术效率,也体现了开发者追求极致与不断创新的精神。
|
5月前
|
XML Android开发 UED
"掌握安卓开发新境界:深度解析AndroidManifest.xml中的Intent-filter配置,让你的App轻松响应scheme_url,开启无限交互可能!"
【8月更文挑战第2天】在安卓开发中,scheme_url 通过在`AndroidManifest.xml`中配置`Intent-filter`,使应用能响应特定URL启动或执行操作。基本配置下,应用可通过定义特定URL模式的`Intent-filter`响应相应链接。
133 12
|
5月前
|
数据采集 编解码 开发工具
Android平台实现无纸化同屏并推送RTMP或轻量级RTSP服务(毫秒级延迟)
一个好的无纸化同屏系统,需要考虑的有整体组网、分辨率、码率、实时延迟、音视频同步和连续性等各个指标,做容易,做好难