Android中bindService基本使⽤⽅法概述
Android中有两种主要⽅式使⽤Service,通过调⽤Context的startService⽅法或调⽤Context的bindService⽅法,本⽂只探讨纯bindService的使⽤,不涉及任何startService⽅法调⽤的情况。如果想了解startService相关的使⽤,请参见。
bindService启动服务的特点
相⽐于⽤startService启动的Service,bindService启动的服务具有如下特点:
1. bindService启动的服务在调⽤者和服务之间是典型的client-server的接⼝,即调⽤者是客户端,service是服务端,service 就⼀个,但是连接绑定到service上⾯的客户端client可以是⼀个或多个。这⾥特别要说明的是,这⾥所提到的client指的是组件,⽐如某个Activity。
2. 客户端client(即调⽤bindService的⼀⽅,⽐如某个Activity)可以通过IBinder接⼝获取Service的实例,从⽽可以实现在client端直接调⽤Service中的⽅法以实现灵活的交互,并且可借助IBinder实现跨进程的client-server的交互,这在纯startService启动的Service中是⽆法实现的。
3. 不同于startService启动的服务默认⽆限期执⾏(可以通过Context的stopService或Service的stopSelf⽅法停⽌运
⾏),bindService启动的服务的⽣命周期与其绑定的client息息相关。当client销毁的时候,client会⾃动与Service解除绑定,当然client也可以通过明确调⽤Context的unbindService⽅法与Service解除绑定。当没有任何client与Service绑定的时
候,Service会⾃⾏销毁(通过startService启动的除外)。
4. startService和bindService⼆者执⾏的回调⽅法不同:startService启动的服务会涉及Service的的onStartCommand回调⽅法,⽽通过bindService启动的服务会涉及Service的onBind、onUnbind等回调⽅法。
bindService代码⽰例
使⽤bindService主要分两种情形:
1. Service的调⽤者client与Service在同⼀个App中;
2. Service的调⽤者client是App1中的⼀个Activity,⽽Service是App2中的Service,client与service分属两个App,这种情形下主要⽤于实现跨进程的通信。
为了简单起见,本⽂只讨论第⼀种情形,即Service的调⽤者client与Service在同⼀个App中,该情形
也是我们在实际开发中⽤到最多的情形。如果想了解通过bindService在两个不同的进程中让客户端与Service通信,可参见另⼀篇博⽂《Android中通过Messenger与Service实现进程间双向通信》。
下⾯我们通过⼀个例⼦演⽰⼀下第⼀种情形下bindService的基本使⽤流程。
⾸先我们有⼀个TestService,该类继承⾃Service,其是client-server接⼝中的server端。我们在其主要的⽣命周期回调⽅法中都加⼊了输出语句。TestService代码如下:
package com.ispring.startservicedemo;
import android.app.Service;
t.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import java.util.Random;
public class TestService extends Service {
public class MyBinder extends Binder{
public TestService getService(){
return TestService.this;
}
}
//通过binder实现调⽤者client与Service之间的通信
private MyBinder binder = new MyBinder();
private final Random generator = new Random();
@Override
public void onCreate() {
Log.i("DemoLog","TestService -> onCreate, Thread: " + Thread.currentThread().getName());
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("DemoLog", "TestService -> onStartCommand, startId: " + startId + ", Thread: " + Thread.currentThread().getName());
return START_NOT_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
Log.i("DemoLog", "TestService -> onBind, Thread: " + Thread.currentThread().getName());
return binder;
}
@Override
public boolean onUnbind(Intent intent) {
Log.i("DemoLog", "TestService -> onUnbind, from:" + StringExtra("from"));
return false;
}
@Override
public void onDestroy() {
Log.i("DemoLog", "TestService -> onDestroy, Thread: " + Thread.currentThread().getName());
}
//getRandomNumber是Service暴露出去供client调⽤的公共⽅法
public int getRandomNumber(){
Int();
}
}
在该App中,除了TestService,还有两个Activity: ActivityA和ActivityB,它们都是Service的调⽤者,即client-server接⼝中的client。
ActivityA是App的启动界⾯,界⾯如下:
ActivityA的代码如下:
package com.ispring.startservicedemo;
import android.app.Activity;
t.ComponentName;
t.Intent;
t.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.widget.Button;
public class ActivityA extends Activity implements Button.OnClickListener {
private TestService service = null;
private boolean isBound = false;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
isBound = true;
TestService.MyBinder myBinder = (TestService.MyBinder)binder;
service = Service();
Log.i("DemoLog", "ActivityA onServiceConnected");
int num = RandomNumber();
Log.i("DemoLog", "ActivityA 中调⽤ TestService的getRandomNumber⽅法, 结果: " + num);
}
@Override
public void onServiceDisconnected(ComponentName name) {
isBound = false;
Log.i("DemoLog", "ActivityA onServiceDisconnected");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_a);
Log.i("DemoLog", "ActivityA -> onCreate, Thread: " + Thread.currentThread().getName());
}
@Override
public void onClick(View v) {
Id() == R.id.btnBindService){
//单击了“bindService”按钮
Intent intent = new Intent(this, TestService.class);
intent.putExtra("from", "ActivityA");
Log.i("DemoLog", "----------------------------------------------------------------------");
Log.i("DemoLog", "ActivityA 执⾏ bindService");
bindService(intent, conn, BIND_AUTO_CREATE);
}else Id() == R.id.btnUnbindService){
//单击了“unbindService”按钮
if(isBound){
Log.i("DemoLog", "----------------------------------------------------------------------");
Log.i("DemoLog", "ActivityA 执⾏ unbindService");
unbindService(conn);
}
}else Id() == R.id.btnStartActivityB){
//单击了“start ActivityB”按钮
Intent intent = new Intent(this, ActivityB.class);
Log.i("DemoLog", "----------------------------------------------------------------------");
Log.i("DemoLog", "ActivityA 启动 ActivityB");
startActivity(intent);
}else Id() == R.id.btnFinish){
//单击了“Finish”按钮
Log.i("DemoLog", "----------------------------------------------------------------------");
Log.i("DemoLog", "ActivityA 执⾏ finish");
this.finish();
}
}
@Override
nextint()方法protected void onDestroy() {
Log.i("DemoLog", "ActivityA -> onDestroy");
}
}
通过单击ActivityA上的“start ActivityB”可以启动ActivityB,ActivityB的界⾯如下:
ActivityB的代码如下:
package com.ispring.startservicedemo;
import android.app.Activity;
t.ComponentName;
t.Intent;
t.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.widget.Button;
public class ActivityB extends Activity implements Button.OnClickListener {
private TestService service = null;
private boolean isBound = false;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
isBound = true;
TestService.MyBinder myBinder = (TestService.MyBinder)binder;
service = Service();
Log.i("DemoLog", "ActivityB onServiceConnected");
int num = RandomNumber();
Log.i("DemoLog", "ActivityB 中调⽤ TestService的getRandomNumber⽅法, 结果: " + num);
}
@Override
public void onServiceDisconnected(ComponentName name) {
isBound = false;
Log.i("DemoLog", "ActivityB onServiceDisconnected");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_b);
}
@Override
public void onClick(View v) {
Id() == R.id.btnBindService){
Intent intent = new Intent(this, TestService.class);
intent.putExtra("from", "ActivityB");
Log.i("DemoLog", "----------------------------------------------------------------------");
Log.i("DemoLog", "ActivityB 执⾏ bindService");
bindService(intent, conn, BIND_AUTO_CREATE);
}else Id() == R.id.btnUnbindService){
if(isBound){
Log.i("DemoLog", "----------------------------------------------------------------------");
Log.i("DemoLog", "ActivityB 执⾏ unbindService");
unbindService(conn);
}
}else Id() == R.id.btnFinish){
//单击了“Finish”按钮
Log.i("DemoLog", "----------------------------------------------------------------------");
Log.i("DemoLog", "ActivityB 执⾏ finish");
this.finish();
}
}
@Override
public void onDestroy(){
Log.i("DemoLog", "ActivityB -> onDestroy");
}
}
我们暂时不点击上⾯的按钮,先看⼀下TestService和ActivityA的代码。
调⽤者(客户端client)要想和Service进⾏交互,那么Service和调⽤者必须都要做好准备。
我们先看Service要做的⼯作。
使⽤bindService将client与server联系在⼀起的关键是binder,在TestService中,我们在其中写了⼀个内部类MyBinder,该类有个公共⽅法getService,通过该⽅法我们可以获取包含MyBinder的TestService。如果想要⾃⼰的Service⽀持bindService启动⽅式,就必须在Service的onBind中返回⼀个IBinder类型的实例。在⽰例中,我们实例化了⼀个MyBinder的实例binder作为TestService的字段,并且将其作为onBind的返回值。
我们总结⼀下如果想让Service⽀持bindService调⽤⽅式,Service需要做以下事情:
1. 在Service的onBind⽅法中返回IBinder类型的实例。
2. onBind⽅法返回的IBinder的实例需要能够返回Service实例本⾝或者通过binder暴露出Service公共⽅法。通常情况下,最简单明了的做法就是将binder弄成Service的内部类,然后在binder中加⼊类似于getService之类的⽅法返回包含binder的Service,这样client可以通过该⽅法得到Service实例。
我们已经知道了Service需要做的事情,我们接下来看⼀下调⽤者需要做的⼯作。
在我们的⽰例中,调⽤者(也就是客户端client)是ActivityA,我们在其中初始化了⼀个ServiceConnection类型的实例,需要重写其onServiceConnected⽅法以及onServiceDisconnected⽅法。我们需要将这个ServiceConnection类型的实例作为参数传给bindService⽅法,当Service还没有创建的时候,Android会先创建Service的实例,然后执⾏Service的onBind⽅法,得到IBinder类型的实例,将该⽅法作为参数传⼊client端的ServiceConnection的onServiceConnected⽅法
中,onServiceConnected⽅法的执⾏表明client端可以获取到Service的IBinder类型的实例,然后将IBinder转换为⾃⼰实际的Binder类型,然后可以通过其直接获取Service的实例或者通过Binder直接执⾏公共⽅法,这取决于Service中Binder的具体实现。在本例中,在onServiceConnected⽅法中,调⽤者ActivityA通过binder的getService⽅法获取到了与其对应的Service,
然后我们就可以直接调⽤Service的公共⽅法以达到使⽤Service的⽬的,这样client与Service之间就通过IBinder建⽴了连接,从⽽进⾏交互。当client与Service失去连接时会触发onServiceDisconnected⽅法。
我们总结⼀下client端要做的事情:
1. 创建ServiceConnection类型的实例,并重写其onServiceConnected⽅法和onServiceDisconnected⽅法。
2. 当Android执⾏onServiceConnected回调⽅法时,我们可以通过IBinder实例得到Service的实例对象或直接调⽤binder的公共⽅法,这样就实现了client与Service的连接。
3. 当Android执⾏onServiceDisconnected回调⽅法时,表⽰client与Service之间断开了连接,我们在此处要写⼀些断开连接后需要做的处理。
在知道了如何让client与Service进⾏交互之后,我们运⾏我们的App,观察各个回调⽅法的执⾏过程,我们有三个测试流程。
测试流程A
该测试涉及到ActivityA,但不涉及ActivityB.
⾸先我们点击ActivityA中的“bindService”按钮,然后点击”unbindService”按钮,输出结果如下所⽰:
⾸先,通过上⾯的代码我们可以看到Service中执⾏的回调⽅法都是执⾏在主线程中的。
当我们调⽤bindService⽅法时,我们需要将Intent、ServiceConnection等实例传⼊,Intent包含了我们要绑定的
Service,ServiceConnection我们在上⾯提到过,实现了其onServiceConnected⽅法和onServiceDisconnected⽅法。在调⽤了bindService之后,由于Service此时还不存在,那么Android就会⾸先创建⼀个TestService的实例,并执⾏其onCreate回调⽅法,onCreate⽅法在其⽣命周期中只会被调⽤⼀次。然后会调⽤Service的onBind⽅法,该⽅法只有在第⼀次bindService 调⽤后才会执⾏,onBind执⾏后会返回⼀个IBinder类型的实例,此时Android会将该IBinder实例存起来,这个IBinder实例是对所有client共享的。当下次其他的client执⾏bindService的时候,不会再执⾏onBind⽅法,因为我们之前已经得到了⼀个IBinder实例,Android会直接使⽤这个IBinder实例。在得到了IBinder实例之后,Android会执⾏client端ServiceConnection中的onServiceConnected⽅法,在该⽅法中我们会得到IBinder实例,并通过该IBinder实例得到了TestService实例,这样我们的客户端ActivityA就通过IBinder与TestService建⽴了连接,我们就可以调⽤TestService的公共⽅法,⽐如调⽤其getRandomNumber⽅法获得随机数。
总结⼀下调⽤bindService之后发⽣的事情:
client 执⾏ bindService ->
如果Service不存在,Service 执⾏ onCreate ->
如果没有执⾏过onBind,Service 执⾏ onBind ->
client的实例ServiceConnection 执⾏ onServiceConnected
在执⾏了bindService之后,⼀共有⼀个client连接到了TestService,即ActivityA,每次client在调⽤了unbindService⽅法之后,该client会与Service解除绑定,在与某个client解除绑定之后,Service会检测是否还有其他的client与其连接绑定,如果没有其他任何client与其处于连接状态,那么Service会执⾏onUnbind⽅法,然后执⾏onDestroy⽅法,最终销毁⾃⼰。当ActivityA执⾏unbindService的时候,唯⼀的⼀个client与TestService解除了绑定的关系,TestService就执⾏了onUnbind⽅法,进⽽执⾏onDestroy⽅法。
总结⼀下调⽤unbindService之后发⽣的事情:
client 执⾏ unbindService ->
client 与 Service 解除绑定连接状态 ->
Service 检测是否还有其他client与其连接,如果没有 ->
Service 执⾏onUnbind ->
Service 执⾏onDestroy
测试流程B
我们在测试完第⼀种流程后,关掉App,重启App,进⾏第⼆种测试流程。
该测试也只涉及ActivityA,不涉及ActivityB。⾸先先点击ActivityA中的“bindService”按钮,然后点击”Finish”按钮,输出结果如下图所⽰:
在该测试中,我们⾸先通过点击”bindService”按钮,使得ActivityA绑定了TestService,但是我们没有调⽤unbindService,⽽是直接通过调⽤“Finish”按钮让ActivityA直接销毁,通过上⾯的输出结果我们可以看到,在ActivityA销毁的时候,执⾏了ActivityA的onDestroy回调⽅法,之后TestService依次执⾏了onUnbind、onDestroy回调⽅法,TestService销毁。client与Service通过bindService连接起来之后,如果client销毁,那么client会⾃动与Service解除绑定,相当于在destroy之前会执⾏unbindService,在ActivityA销毁之后,ActivityA与Service解除了绑定,此时再没有client与Service处于连接绑定状态,这样Service就会执⾏onUnbind回调⽅法,表⽰没有client和我玩了,最后执⾏onDestroy回调⽅法。
测试流程C

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。