binder机制是贯穿整个android系统的进程间访问机制,经常被用来访问service,我们结合代码看一下binder在访问service的情形下是怎么具体使用的。
service 你可以理解成没有的界面的activity,它是跑在后台的程序,所谓后台是相对于可以被看得到的程序的,后台程序是不能直接交互的程序。
binder主要是用来进程间通信的,但也可用在和本地service通信。
1. 我们先来看一个与本地service通信的例子。
- package com.ckt.wangxin;
- import android.app.Service;
- import android.content.Intent;
- import android.os.Binder;
- import android.os.IBinder;
- import android.widget.Toast;
- /**
- * This is a service stub for both LocalBinderClient
- * and RemoteBinderClient
- * @author Wang Xin
- * @email springnap@163.com
- *
- */
- public class LocalService extends Service {
- @Override
- public IBinder onBind(Intent intent) {
- return new LocalBinder();
- }
- public void sayHelloWorld(){
- Toast.makeText(this.getApplicationContext(), "Hello World Local Service!", Toast.LENGTH_SHORT).show();
- }
- public class LocalBinder extends Binder {
- LocalService getService() {
- // Return this instance of LocalService so clients can call public methods
- return LocalService.this;
- }
- }
- }
local servcie 的代码如上,在onBinder方法中返回binder,binder包含了service的句柄,客户端得到句柄以后就可以调用servcie的公共方法了,这种调用方式是最常见的。
客户端代码
- package com.ckt.wangxin;
- import android.app.Activity;
- import android.content.ComponentName;
- import android.content.Context;
- import android.content.Intent;
- import android.content.ServiceConnection;
- import android.os.Bundle;
- import android.os.IBinder;
- import android.util.Log;
- import com.ckt.wangxin.LocalService.LocalBinder;
- public class LocalServiceTestActivity extends Activity {
- static final String TAG = "LocalBinderTestActivity";
- ServiceConnection mSc;
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- mSc = new ServiceConnection(){
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- Log.d(TAG, "service connected");
- LocalService ss = ((LocalBinder)service).getService();
- ss.sayHelloWorld();
- }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- Log.d(TAG, "service disconnected");
- }
- };
- }
- @Override
- protected void onStart() {
- super.onStart();
- Log.d(TAG, this.getApplicationContext().getPackageCodePath());
- Intent service = new Intent(this.getApplicationContext(),LocalService.class);
- this.bindService(service, mSc, Context.BIND_AUTO_CREATE);
- }
- @Override
- protected void onStop() {
- super.onStop();
- //must unbind the service otherwise the ServiceConnection will be leaked.
- <span style="color: rgb(255, 0, 0); ">this.unbindService(mSc);</span>
- }
- }
需要注意的是在onStop中要解绑定service, 否则会造成内存泄露的问题。
2. 我们再看一下与另外一个进程中的service进行通信的问题(跨进程通信!)。
如何将servcie运行在另外一个进程呢?在manifest 里面配置个属性就行了。
android:process=":remote" , 代表这个service运行在同一个应用程序的不同进程中。
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.ckt.wangxin"
- android:versionCode="1"
- android:versionName="1.0" >
- <uses-sdk android:minSdkVersion="15" />
- <application
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name" >
- <activity
- android:name=".LocalServiceTestActivity"
- android:label="@string/app_name" >
- <!-- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter> -->
- </activity>
- <service android:name=".LocalService"></service>
- <!-- android:process=":remote" specify this service run in
- another process in the same application. -->
- <service android:name=".RemoteService" android:process=":remote"></service>
- <activity android:name="RemoteServiceTestActivity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
- </manifest>
客户端可以使用Messenger发送消息到service。
客户端代码:
- package com.ckt.wangxin;
- import android.app.Activity;
- import android.content.ComponentName;
- import android.content.Context;
- import android.content.Intent;
- import android.content.ServiceConnection;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.IBinder;
- import android.os.Message;
- import android.os.Messenger;
- import android.os.RemoteException;
- import android.util.Log;
- import android.widget.Toast;
- public class RemoteServiceTestActivity extends Activity {
- static final String TAG = "RemoteServiceTestActivity";
- ServiceConnection mSc;
- public static final int SAY_HELLO_TO_CLIENT = 0;
- /**
- * Handler of incoming messages from service.
- */
- class IncomingHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case SAY_HELLO_TO_CLIENT:
- Toast.makeText(RemoteServiceTestActivity.this.getApplicationContext(), "Hello World Remote Client!",
- Toast.LENGTH_SHORT).show();
- break;
- default:
- super.handleMessage(msg);
- }
- }
- }
- Messenger messenger_reciever = new Messenger(new IncomingHandler());
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- mSc = new ServiceConnection(){
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- Log.d(TAG, "service connected");
- <span style="color: rgb(204, 0, 0); ">Messenger messenger = new Messenger(service);
- Message msg = new Message();
- msg.what = RemoteService.MSG_SAY_HELLO;</span>
- msg.replyTo = messenger_reciever;
- try {
- <span style="color: rgb(255, 0, 0); ">messenger.send(msg);</span>
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- Log.d(TAG, "service disconnected");
- }
- };
- }
- @Override
- protected void onStart() {
- super.onStart();
- Log.d(TAG, this.getApplicationContext().getPackageCodePath());
- Intent service = new Intent(this.getApplicationContext(),RemoteService.class);
- this.bindService(service, mSc, Context.BIND_AUTO_CREATE);
- }
- @Override
- protected void onStop() {
- super.onStop();
- //must unbind the service otherwise the ServiceConnection will be leaked.
- this.unbindService(mSc);
- }
- }
获得service端传来的binder,用来构建一个Messenger向service发送消息。
service端代码:
- package com.ckt.wangxin;
- 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.os.RemoteException;
- import android.widget.Toast;
- public class RemoteService extends Service {
- public static final int MSG_SAY_HELLO = 0;
- @Override
- public IBinder onBind(Intent intent) {
- <span style="color: rgb(204, 0, 0); "> return messager.getBinder();</span>
- }
- Handler IncomingHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- if(msg.replyTo != null){
- Message msg_client = this.obtainMessage();
- msg_client.what = RemoteServiceTestActivity.SAY_HELLO_TO_CLIENT;
- try {
- ((Messenger)msg.replyTo).send(msg_client);
- } catch (RemoteException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- switch (msg.what) {
- case MSG_SAY_HELLO:
- Toast.makeText(RemoteService.this.getApplicationContext(), "Hello World Remote Service!",
- Toast.LENGTH_SHORT).show();
- break;
- default:
- super.handleMessage(msg);
- }
- }
- };
- Messenger messager = new Messenger (IncomingHandler);
- }
之前提及过,启动Service有两种方式:startService 与 bindService。前者已经说过如何使用,所以,这篇贴子主要是关于 bind service的。 这里所讨论的是仅针对那些被绑定的service的,而那些既被startService() 又被 bindService() 的 service 不在此范围内。
① Bind Service就像是C/S架构中的服务端,其他组件(比如 Activity)绑定到它(通过 bindService()),可以向它发送请求,可以接受从它返回的响应,它甚至还提供了进程间通信(IPC)功能。
② 一个service要想能够被其他组件绑定,那么它的 onBind() 方法必须被实现,且必须返回一个 IBinder 对象,然后其他组件可以通过这个 IBinder 对象与该 service 进行通讯。
③ 多个client可以绑定至同一个service,但该 service 的onBind() 方法只会在第一个 client 绑定至其的时候被调用,当其他 client 再次绑定到它的时候,并不会调用 onBind() 方法,而是直接返回第一次被调用时产生的那个 IBinder 对象。也就是说,在其生命周期内,onBind() 只会被调用一次。
④ Bind Service 的生命周期如下图所示:
⑤ Bind Service 不会在后台无限期的一直运行,而是当所有绑定至其的组件都调用了 unbindService() 进行解绑之后,系统就会将其停掉以回收资源。
⑥ 当我们要实现一个 Bind Service 的时候,最重要的就是实现它的 onBind() 方法以返回一个 IBinder 对象
要生成一个 Bound Service ,共有三种方式:继承自 Binder 类,使用 Messenger ,使用 AIDL。下面且听小生一一道来。
第一种:继承自 Binder 类
需要注意的是,这种方式仅仅适用于这种场合:service 与 application 在同一个进程中。这种场合也是最最常见的。
它分以下几个步骤:
a. 在你的 service 类中声明一个内部类来继承 Binder 类。在该内部类中,最好提供一个公共方法来返回你的 service 实例。
b. 在你的 service 类中需要声明一个这个内部类的实例,以供在 onBind() 方法中返回
c. 在 client 端,在 onServiceConnected() 方法中得到从 onBind() 方法中返回的 IBinder 对象,然后可以通过该 对象中的公共方法得到相应的 service 实例,正如 第一个步骤 所说的那样。
d. 在 service 中提供公共方法, 这样就可以在组件(如 Activity 中调用这些公共方法了)
下面给出一例:
service 代码
- public class BindServiceWithIBinder extends Service {
- private static final String TAG = "BindServiceWithIBinder";
- private final MyIBinder myIBinder = new MyIBinder();
- /**
- * bindService() 时,调用的是这个方法,而非 onStartCommnad() 方法
- */
- @Override
- public IBinder onBind(Intent intent) {
- // 在主 Activity 上的 TextView 中打印出一行LOG
- MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(
- MyServiceActivity.UPDATE_VIEW, TAG + " ----> onBind"));
- return myIBinder;
- }
- @Override
- public void onCreate() {
- MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(
- MyServiceActivity.UPDATE_VIEW, TAG + " ----> onCreate"));
- }
- @Override
- public void onDestroy() {
- MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(
- MyServiceActivity.UPDATE_VIEW, TAG + " ----> onDestroy"));
- }
- @Override
- public void onRebind(Intent intent) {
- MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(
- MyServiceActivity.UPDATE_VIEW, TAG + " ----> onRebind"));
- }
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(
- MyServiceActivity.UPDATE_VIEW, TAG + " ----> onStartCommand"));
- return START_STICKY;
- }
- @Override
- public boolean onUnbind(Intent intent) {
- MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(
- MyServiceActivity.UPDATE_VIEW, TAG + " ----> onUnbind"));
- return super.onUnbind(intent);
- }
- /**
- * 声明一个 Binder 类的实现类,供在 onBind() 方法中返回该类的一个实例
- * @author 001718
- *
- */
- public class MyIBinder extends Binder {
- public Service getService() {
- MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(
- MyServiceActivity.UPDATE_VIEW,
- "BindServiceWithIBinder.MyIBinder.getService()"));
- return BindServiceWithIBinder.this;
- }
- }
- /**
- * service 提供的公共方法,在activity中可以调用
- */
- public void download() {
- try {
- MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(
- MyServiceActivity.UPDATE_VIEW, TAG
- + " ----> download(): 文件下载中..."));
- Thread.sleep(3000);
- MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(
- MyServiceActivity.UPDATE_VIEW, TAG
- + " ----> download(): 文件下载完成..."));
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
主 Activity 中的相应关键代码为:
- private void doUnbindService() {
- if (isBound) {
- unbindService(myLocalServiceConnection);
- isBound = false;
- }
- }
- private void doBindService() {
- Log.i("bind", "begin to bind");
- bindService(intent, myLocalServiceConnection, Context.BIND_AUTO_CREATE);
- }
- private ServiceConnection myLocalServiceConnection = new ServiceConnection() {
- public void onServiceConnected(android.content.ComponentName name,
- android.os.IBinder service) {
- MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(
- MyServiceActivity.UPDATE_VIEW,
- "myServiceConnection.onServiceConnected"));
- // 因为 客户端 与 服务 在同一个进程内,这样一来,就可以知道参数 "service"的类型了,也就可以进行显示的强制类型转换了。
- // 而如果 客户端与服务不在同一个进程中的话,那么此处是不可以进行显示强制类型转换的,
- // 因为,通过Debug,可以发现此时传进来的 Service 的类型是 BinderProxy
- MyIBinder myIBinder = (MyIBinder) service;
- bsi = (BindServiceWithIBinder) myIBinder.getService();
- isBound = true;
- bsi.download();
- };
- public void onServiceDisconnected(android.content.ComponentName name) {
- MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(
- MyServiceActivity.UPDATE_VIEW,
- "myServiceConnection.onServiceDisconnected"));
- isBound = false;
- };
- };
下面来看运行效果:
连续点击两次 Bind Service
从此图中可以看出,bind service 的响应过程。也可以看到,第二次点击时,service 没作任何反应,因为当前 Activity 在第一次点击后就已经跟此service绑定了。
点击 Unbind Service
至此,该 service 的生命周期结束,它也会被系统给停掉以回收资源。
引用了如下博文:
http://blog.csdn.net/xinfuqizao/article/details/7742521
http://rainbow702.iteye.com/blog/1144521