Bound Service 以绑定方式运行的Service
官方:http://developer.android.com/guide/components/bound-services.html
*Service的两种运行方式:*
独立运行:这种运行方式是指Service被其他组件(如Activity)通过调用startService方法启动。由这种方式启动的Service可以保持一直运行,而与启动它的组件的状态无关,即使启动它的组建已经被销毁。通常这种独立启动的Service只需要执行单一的任务,而不必与启动它的组件联系。比如,某个Service可能用来下载或者上传一个文件到网络,当这个任务完成以后,Service就应该销毁自己。(这里的特点就是保证了即使启动它的组件已经被销毁,也能保证任务得到完整执行)
绑定运行:Service除了独立启动去执行单一任务,它也可以被其他组件绑定,通过bindService方法启动运行。绑定启动的Service和绑定它的客户端将拥有一个接口,实现发起请求、获得结果等任务,Service不仅可以在某一应用内被绑定,它还能实现跨进程绑定(IPC),即其他的应用也可以绑定到这个服务。以绑定启动方式运行的Service的生命周期将与和它绑定的组件生命周期相关联,即“非同年同月同日生,但一定同年同月同日同时死”。 一个Service允许同时被多个组件绑定,当所有与它绑定的组件都被销毁时,这个Service也会被销毁。
这里需要注意的是,Service独立运行和绑定运行两种方式并不是互斥的,我们可以同时调用startService和bindService来使这个Service运行起来。
*基础认识*:
一个以绑定方式运行的Service是Service抽象类的一个具体实现,并且它允许本应用甚至其他应用的组件绑定到它,并与它交互。一个绑定运行的Service必须实现onBind()方法,这个方法会返回一个IBinder对象,其他组件可以通过Ibinde中的接口与Service实现交互。
当我们调用bindService(Intent intent, ServiceConnection serviceConn, int flags)方法绑定到Service时,intent对象指定了具体绑定的Service类,并可以携带一定的数据,这个intent将会传递给IBinder onBind(Intent)方法,serviceConn对象是接口ServiceConnection的一个实现,它定义了在绑定成功时以及异常断开时的回调方法。当bindService方法调用时,它会立即返回,并不等待连接真正建立;当连接成功创建时,系统会调用ServiceConnection的onServiceConnected (ComponentName name, IBinder binder)方法,其中的binder就包含了客户端与Service之间的接口。 这里需要注意的是,系统仅会在第一次绑定到Service,调用IBinder onBind(Intent)方法,后续的绑定将直接由系统将IBinder对象发送给onServiceConnected (ComponentName name, IBinder binder)方法,而不再调用onBind方法。
当所有绑定到Service的客户端与Service解绑时,系统会自动销毁Service。所以不必我们自己去关闭。
创建一个绑定运行的Service的重点就在于IBinder onBind(Intent)返回的IBinder对象的定义,这里有多种方法,后续将分别介绍。
*创建一个绑定运行的Service*:
创建以绑定方式运行的Service可以分为两种应用场景:本地绑定Service、远程绑定Service。
1、本地绑定Service
如果Service只服务于本应用,那么我们只需要继承Binder类,定义我们需要实现的方法即可。具体示例如下:
我们定义这个Service类以及绑定他的客户端类,代码中以注释的形式介绍具体方法:
Service类:
public class LocalService extends Service {
// Binder given to clients
private final IBinder mBinder = new LocalBinder();
// Random number generator
private final Random mGenerator = new Random();
/**
内部类,定义了LocalBinder,它提供了一个方法,就是返回所在的Service对象,客户端获得这个对象就可以调用Service类的公开方法 */
public class LocalBinder extends Binder {
LocalService getService() {
// 返回所在的Service对象
return LocalService.this;
} }
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
/** method for clients */
public string getInfo() {
return "我是LocalService的公开方法,可以通过LocalService对象访问到我";
}
}
客户端类:
public class BindingActivity extends Activity {
LocalService mService;
boolean mBound = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
protected void onStart() {
super.onStart();
// Bind to LocalService
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
public void onButtonClick(View v) {
if (mBound) {
//我们可以通过返回的mService对象调用LocalService的公开方法
int num = mService.getRandomNumber();
Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
}
}
/** 匿名内部类 创建bindService需要的 ServiceConnection 对象*/
private ServiceConnection mConnection = new ServiceConnection() {
//当客户端与Service连接成功时,系统会调用这个方法
@Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// 将service对象向下转型为LocalBinder对象,LocalService的对象有公共方法getService,这个方法可以获得LocalService对象引用
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
*2、远程绑定Service*
使用Messenger
在这种方式中,我们定义一个Handler来处理不同的Message对象。这个Handler是Messenger实现与客户端共享IBinder的基础,它允许客户端通过Message对象向Service发送命令。另外,客户端也可以定义一个Messenger,这样,Service也可以把消息发送给客户端。 这是实现进程间通信的最简单的方式,因为Messenger队列将会在单线程中执行,我们不需要去考虑线程安全。
使用Messenger实现进程间通信的步骤:
- 实现一个Handler,它用来处理传递的Message对象;
- 创建一个Messenger对象,将Handler对象作为构造参数;
- 使用Messenger对象创建一个IBinder对象,并通过onBind返回;
- 客户端将接收到的IBinder对象作为Messenger的构造参数,实例化一个Messenger对象,这个Messenger对象将拥有Handler的引用;
- 在客户端通过Handler发送Message对象,Service中就可以通过Handler的handleMessage处理这个Message。
下面是一个简单的示例:
Service类:
public class MessengerService extends Service {
/** Command to the service to display a message */
static final int MSG_SAY_HELLO = 1;
/**
* Handler of incoming messages from clients.
*/
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);
} }}
/**
* Target we publish for clients to send messages to IncomingHandler.
*/
final Messenger mMessenger = new Messenger(new IncomingHandler());
/**
* When binding to the service, we return an interface to our messenger
* for sending messages to the service.
*/
@Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
}
客户端类:
public class ActivityMessenger extends Activity {
/** Messenger for communicating with the service. */
Messenger mService = null;
/** Flag indicating whether we have called bind on the service. */
boolean mBound;
/**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the object we can use to
// interact with the service. We are communicating with the
// service using a Messenger, so here we get a client-side
// representation of that from the raw IBinder object.
mService = new Messenger(service);
mBound = true;
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
mBound = false;
}
};
public void sayHello(View v) {
if (!mBound) return;
// Create and send a message to the service, using a supported 'what' value
Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
try {//Messenger对象发送消息,这个msg对象将交给Service类中的handleMessage处理
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;
}
}
}
使用AIDL
AIDL (Android Interface Definition Language安卓接口描述语言)。实际上上面介绍的Messenger方式也是基于AIDL实现的,AIDL可以实现对象在进程间的传递。Messenger方式只能一个一个地客户端发送的请求,如果需要同时处理多个请求(多线程),那么可以直接使用AIDL,但是我们必须处理多线程,并且保证线程安全。
官方并不建议使用AIDL,大部分的应用也不必去使用它,AIDL会带来多线程引起的线程安全问题,因此这篇文章中并没对AIDL如何使用做介绍,不过我会另外写文章专门介绍。
*绑定到一个Service:*
应用组件通过bindService绑定到Service.Android系统会调用onBind方法,然后返回一个IBinder给客户端组件,用于和Service交互。
这里的应用组件包括activities, services, and content providers,但不包括broadcast receiver。
需要注意的是,绑定过程是异步的,bindService会在调用时立即返回,当真正绑定成功时,系统会通过ServiceConnection返回IBinder对象。
因此,绑定到Service的过程如下:
- 实现ServiceConnection接口
onServiceConnected()方法:当绑定成功时由系统调用,传入IBinder对象
onServiceDisconnected()方法:当绑定的连接异常关闭时系统会调用这个方法。但是当客户端正常断开连接时,这个方法将不会执行。 - 调用bindService方法,将ServiceConnection传入。
- 当系统调用onServiceConnected方法时,我们就可以获得IBinder对象。这样我们和Service共享了IBinder对象,IBinder所能引用到的Service内的资源,客户端也就能得到了;
当需要取消绑定时,调用unBindService()即可。
当客户端销毁的时候,它会自动取消绑定,但是你仍然应该在不使用Service的时候主动去取消绑定。如果我们需要在Activity可见时,使用这个服务,那么就可以在onStart的时候绑定,而在onStop的时候就取消绑定;如果当Activity进入后台时仍然需要这个服务,那么就在onCreate中绑定,在onDestory中取消绑定。 但是我们不要在onResume中去绑定,在onPause中取消绑定,这样在多个Activity绑定到这个Service时,如果Activity间进行切换,就容易造成冲突。
一张图认识Bound Service生命周期
这里需要注意的就是onRebind方法的执行时机。