Android--Service之绑定服务交互
前言
开篇名义,这篇博客介绍一下Android下使用绑定服务进行时数据交互的几种方法。关于Android下Service的内容,前面两篇博客已经介绍了,不清楚的可以移步过去先看看:Android--Service之基础、Android--Service之提高。
在前面的博客中已经介绍到了,对于Service组件而言,它只有在绑定模式下才可以与客户端进行时交互,这里讲解几个方法进行绑定服务与客户端间的交互方法:
虽然根据官方文档给出了三个方法,其中AIDL涉及的内容超出本博客内容范围,以后有机会在另外介绍,本篇博客只介绍1、2两种方式的数据交互。
使用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
package cn.bgxt.servicebinddatedemo; import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log; public class IBinderSer extends Service {
private final String TAG="main";
private final int MULTIPLE=1024;
public final IBinder mBinder=new LocalBinder(); public class LocalBinder extends Binder{
// 在Binder中定义一个自定义的接口用于数据交互
// 这里直接把当前的服务传回给宿主
public IBinderSer getService(){
return IBinderSer.this;
}
} @Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "The service is binding!");
// 绑定服务,把当前服务的IBinder对象的引用传递给宿主
return mBinder;
} public int getMultipleNum(int num){
// 定义一个方法 用于数据交互
return MULTIPLE*num;
}
}
调用服务的Activity:IBinderActivity.java
package cn.bgxt.servicebinddatedemo; import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Messenger;
import android.os.Parcel;
import android.os.RemoteException;
import android.view.View;
import android.widget.Button;
import android.widget.Toast; public class IBinderActivity extends Activity {
private Button btnStart, btnInvoke, btnStop;
IBinderSer mService=null;
private ServiceConnection mConnection = new ServiceConnection() { @Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
} @Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 获取服务上的IBinder对象,调用IBinder对象中定义的自定义方法,获取Service对象
IBinderSer.LocalBinder binder=(IBinderSer.LocalBinder)service;
mService=binder.getService();
}
}; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_service);
btnStart = (Button) findViewById(R.id.btnStartSer);
btnInvoke = (Button) findViewById(R.id.btnInvokeMethod);
btnStop = (Button) findViewById(R.id.btnStopSer); btnStart.setOnClickListener(onclick);
btnInvoke.setOnClickListener(onclick);
btnStop.setOnClickListener(onclick);
} View.OnClickListener onclick = new View.OnClickListener() { @Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnStartSer:
Toast.makeText(getApplicationContext(), "绑定服务成功", Toast.LENGTH_SHORT).show();
bindService(new Intent(IBinderActivity.this,IBinderSer.class),mConnection,Service.BIND_AUTO_CREATE);
break;
case R.id.btnInvokeMethod:
if(mService==null){
Toast.makeText(getApplicationContext(), "请先绑定服务", Toast.LENGTH_SHORT).show();
return;
}
// 调用绑定服务上的方法,进行数据交互
int iResult=mService.getMultipleNum(10);
Toast.makeText(getApplicationContext(), "服务计算结果为:"+iResult, Toast.LENGTH_SHORT).show();
break;
case R.id.btnStopSer:
Toast.makeText(getApplicationContext(), "服务解除绑定", Toast.LENGTH_SHORT).show();
unbindService(mConnection);
mService=null;
break;
default:
break;
}
}
};
}
执行结果:
使用Messenger类
除了使用IBinder之外,还可以使用Messenger,那么先来聊聊什么是Messenger。
Messenger引用了一个Handler独享,可以使用Messenger.send(Message msg)方法跨进程向服务发送消息,只需要在服务中使用Handler创建一个Messenger,宿主持有这个Messenger就可以与服务进行通信。之前介绍的handler+Message的通信方式不同,那都是在同一个进程中的,从工作线程持有一个主线程的Handler对象,从而向主线程发送消息,这里不了解的可以看看之前的博客:Android--多线程之Handler。而上面介绍过了,Android可以使用IBinder实现跨进程通信,并且也将Handler与IBinder结合起来实现跨进程发送消息。
当然这里提一下,Messenger管理的是一个消息队列,它会依据消息进入的先后次序予以执行,所以也不需要把服务设计为线程安全是。
实现Messenger实现进程通信,主要有以下几点注意:
- 在服务中实现一个Handler类,并实例化它,在handleMessage()方法中接收客户端的请求。
- 在服务中使用这个Handler对象创建一个Messenger对象。
- 使用Messenger对象的getBinder()方法返回一个IBinder对象作为onBind()的返回值返回给客户端。
- 在客户端使用IBinder实例化一个Messenger对象,并使用它向服务端发送信息。
下面通过一个简单的例子来演示一下利用Messenger在服务与客户端进行的通信。
服务:MessengerSer.java
package cn.bgxt.servicebinddatedemo; 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.util.Log;
import android.widget.Toast; public class MessengerSer extends Service {
private final String TAG="main";
static final int MSG_SAY_HELLO = 1; public class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
Toast.makeText(getApplicationContext(), "Service say hello!",
Toast.LENGTH_SHORT).show();
Log.i(TAG, "Service say hello!");
break;
default:
super.handleMessage(msg);
}
}
} IncomingHandler incomingHandler=new IncomingHandler();
final Messenger mMessenger=new Messenger(new IncomingHandler()); @Override
public IBinder onBind(Intent arg0) {
return mMessenger.getBinder();
} }
服务绑定的Activity:MessengerActivity.java
package cn.bgxt.servicebinddatedemo; import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.view.View;
import android.widget.Button;
import android.widget.Toast; public class MessengerActivity extends Activity {
private Button btnStart, btnInvoke, btnStop;
private Messenger mService = null; private ServiceConnection mConnection = new ServiceConnection() { @Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
} @Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 使用服务端的IBinder对象实例化一个Messenger对象
mService = new Messenger(service);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_service);
btnStart = (Button) findViewById(R.id.btnStartSer);
btnInvoke = (Button) findViewById(R.id.btnInvokeMethod);
btnStop = (Button) findViewById(R.id.btnStopSer); btnStart.setOnClickListener(onclick);
btnInvoke.setOnClickListener(onclick);
btnStop.setOnClickListener(onclick);
} View.OnClickListener onclick = new View.OnClickListener() { @Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnStartSer:
Toast.makeText(getApplicationContext(), "绑定服务成功", Toast.LENGTH_SHORT).show();
bindService(new Intent(getApplicationContext(),MessengerSer.class), mConnection, Service.BIND_AUTO_CREATE);
break;
case R.id.btnInvokeMethod:
if(mService==null){
Toast.makeText(getApplicationContext(), "请先绑定服务",Toast.LENGTH_SHORT).show();
return ;
}
// 实例化一个Message对象
Message msg=Message.obtain(null, MessengerSer.MSG_SAY_HELLO, 0, 0);
try{
// 把Message独享传递给服务端处理
mService.send(msg);
}
catch(RemoteException e){
e.printStackTrace();
}
break;
case R.id.btnStopSer:
Toast.makeText(getApplicationContext(), "服务解除绑定", Toast.LENGTH_SHORT).show();
unbindService(mConnection);
mService=null;
break;
default:
break;
} }
};
}
执行结果:
使用AIDL
AIDL(Android Interface Definition Language),它可以实现跨进程间的通信。之前讲到的Messenger实现跨进程通信,其实也是基于AIDL作为底层结构。但是正如上面提到的,Messenger创建的一个消息队列是在一个单独的线程中,所以服务一次仅处理一个请求,然而,如果想要服务同时处理多个请求,就需要使用到AIDL,但是这种情况下就要考虑多线程和线程安全的问题了。这个不在本篇博客的范畴内,以后有机会在细细讲解。
Android--Service之绑定服务交互的更多相关文章
- Android应用程序绑定服务(bindService)的过程源代码分析
文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6745181 Android应用程序组件Serv ...
- Android应用程序绑定服务(bindService)的过程源码分析
Android应用程序组件Service与Activity一样,既能够在新的进程中启动,也能够在应用程序进程内部启动:前面我们已经分析了在新的进程中启动Service的过程,本文将要介绍在应用程序内部 ...
- 【起航计划 034】2015 起航计划 Android APIDemo的魔鬼步伐 33 App->Service->Local Service Binding 绑定服务 ServiceConnection Binder
本例和下列Local Service Controller 的Activity代码都定义在LocalServiceActivities.Java 中,作为LocalServiceActivities ...
- Android Service与Activity的交互
Android中有时候需要在Service中改变Activity的UI,或者在Activity中修改Service中的数值.首先必须使用与Activity绑定的Service,有三种方式可以实现.第一 ...
- Android—Service与Activity的交互
service-Android的四大组件之一.人称"后台服务"指其本身的运行并不依赖于用户可视的UI界面 实际开发中我们经常需要service和activity之间可以相互传递数据 ...
- Android service的开启和绑定,以及调用service的方法
界面: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android= ...
- Android:Service的非绑定式的创建和生命周期
Android的Service若使用非绑定式的创建,则创建后将无法再与它取得联系.即无法传递消息參数等: 所以假设希望创建后仍然与其存在联系,那么能够參考我的前几篇博客<Android:Serv ...
- Android Service组件(1)
android service 和其他服务一样,并没有实际运行的界面,它运行在android 后台.一般通过service为应用程序提供服务(比如,从Internet下载文件,控制音乐播放器等).Se ...
- Android - Service启动机制
以下资料摘录整理自老罗的Android之旅博客,是对老罗的博客关于Android底层原理的一个抽象的知识概括总结(如有错误欢迎指出)(侵删):http://blog.csdn.net/luoshe ...
随机推荐
- (Spring加载xml时)org.xml.sax.SAXParseException: cvc-elt.1: Cannot find the declaration of element 'beans'.
ApplicationContext ctx = new ClassPathXmlApplicationContext("test.xml");报错 在启动Spring时,报以下错 ...
- pt-query-digest 安装及使用
打个草稿 介绍:pt-query-digest 可用于mysql的慢查询的日志分析,分析统计出每种慢查询的基本信息,如响应时间.最大执行时间.最小执行时间.执行时间的中位数等.(当然不只是这个功能) ...
- 单点登录CAS使用记(七):关于服务器超时以及客户端超时的分析
我的预想情况 一般情况下,当用户登录一个站点后,如果长时间没有发生任何动作,当用户再次点击时,会被强制登出并且跳转到登录页面, 提醒用户重新登录.现在我已经为站点整合了CAS,并且已经实现了单点登录以 ...
- Eclipse自动生成文档注释
/** *这种格式的注释就是文档注释 */ 快捷键是alt+shift+j,将光标放在类名,变量名,方法名上,按快捷键.
- Ignatius and the Princess I
算法:搜索+优先队列+(递归输出结果) The Princess has been abducted by the BEelzebub feng5166, our hero Ignatius has ...
- 删除所有ecshop版权和logo
前面我们已经讲过如何删除ecshop的版权,但是还有很多人不会,今天就详细的讲下如何删除所有ecshop版权和logo 前台部分: 1:去掉头部TITLE部分的ECSHOP演示站 Powered by ...
- 转载,crtmpserver文件夹结构分析
1. 顶层(crtmpserver/) 下的文件夹结构 3rdparty/ : 对lua, xml进行解析的源代码文件夹 sources/ : 项目的 ...
- 细说PHP优化那些事
我们在用PHP编程的时候,总是想要使自己的程序占用资源最小,运行速度更快,代码量更少.往往我们在追求这些的同时却失去了很多东西.下面我想讲讲我对PHP优化的理解.优化的目的是花最少的代价换来最快的运行 ...
- WINDOW下php开启pgsql拓展
操作步骤: 1.修改php.ini,去掉“extension=php_pgsql.dll ”和“extension=php_pdo_pgsql.dll ”前的分号.2.确认C:\php\ext\下ph ...
- CLR via C#字符串和文本处理
一.字符 在.NET Framewole中,字符总是表示成16位Unicode代码值,这简化了国际化应用程序的开发. 每个字符都表示成System.Char结构(一个值类型) 的一个实例.Sy ...