为了便于对本文的理解,请先阅读上一篇文章《Service详解_StartedService》。从上篇文章可以知道,在同一个APP内,其它组件能启动和停止一个StartedService,但是不能调用Service内的函数,不能获取Service的返回值。如果要实现组件与Service交互,就要使用BoundService。BoundService是Client-Server工作模式中的Server,其它组件通过bindService()绑定到它,建立一个长连接,并用IBinder与Service进行交互。BoundService分为:
1、 Local Bind:组件与Service运行在同一进程中,Service只在应用内使用,不会被跨进程访问。通过Binder创建接口,实现组件与Local Service交互。
2、 Remote Bind:组件与Service运行在不同进程中,有来自其他进程的Client访问Service。使用AIDL定义进程间通信接口,实现组件与Remote Service进程间通信。
本文主要讲解Local Bind,关于Remote Bind请阅读下一篇文章《Service详解_AIDL》。
创建接口
通过扩展Binder类创建接口,用onBind()返回Binder实例,下面是具体步骤:
1、 在Service中创建一个Binder实例,执行以下操作之一:
•包含Client可以调用的public函数。
•返回Service的实例,用于Client调用Service的public函数。
•返回Service中的其它类的对象,用于Client调用Service的public函数。
2、 通过onBind()回调函数返回这个Binder实例。
如下代码展示一个Service用Binder向Client提供交互接口:
public class LocalService extends Service {
//用于返回给Clients的Binder
private final IBinder mBinder = new LocalBinder();
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
//通过扩展Binder创建接口
public class LocalBinder extends Binder {
public LocalService getService() {
//返回LocalServiceA的实例,Clients用于调用LocalServiceA中的public方法
return LocalService.this;
}
}
//Client通过Binder可以调用Service中的这个函数
public int plus(int a, int b) {
return a + b;
}
}
LocalBinder用用getService()向Client提供获取Service实例的方法,Client就能调用Service中的函数(如plus(int a, int b))。
下面代码展示一个Activity绑定到Service,当点击按钮时调用plus(int a, int b)方法。
public class BindLocalServiceActivity extends Activity implements View.OnClickListener {
private static final String TAG = "BindLServiceActivity";
private Context mContext;
private Button bindLocalServiceBtn;
private Button unbindLocalServiceBtn;
private Button plushBtn;
private LocalService mLocalService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bind_local_service);
mContext = this;
bindLocalServiceBtn = (Button) findViewById(R.id.bind_local_service_btn);
unbindLocalServiceBtn = (Button) findViewById(R.id.unbind_local_service_btn);
plushBtn = (Button) findViewById(R.id.plus_btn);
bindLocalServiceBtn.setOnClickListener(this);
unbindLocalServiceBtn.setOnClickListener(this);
plushBtn.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.bind_local_service_btn:
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
break;
case R.id.unbind_local_service_btn:
unbindService(mConnection);
Toast.makeText(mContext, "unbindService", Toast.LENGTH_SHORT).show();
break;
case R.id.plus_btn:
if(mLocalService != null) {
int result = mLocalService.plus(1, 3);
Log.i(TAG, "plus result="+result);
Toast.makeText(mContext, "plus result="+result, Toast.LENGTH_SHORT).show();
}else {
Log.e(TAG, "unbind error");
Toast.makeText(mContext, "unbind error", Toast.LENGTH_SHORT).show();
}
break;
default:
Log.e(TAG, "onClick, unsupport type");
break;
}
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Toast.makeText(mContext, "onServiceConnected", Toast.LENGTH_SHORT).show();
LocalService.LocalBinder binder = (LocalService.LocalBinder)iBinder;
mLocalService = binder.getService();
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
Toast.makeText(mContext, "onServiceDisconnected", Toast.LENGTH_SHORT).show();
}
};
}
绑定Service
应用组件用bindService()绑定到Service,Service的onBind()返回一个IBinder,Client用这个IBinder与Service交互。绑定是异步进行的,bindService()会立刻返回,但在onServiceConnected()中接收到IBinder。Client绑定到Service的步骤如下:
1、 实现ServiceConnection并重写下面两个回调函数:
•onServiceConnected()
Client在这个回调函数中接收到onBind()返回的IBinder。
•onServiceDisconnected()
当与Service的连接意外中断时,如Service crash或被kill,会调用此回调函数。Client从Service解绑时,不调用此函数。
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Toast.makeText(mContext, "onServiceConnected", Toast.LENGTH_SHORT).show();
LocalService.LocalBinder binder = (LocalService.LocalBinder)iBinder;
mLocalService = binder.getService();
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
Toast.makeText(mContext, "onServiceDisconnected", Toast.LENGTH_SHORT).show();
}
};
2、 调用bindService()并传入ServiceConnection实例。
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
3、 当系统调用onServiceConnected()后,Client就可以获取到IBinder。
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Toast.makeText(mContext, "onServiceConnected", Toast.LENGTH_SHORT).show();
LocalService.LocalBinder binder = (LocalService.LocalBinder)iBinder;
mLocalService = binder.getService();
}
通过IBinder调用Service中的函数
int result = mLocalService.plus(1, 3);
4、 Client调用unbindService()与Service解绑。
unbindService(mConnection);
如果组件被销毁时仍然绑定了Service,那么组件销毁后就会和Service解绑。Client最好在与Service交互完后及时解绑,这样可以及时关闭空闲的Service。
BoundService的生命周期
如果一个BoundService没有调用过 onStartCommand(),那么所有Client与它解绑后,系统会销毁它;如果调用过onStartCommand(),那么必须用stopSelf()或stopService()停止Service。如果一个Service既能start又能bind, onUnbind()返回true,那么再次绑定会调用onRebind()。如下图所示:
图1:一个同时支持start和bind的service的生命周期
示例代码
本文档所有示例代码下载路径:http://download.youkuaiyun.com/download/jennyliliyang/10155100