BoundService的基本使用
2015-08-03 13:48
579 查看
导读:
bound服务允许组件(比如activity)对其进行绑定、发送请求、接收响应、甚至进行进程间通信(IPC)。bound服务一般只在为其它应用程序组件服务期间才是存活的,而不会一直在后台保持运行。
bound服务是Service类的一种实现,它允许其它应用程序与其绑定并交互。为了让服务支持绑定,你必须实现onBind()回调方法。这个方法返回一个IBinder对象,此对象定义了客户端与服务进行交互时所需的编程接口。
客户端可以通过调用bindService()方法来绑定服务。在调用时,必须提供一个ServiceConnection的实现码,用于监控与服务的联接。bindService()将会立即返回,没有返回值。但是Android系统在创建客户端与服务之间的联接时,会调用ServiceConnection中的onServiceConnected()方法,传递一个IBinder,客户端将用它与服务进行通信。
多个客户端可以同时联接到一个服务上。不过,只有在第一个客户端绑定时,系统才会调用服务的onBind()方法来获取IBinder。然后,系统会向后续请求绑定的客户端传送这同一个IBinder,而不再调用onBind()。
当最后一个客户端解除绑定后,系统会销毁服务(除非服务同时是通过startService()启动的)。
当你实现自己的bound服务时,最重要的工作就是定义onBind()回调方法所返回的接口。定义服务IBinder接口的方式有好几种。
扩展Binder类如果服务是你的应用程序所私有的,并且与客户端运行于同一个进程中(通常都是如此),你应该通过扩展Binder类来创建你的接口,并从onBind()返回一个它的实例。客户端接收该Binder对象并用它来直接访问Binder甚至Service中可用的公共(public)方法。如果你的服务只是为你自己的应用程序执行一些后台工作,那这就是首选的技术方案。不用这种方式来创建接口的理由只有一个,就是服务要被其它应用程序使用或者要跨多个进程使用。
使用Messenger如果你需要接口跨越多个进程进行工作,可以通过Messenger来为服务创建接口。在这种方式下,服务定义一个响应各类消息对象Message的Handler。此Handler是Messenger与客户端共享同一个IBinder的基础,它使得客户端可以用消息对象Message向服务发送指令。此外,客户端还可以定义自己的Message,以便服务能够往回发送消息。这是执行进程间通信(IPC)最为简便的方式,因为Messenger会把所有的请求放入一个独立进程中的队列,这样你就不一定非要把服务设计为线程安全的模式了。
使用AIDLAndroid接口定义语言AIDL(Android Interface Definition Language)完成以下的所有工作:将对象解析为操作系统可识别的原始形态,并将它们跨进程序列化(marshal)以完成IPC。前一个使用Messenger的方式,实际上也是基于AIDL的,它用AIDL作为底层结构。如上所述,Messenger将在一个单独的进程中创建一个包含了所有客户端请求的队列,这样服务每次就只会收到一个请求。可是,如果想让你的服务能同时处理多个请求,那你就可以直接使用AIDL。这种情况下,你的服务必须拥有多线程处理能力,并且是以线程安全的方式编写的。要直接使用AIDL,你必须创建一个.aidl文件,其中定义了编程的接口。Android SDK 工具使用此文件来生成一个抽象类(abstract class),其中实现了接口及对IPC的处理,然后你就可以在自己的服务中扩展该类。
注意:绝大多数应用程序都不应该用AIDL来创建bound服务,因为这可能需要多线程处理能力并且会让代码变得更为复杂。因此,AIDL对绝大多数应用程序都不适用,并且本文也不会讨论如何在服务中使用它的内容。如果你确信需要直接使用AIDL,那请参阅AIDL文档。
注意:仅当客户端和服务位于同一个应用程序和进程中,这也是最常见的情况,这种方式才会有用。比如,一个音乐应用需要把一个activity绑定到它自己的后台音乐播放服务上,采用这种方式就会很不错。
以下是设置步骤:
在你的服务中,创建一个Binder的实例,其中实现以下三者之一:
包含了可供客户端调用的公共方法
返回当前Service实例,其中包含了可供客户端调用的公共方法。
或者,返回内含服务类的其它类的一个实例,服务中包含了可供客户端调用的公共方法。
从回调方法onBind()中返回Binder的该实例。
在客户端中,在回调方法onServiceConnected()中接收Binder并用所提供的方法对绑定的服务进行调用。
注意:
服务和客户端之所以必须位于同一个应用程序中,是为了让客户端能够正确转换(cast)返回的对象并调用对象的API。服务和客户端也必须位于同一个进程中,因为这种方式不能执行任何跨进程的序列化(marshalling)操作。
在进行具体的代码前,我们可以通过一幅图来了解,boundService的工作顺序:
下面是代码:
Service
package com.boundservice;
import java.util.Random;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
/**
* BoundService的使用:使用IBinder(其他两种是Messenger、AIDL)
* 1.onBind()
* 2.客户端与服务的交互是通过IBinder对象,实现一个接口,在这个接口里做些逻辑操作
* 3.一个服务可以被多个客户端绑定 4.当服务完成任务后会自行调用unBindService来解绑
*
*/
public class TestBoundService extends Service {
//给客户端定义的IBinder
private IBinder mBinder = new LocalBinder();
//生成随机数
private final Random mRandom = new Random();
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return mBinder;
}
/**
* 用于客户端Binder的类。
* 因为知道本服务总是运行于与客户端相同的进程中,我们就不需要用IPC进行处理。
*/
public class LocalBinder extends Binder{
public TestBoundService getService(){
return TestBoundService.this;
}
}
/** method for clients 给客户端定义的方法 public*/
public int getRandomNumber() {
return mRandom.nextInt(100);
}
}
Activity
package com.example.boundservice;
import com.boundservice.TestBoundService;
import com.boundservice.TestBoundService.LocalBinder;
import android.os.Bundle;
import android.os.IBinder;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.view.Menu;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends Activity {
private boolean mBound = false;
TestBoundService mService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
/**
* 这里是在onStart()方法中绑定服务的,你也可以根据自己的需求绑定在onCreate()中等
* bindService中的参数说明:第一个:intent对象;第二个:ServiceConnection对象;第三个:表明只要绑定存在,就自动建立
* Service;同时也告知Android系统,这个Service的重要程度与调用者相同,
* 除非考虑终止调用者,否则不要关闭这个Service
*/
@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(MainActivity.this,TestBoundService.class);
bindService(intent, servinceConnection, Context.BIND_AUTO_CREATE);
}
public void click(View view){
if(mBound){
// 调用服务中的公共方法,但是当该调用使应用程序挂起时,就应该另起线程调用该方法,以免降低Activity的性能
int num = mService.getRandomNumber();
Toast.makeText(MainActivity.this, "获取的随机数字是:--->"+num, Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onStop() {
super.onStop();
if(mBound){
unbindService(servinceConnection);
}
}
private ServiceConnection servinceConnection = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//在这个方法中获取Binder对象,通过Binder对象得到服务类的实例,再将mBound置为true
LocalBinder localBinder = (LocalBinder) service;
mService = localBinder.getService();
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
//当与服务绑定失败时调用的方法
mBound = false;
}
};
}
xml文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="点击与服务进行交互"
android:textSize="20sp"
android:onClick="click"
/>
</RelativeLayout>
最后,不要忘记在Manifest.xml中的<Application>节点中注册Service服务
参考网址:http://www.android-doc.com/guide/components/bound-services.html
当你需要进行IPC时,使用Messenger要比用AIDL实现接口要容易些,因为Messenger会把所有调用服务的请求放入一个队列。而纯粹的AIDL接口会把这些请求同时发送给服务,这样服务就必须要能够多线程运行。
对于绝大多数应用程序而言,服务没有必要多线程运行,因此利用Messenger 可以让服务一次只处理一个调用。如果你的服务非要多线程运行,那你就应该用AIDL来定义接口。
如果你的服务需要与远程进程进行通信,那你可以使用一个Messenger来提供服务的接口。这种技术能让你无需使用AIDL就能进行进程间通信(IPC)。
以下概括了Messenger的使用方法:
服务实现一个Handler ,用于客户端每次调用时接收回调。
此Handler用于创建一个Messenger对象(它是一个对Handler的引用)。
此Messenger对象创建一个IBinder,服务在onBind()中把它返回给客户端。
客户端用IBinder将Messenger(引用服务的Handler)实例化,客户端用它向服务发送消息对象Message。
服务接收Handler中的每个消息Message——确切的说,是在handleMessage()方法中接收。
通过这种方式,客户端不需要调用服务中的“方法”。取而代之的是,客户端发送“消息”(Message对象),服务则接收位于Handler中的这个消息。
以下是服务使用一个Messenger做为接口的简单例子:
public class
MessengerService
extends
Service {
/** 发送给服务的用于显示信息的指令*/
static
final int MSG_SAY_HELLO
= 1;
/**
* 从客户端接收消息的Handler
*/
class
IncomingHandler
extends Handler
{
@Override
public
void handleMessage(Message msg)
{
switch
(msg.what)
{
case MSG_SAY_HELLO:
Toast.makeText(getApplicationContext(),
"hello!",
Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}
/**
* 向客户端公布的用于向IncomingHandler发送信息的Messager
*/
final
Messenger mMessenger
= new
Messenger(new
IncomingHandler());
/**
* 当绑定到服务时,我们向Messager返回接口,
* 用于向服务发送消息
*/
@Override
public
IBinder onBind(Intent intent)
{
Toast.makeText(getApplicationContext(),
"binding",
Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
}
请注意Handler中的handleMessage()方法,这里是服务接收输入消息Message的地方,也是根据what数字来决定要执行什么操作的地方。
客户端要做的全部工作就是根据服务返回的IBinder创建一个Messenger,并用send()方法发送一个消息。例如,以下是一个activity示例,它绑定到上述服务,并向服务发送MSG_SAY_HELLO消息:
public class
ActivityMessenger
extends
Activity {
/** 用于和服务通信的Messenger*/
Messenger mService
= null;
/** 标识我们是否已绑定服务的标志 */
boolean mBound;
/**
* 与服务的主接口进行交互的类
*/
private
ServiceConnection mConnection
= new
ServiceConnection()
{
public
void onServiceConnected(ComponentName className,
IBinder service)
{
// 与服务建立联接后将会调用本方法,
// 给出用于和服务交互的对象。
// 我们将用一个Messenger来与服务进行通信,
// 因此这里我们获取到一个原始IBinder对象的客户端实例。
mService =
new
Messenger(service);
mBound =
true;
}
public
void onServiceDisconnected(ComponentName className)
{
// 当与服务的联接被意外中断时——也就是说服务的进程崩溃了,
// 将会调用本方法。
mService =
null;
mBound =
false;
}
};
public
void sayHello(View v)
{
if
(!mBound)
return;
// 创建并向服务发送一个消息,用到了已约定的'what'值
Message msg
= Message.obtain(null,
MessengerService.MSG_SAY_HELLO,
0,
0);
try
{
mService.send(msg);
}
catch (RemoteException e)
{
e.printStackTrace();
}
}
@Override
protected
void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
protected
void onStart()
{
super.onStart();
// Bind to the service
bindService(new
Intent(this,
MessengerService.class), mConnection,
Context.BIND_AUTO_CREATE);
}
@Override
protected
void onStop()
{
super.onStop();
// Unbind from the service
if
(mBound)
{
unbindService(mConnection);
mBound =
false;
}
}
}
请注意,上述例子中没有给出服务是如何响应客户端的。如果你需要服务进行响应,那你还需要在客户端创建一个Messenger。然后,当客户端接收到onServiceConnected()回调后,它再发送一个消息Message给服务,消息的send()方法中的replyTo参数里包含了客户端的Messenger。
在MessengerService.java(服务)和MessengerServiceActivities.java(客户端)例程中,你可以看到如何双向发送消息的例子。
总结:
绑定是异步进行的。bindService()将立即返回,并不会向客户端返回IBinder。为了接收IBinder,客户端必须创建一个ServiceConnection的实例,并把它传给bindService()。ServiceConnection包含了一个回调方法,系统将会调用该方法来传递客户端所需的那个IBinder。
注意:
只有activity、服务和content provider才可以绑定到服务上——你不能从广播接收器(broadcast receiver)中绑定服务。
因此,要把客户端绑定到服务上,你必须:
实现ServiceConnection。
你的实现代码必须重写两个回调方法:onServiceConnected()
系统调用该方法来传递服务的onBind()方法所返回的IBinder。
onServiceDisconnected()
当与服务的联接发生意外中断时,比如服务崩溃或者被杀死时,Android系统将会调用该方法。客户端解除绑定时,不会调用该方法。
调用bindService(),传入已实现的ServiceConnection。
当系统调用你的onServiceConnected()回调方法时,你可以利用接口中定义的方法开始对服务的调用。
要断开与服务的联接,请调用unbindService()。
当客户端被销毁时,与服务的绑定也将解除。但与服务交互完毕后,或者你的activity进入pause状态时,你都应该确保解除绑定,以便服务能够在用完后及时关闭。(绑定和解除绑定的合适时机将在后续章节中继续讨论。)
例如,以下代码段将客户端与前面#扩展Binder类创建的服务联接,而要做的全部工作就是把返回的IBinder转换(cast)为LocalService类,并获取LocalService的实例:
LocalService mService;
private
ServiceConnection mConnection
= new
ServiceConnection()
{
// 与服务的联接建立之后将会调用
public
void onServiceConnected(ComponentName className,
IBinder service)
{
// 因为我们已经与明显是运行于同一进程中的服务建立了联接,
// 我们就可以把它的IBinder转换为一个实体类并直接访问它。
LocalBinder binder
= (LocalBinder) service;
mService = binder.getService();
mBound =
true;
}
// 与服务的联接意外中断时将会调用
public
void onServiceDisconnected(ComponentName className)
{
Log.e(TAG,
"onServiceDisconnected");
mBound =
false;
}
};
利用这个ServiceConnection,客户端就能够把它传入bindService()完成与服务的绑定。例如:
Intent intent
= new
Intent(this,
LocalService.class);
bindService(intent, mConnection,
Context.BIND_AUTO_CREATE);
bindService()的第一个参数是一个Intent,它明确给出了要绑定的服务名称(注意intent可以是隐式的)。
第二个参数是ServiceConnection对象。
第三个参数是一个指明绑定选项的标志。通常应该是BIND_AUTO_CREATE,表示如果服务未启动的话则创建服务。其它可能的值包括BIND_DEBUG_UNBIND和BIND_NOT_FOREGROUND,或者为0表示不指定。
其它注意事项
以下是有关绑定服务的一些重要注意事项:
你应该确保捕获DeadObjectException异常,当联接中断时会抛出该异常。这是远程方法唯一会抛出的异常。
对象的引用计数是跨进程的。
你通常应该成对地进行绑定和解除绑定,并与客户端生命周期的启动和结束过程相呼应。比如:
如果仅当你的activity可见时才需要与服务进行交互,则你应该在onStart()中进行绑定,并在onStop()中解除绑定。
如果你的activity需要在stopped后并进入后台期间仍然能接收响应,则你可以在onCreate()中进行绑定,并在[1]中解除绑定。请注意这表明你的activity在整个运行期间都需要使用服务(即使在后台),因此假如服务位于其它进程中,则你会增加进程的重量级,进程也会更容易被系统杀死。
注意:你通常不应该在activity的onResume()和onPause()中绑定和解除绑定,因为这两个回调方法在每次切换生命周期状态时都会发生,这时你应该让处理工作最少化。而且,如果应用程序中有多个activity都绑定到同一个服务上,则在两个activity间切换时都会发生状态转换,因为当前activity解除绑定(在pause时)后,紧接着下一个activity又会进行绑定(resume时),所以服务也许在销毁后马上就要重建。(这种activity状态转换、多个activity间的生命周期协作在Activities文档中描述。)
更多展示绑定服务的示例代码,请参阅ApiDemos中的RemoteService.java类。
图 1. started且允许绑定的服务的生命周期
一旦服务被所有客户端解除绑定,则Android系统将会销毁它(除非它同时又是用onStartCommand()started)。因此,如果你的服务就是一个纯粹的bound服务,那你就不需要管理它的生命周期——Android系统会替你管理,根据是否还有客户端对其绑定即可。
不过,如果你选择实现onStartCommand()回调方法,那么你就必须显式地终止服务,因为此服务现在已经被视为started了。这种情况下,无论是否还存在客户端与其绑定,此服务都会运行下去,直至自行用stopSelf()终止或由其它组件调用stopService()来终止。
此外,如果你的服务是started且允许被绑定,那么系统调用你的onUnbind()方法时,你可以选择返回true。这样作的结果就是,下次客户端绑定时将会收到onRebind()调用(而不是收到onBind()调用)。onRebind()返回void,但客户端仍然能在它的onServiceConnected()回调方法中收到IBinder。图1展示了这种生命周期的运行逻辑。
bound服务允许组件(比如activity)对其进行绑定、发送请求、接收响应、甚至进行进程间通信(IPC)。bound服务一般只在为其它应用程序组件服务期间才是存活的,而不会一直在后台保持运行。
bound服务是Service类的一种实现,它允许其它应用程序与其绑定并交互。为了让服务支持绑定,你必须实现onBind()回调方法。这个方法返回一个IBinder对象,此对象定义了客户端与服务进行交互时所需的编程接口。
客户端可以通过调用bindService()方法来绑定服务。在调用时,必须提供一个ServiceConnection的实现码,用于监控与服务的联接。bindService()将会立即返回,没有返回值。但是Android系统在创建客户端与服务之间的联接时,会调用ServiceConnection中的onServiceConnected()方法,传递一个IBinder,客户端将用它与服务进行通信。
多个客户端可以同时联接到一个服务上。不过,只有在第一个客户端绑定时,系统才会调用服务的onBind()方法来获取IBinder。然后,系统会向后续请求绑定的客户端传送这同一个IBinder,而不再调用onBind()。
当最后一个客户端解除绑定后,系统会销毁服务(除非服务同时是通过startService()启动的)。
当你实现自己的bound服务时,最重要的工作就是定义onBind()回调方法所返回的接口。定义服务IBinder接口的方式有好几种。
创建一个Bound服务
创建一个支持绑定的服务时,你必须提供一个IBinder,用作客户端和服务间进行通信的编程接口。定义这类接口的方式有三种:扩展Binder类如果服务是你的应用程序所私有的,并且与客户端运行于同一个进程中(通常都是如此),你应该通过扩展Binder类来创建你的接口,并从onBind()返回一个它的实例。客户端接收该Binder对象并用它来直接访问Binder甚至Service中可用的公共(public)方法。如果你的服务只是为你自己的应用程序执行一些后台工作,那这就是首选的技术方案。不用这种方式来创建接口的理由只有一个,就是服务要被其它应用程序使用或者要跨多个进程使用。
使用Messenger如果你需要接口跨越多个进程进行工作,可以通过Messenger来为服务创建接口。在这种方式下,服务定义一个响应各类消息对象Message的Handler。此Handler是Messenger与客户端共享同一个IBinder的基础,它使得客户端可以用消息对象Message向服务发送指令。此外,客户端还可以定义自己的Message,以便服务能够往回发送消息。这是执行进程间通信(IPC)最为简便的方式,因为Messenger会把所有的请求放入一个独立进程中的队列,这样你就不一定非要把服务设计为线程安全的模式了。
使用AIDLAndroid接口定义语言AIDL(Android Interface Definition Language)完成以下的所有工作:将对象解析为操作系统可识别的原始形态,并将它们跨进程序列化(marshal)以完成IPC。前一个使用Messenger的方式,实际上也是基于AIDL的,它用AIDL作为底层结构。如上所述,Messenger将在一个单独的进程中创建一个包含了所有客户端请求的队列,这样服务每次就只会收到一个请求。可是,如果想让你的服务能同时处理多个请求,那你就可以直接使用AIDL。这种情况下,你的服务必须拥有多线程处理能力,并且是以线程安全的方式编写的。要直接使用AIDL,你必须创建一个.aidl文件,其中定义了编程的接口。Android SDK 工具使用此文件来生成一个抽象类(abstract class),其中实现了接口及对IPC的处理,然后你就可以在自己的服务中扩展该类。
注意:绝大多数应用程序都不应该用AIDL来创建bound服务,因为这可能需要多线程处理能力并且会让代码变得更为复杂。因此,AIDL对绝大多数应用程序都不适用,并且本文也不会讨论如何在服务中使用它的内容。如果你确信需要直接使用AIDL,那请参阅AIDL文档。
扩展Binder类
如果你的服务只用于本地应用程序并且不需要跨进程工作,那你只要实现自己的Binder类即可,这样你的客户端就能直接访问服务中的公共方法了。注意:仅当客户端和服务位于同一个应用程序和进程中,这也是最常见的情况,这种方式才会有用。比如,一个音乐应用需要把一个activity绑定到它自己的后台音乐播放服务上,采用这种方式就会很不错。
以下是设置步骤:
在你的服务中,创建一个Binder的实例,其中实现以下三者之一:
包含了可供客户端调用的公共方法
返回当前Service实例,其中包含了可供客户端调用的公共方法。
或者,返回内含服务类的其它类的一个实例,服务中包含了可供客户端调用的公共方法。
从回调方法onBind()中返回Binder的该实例。
在客户端中,在回调方法onServiceConnected()中接收Binder并用所提供的方法对绑定的服务进行调用。
注意:
服务和客户端之所以必须位于同一个应用程序中,是为了让客户端能够正确转换(cast)返回的对象并调用对象的API。服务和客户端也必须位于同一个进程中,因为这种方式不能执行任何跨进程的序列化(marshalling)操作。
在进行具体的代码前,我们可以通过一幅图来了解,boundService的工作顺序:
下面是代码:
Service
package com.boundservice;
import java.util.Random;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
/**
* BoundService的使用:使用IBinder(其他两种是Messenger、AIDL)
* 1.onBind()
* 2.客户端与服务的交互是通过IBinder对象,实现一个接口,在这个接口里做些逻辑操作
* 3.一个服务可以被多个客户端绑定 4.当服务完成任务后会自行调用unBindService来解绑
*
*/
public class TestBoundService extends Service {
//给客户端定义的IBinder
private IBinder mBinder = new LocalBinder();
//生成随机数
private final Random mRandom = new Random();
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return mBinder;
}
/**
* 用于客户端Binder的类。
* 因为知道本服务总是运行于与客户端相同的进程中,我们就不需要用IPC进行处理。
*/
public class LocalBinder extends Binder{
public TestBoundService getService(){
return TestBoundService.this;
}
}
/** method for clients 给客户端定义的方法 public*/
public int getRandomNumber() {
return mRandom.nextInt(100);
}
}
Activity
package com.example.boundservice;
import com.boundservice.TestBoundService;
import com.boundservice.TestBoundService.LocalBinder;
import android.os.Bundle;
import android.os.IBinder;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.view.Menu;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends Activity {
private boolean mBound = false;
TestBoundService mService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
/**
* 这里是在onStart()方法中绑定服务的,你也可以根据自己的需求绑定在onCreate()中等
* bindService中的参数说明:第一个:intent对象;第二个:ServiceConnection对象;第三个:表明只要绑定存在,就自动建立
* Service;同时也告知Android系统,这个Service的重要程度与调用者相同,
* 除非考虑终止调用者,否则不要关闭这个Service
*/
@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(MainActivity.this,TestBoundService.class);
bindService(intent, servinceConnection, Context.BIND_AUTO_CREATE);
}
public void click(View view){
if(mBound){
// 调用服务中的公共方法,但是当该调用使应用程序挂起时,就应该另起线程调用该方法,以免降低Activity的性能
int num = mService.getRandomNumber();
Toast.makeText(MainActivity.this, "获取的随机数字是:--->"+num, Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onStop() {
super.onStop();
if(mBound){
unbindService(servinceConnection);
}
}
private ServiceConnection servinceConnection = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//在这个方法中获取Binder对象,通过Binder对象得到服务类的实例,再将mBound置为true
LocalBinder localBinder = (LocalBinder) service;
mService = localBinder.getService();
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
//当与服务绑定失败时调用的方法
mBound = false;
}
};
}
xml文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="点击与服务进行交互"
android:textSize="20sp"
android:onClick="click"
/>
</RelativeLayout>
最后,不要忘记在Manifest.xml中的<Application>节点中注册Service服务
参考网址:http://www.android-doc.com/guide/components/bound-services.html
使用Messenger
与AIDL相比当你需要进行IPC时,使用Messenger要比用AIDL实现接口要容易些,因为Messenger会把所有调用服务的请求放入一个队列。而纯粹的AIDL接口会把这些请求同时发送给服务,这样服务就必须要能够多线程运行。
对于绝大多数应用程序而言,服务没有必要多线程运行,因此利用Messenger 可以让服务一次只处理一个调用。如果你的服务非要多线程运行,那你就应该用AIDL来定义接口。
如果你的服务需要与远程进程进行通信,那你可以使用一个Messenger来提供服务的接口。这种技术能让你无需使用AIDL就能进行进程间通信(IPC)。
以下概括了Messenger的使用方法:
服务实现一个Handler ,用于客户端每次调用时接收回调。
此Handler用于创建一个Messenger对象(它是一个对Handler的引用)。
此Messenger对象创建一个IBinder,服务在onBind()中把它返回给客户端。
客户端用IBinder将Messenger(引用服务的Handler)实例化,客户端用它向服务发送消息对象Message。
服务接收Handler中的每个消息Message——确切的说,是在handleMessage()方法中接收。
通过这种方式,客户端不需要调用服务中的“方法”。取而代之的是,客户端发送“消息”(Message对象),服务则接收位于Handler中的这个消息。
以下是服务使用一个Messenger做为接口的简单例子:
public class
MessengerService
extends
Service {
/** 发送给服务的用于显示信息的指令*/
static
final int MSG_SAY_HELLO
= 1;
/**
* 从客户端接收消息的Handler
*/
class
IncomingHandler
extends Handler
{
@Override
public
void handleMessage(Message msg)
{
switch
(msg.what)
{
case MSG_SAY_HELLO:
Toast.makeText(getApplicationContext(),
"hello!",
Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}
/**
* 向客户端公布的用于向IncomingHandler发送信息的Messager
*/
final
Messenger mMessenger
= new
Messenger(new
IncomingHandler());
/**
* 当绑定到服务时,我们向Messager返回接口,
* 用于向服务发送消息
*/
@Override
public
IBinder onBind(Intent intent)
{
Toast.makeText(getApplicationContext(),
"binding",
Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
}
请注意Handler中的handleMessage()方法,这里是服务接收输入消息Message的地方,也是根据what数字来决定要执行什么操作的地方。
客户端要做的全部工作就是根据服务返回的IBinder创建一个Messenger,并用send()方法发送一个消息。例如,以下是一个activity示例,它绑定到上述服务,并向服务发送MSG_SAY_HELLO消息:
public class
ActivityMessenger
extends
Activity {
/** 用于和服务通信的Messenger*/
Messenger mService
= null;
/** 标识我们是否已绑定服务的标志 */
boolean mBound;
/**
* 与服务的主接口进行交互的类
*/
private
ServiceConnection mConnection
= new
ServiceConnection()
{
public
void onServiceConnected(ComponentName className,
IBinder service)
{
// 与服务建立联接后将会调用本方法,
// 给出用于和服务交互的对象。
// 我们将用一个Messenger来与服务进行通信,
// 因此这里我们获取到一个原始IBinder对象的客户端实例。
mService =
new
Messenger(service);
mBound =
true;
}
public
void onServiceDisconnected(ComponentName className)
{
// 当与服务的联接被意外中断时——也就是说服务的进程崩溃了,
// 将会调用本方法。
mService =
null;
mBound =
false;
}
};
public
void sayHello(View v)
{
if
(!mBound)
return;
// 创建并向服务发送一个消息,用到了已约定的'what'值
Message msg
= Message.obtain(null,
MessengerService.MSG_SAY_HELLO,
0,
0);
try
{
mService.send(msg);
}
catch (RemoteException e)
{
e.printStackTrace();
}
}
@Override
protected
void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
protected
void onStart()
{
super.onStart();
// Bind to the service
bindService(new
Intent(this,
MessengerService.class), mConnection,
Context.BIND_AUTO_CREATE);
}
@Override
protected
void onStop()
{
super.onStop();
// Unbind from the service
if
(mBound)
{
unbindService(mConnection);
mBound =
false;
}
}
}
请注意,上述例子中没有给出服务是如何响应客户端的。如果你需要服务进行响应,那你还需要在客户端创建一个Messenger。然后,当客户端接收到onServiceConnected()回调后,它再发送一个消息Message给服务,消息的send()方法中的replyTo参数里包含了客户端的Messenger。
在MessengerService.java(服务)和MessengerServiceActivities.java(客户端)例程中,你可以看到如何双向发送消息的例子。
总结:
绑定一个服务
应用程序组件(客户端)可以通过调用bindService()来绑定服务。然后Android系统会调用服务的onBind()方法,返回一个用于和服务进行交互的IBinder。绑定是异步进行的。bindService()将立即返回,并不会向客户端返回IBinder。为了接收IBinder,客户端必须创建一个ServiceConnection的实例,并把它传给bindService()。ServiceConnection包含了一个回调方法,系统将会调用该方法来传递客户端所需的那个IBinder。
注意:
只有activity、服务和content provider才可以绑定到服务上——你不能从广播接收器(broadcast receiver)中绑定服务。
因此,要把客户端绑定到服务上,你必须:
实现ServiceConnection。
你的实现代码必须重写两个回调方法:onServiceConnected()
系统调用该方法来传递服务的onBind()方法所返回的IBinder。
onServiceDisconnected()
当与服务的联接发生意外中断时,比如服务崩溃或者被杀死时,Android系统将会调用该方法。客户端解除绑定时,不会调用该方法。
调用bindService(),传入已实现的ServiceConnection。
当系统调用你的onServiceConnected()回调方法时,你可以利用接口中定义的方法开始对服务的调用。
要断开与服务的联接,请调用unbindService()。
当客户端被销毁时,与服务的绑定也将解除。但与服务交互完毕后,或者你的activity进入pause状态时,你都应该确保解除绑定,以便服务能够在用完后及时关闭。(绑定和解除绑定的合适时机将在后续章节中继续讨论。)
例如,以下代码段将客户端与前面#扩展Binder类创建的服务联接,而要做的全部工作就是把返回的IBinder转换(cast)为LocalService类,并获取LocalService的实例:
LocalService mService;
private
ServiceConnection mConnection
= new
ServiceConnection()
{
// 与服务的联接建立之后将会调用
public
void onServiceConnected(ComponentName className,
IBinder service)
{
// 因为我们已经与明显是运行于同一进程中的服务建立了联接,
// 我们就可以把它的IBinder转换为一个实体类并直接访问它。
LocalBinder binder
= (LocalBinder) service;
mService = binder.getService();
mBound =
true;
}
// 与服务的联接意外中断时将会调用
public
void onServiceDisconnected(ComponentName className)
{
Log.e(TAG,
"onServiceDisconnected");
mBound =
false;
}
};
利用这个ServiceConnection,客户端就能够把它传入bindService()完成与服务的绑定。例如:
Intent intent
= new
Intent(this,
LocalService.class);
bindService(intent, mConnection,
Context.BIND_AUTO_CREATE);
bindService()的第一个参数是一个Intent,它明确给出了要绑定的服务名称(注意intent可以是隐式的)。
第二个参数是ServiceConnection对象。
第三个参数是一个指明绑定选项的标志。通常应该是BIND_AUTO_CREATE,表示如果服务未启动的话则创建服务。其它可能的值包括BIND_DEBUG_UNBIND和BIND_NOT_FOREGROUND,或者为0表示不指定。
其它注意事项
以下是有关绑定服务的一些重要注意事项:
你应该确保捕获DeadObjectException异常,当联接中断时会抛出该异常。这是远程方法唯一会抛出的异常。
对象的引用计数是跨进程的。
你通常应该成对地进行绑定和解除绑定,并与客户端生命周期的启动和结束过程相呼应。比如:
如果仅当你的activity可见时才需要与服务进行交互,则你应该在onStart()中进行绑定,并在onStop()中解除绑定。
如果你的activity需要在stopped后并进入后台期间仍然能接收响应,则你可以在onCreate()中进行绑定,并在[1]中解除绑定。请注意这表明你的activity在整个运行期间都需要使用服务(即使在后台),因此假如服务位于其它进程中,则你会增加进程的重量级,进程也会更容易被系统杀死。
注意:你通常不应该在activity的onResume()和onPause()中绑定和解除绑定,因为这两个回调方法在每次切换生命周期状态时都会发生,这时你应该让处理工作最少化。而且,如果应用程序中有多个activity都绑定到同一个服务上,则在两个activity间切换时都会发生状态转换,因为当前activity解除绑定(在pause时)后,紧接着下一个activity又会进行绑定(resume时),所以服务也许在销毁后马上就要重建。(这种activity状态转换、多个activity间的生命周期协作在Activities文档中描述。)
更多展示绑定服务的示例代码,请参阅ApiDemos中的RemoteService.java类。
管理Bound服务的生命周期
图 1. started且允许绑定的服务的生命周期
一旦服务被所有客户端解除绑定,则Android系统将会销毁它(除非它同时又是用onStartCommand()started)。因此,如果你的服务就是一个纯粹的bound服务,那你就不需要管理它的生命周期——Android系统会替你管理,根据是否还有客户端对其绑定即可。
不过,如果你选择实现onStartCommand()回调方法,那么你就必须显式地终止服务,因为此服务现在已经被视为started了。这种情况下,无论是否还存在客户端与其绑定,此服务都会运行下去,直至自行用stopSelf()终止或由其它组件调用stopService()来终止。
此外,如果你的服务是started且允许被绑定,那么系统调用你的onUnbind()方法时,你可以选择返回true。这样作的结果就是,下次客户端绑定时将会收到onRebind()调用(而不是收到onBind()调用)。onRebind()返回void,但客户端仍然能在它的onServiceConnected()回调方法中收到IBinder。图1展示了这种生命周期的运行逻辑。
相关文章推荐
- Java 8之方法引用(Method References)
- MVC上传相关
- Web.xml配置中目前需要掌握的知识点
- Nginx详细配置的备注说明
- 海量数据总结
- 微信开发总结 六
- Oracle内存数据库Timesten的使用(2)
- python 使用元类
- iOS关于关于动态函数的操作
- 每天一个linux命令(28):tar命令
- Apache http 发送post请求,解析响应
- 【iOS地图导航】“步行导航”及“HUD导航”的实现,demo+高清源码,大家按需自取~~
- 最近了解了一下ios开发的相关资料,把开源的相关文章进行记录
- Oracle内存数据库Timesten的使用(1)
- StringUtils详细介绍
- DB2 sql中计算出保留两位小数位的查询
- 21. PHP 表单验证 - 完成表单实例
- arcgis 栅格计算器(Spatial Analyst/Raster Calculator)
- jquery手动添加/移除对应的字段校验
- Windows command vs Linux command