Android使用binder访问service的方式

来源:互联网 发布:软件项目质量控制 编辑:程序博客网 时间:2024/05/16 09:15

binder机制是贯穿整个android系统的进程间访问机制,经常被用来访问service,我们结合代码看一下binder在访问service的情形下是怎么具体使用的。


service 你可以理解成没有的界面的activity,它是跑在后台的程序,所谓后台是相对于可以被看得到的程序的,后台程序是不能直接交互的程序。

binder主要是用来进程间通信的,但也可用在和本地service通信。


1. 我们先来看一个与本地service通信的例子。

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. package com.ckt.wangxin;  
  2.   
  3. import android.app.Service;  
  4. import android.content.Intent;  
  5. import android.os.Binder;  
  6. import android.os.IBinder;  
  7. import android.widget.Toast;  
  8. /** 
  9.  * This is a service stub for both LocalBinderClient 
  10.  * and RemoteBinderClient  
  11.  * @author Wang Xin 
  12.  * @email springnap@163.com 
  13.  * 
  14.  */  
  15. public class LocalService extends Service {  
  16.   
  17.     @Override  
  18.     public IBinder onBind(Intent intent) {  
  19.         return new LocalBinder();  
  20.     }     
  21.           
  22.     public void sayHelloWorld(){  
  23.         Toast.makeText(this.getApplicationContext(), "Hello World Local Service!", Toast.LENGTH_SHORT).show();  
  24.     }     
  25.           
  26.     public class LocalBinder extends Binder {  
  27.         LocalService getService() {  
  28.             // Return this instance of LocalService so clients can call public methods  
  29.             return LocalService.this;  
  30.         }     
  31.     }     
  32. }  

local servcie 的代码如上,在onBinder方法中返回binder,binder包含了service的句柄,客户端得到句柄以后就可以调用servcie的公共方法了,这种调用方式是最常见的。


客户端代码

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. package com.ckt.wangxin;  
  2.   
  3. import android.app.Activity;  
  4. import android.content.ComponentName;  
  5. import android.content.Context;  
  6. import android.content.Intent;  
  7. import android.content.ServiceConnection;  
  8. import android.os.Bundle;  
  9. import android.os.IBinder;  
  10. import android.util.Log;  
  11.   
  12. import com.ckt.wangxin.LocalService.LocalBinder;  
  13.   
  14. public class LocalServiceTestActivity extends Activity {  
  15.     static final String TAG = "LocalBinderTestActivity";  
  16.     ServiceConnection mSc;  
  17.       
  18.     /** Called when the activity is first created. */  
  19.     @Override  
  20.     public void onCreate(Bundle savedInstanceState) {  
  21.         super.onCreate(savedInstanceState);  
  22.         setContentView(R.layout.main);  
  23.           
  24.         mSc = new ServiceConnection(){  
  25.             @Override  
  26.             public void onServiceConnected(ComponentName name, IBinder service) {  
  27.                 Log.d(TAG, "service connected");  
  28.                 LocalService ss = ((LocalBinder)service).getService();  
  29.                 ss.sayHelloWorld();  
  30.             }  
  31.   
  32.             @Override  
  33.             public void onServiceDisconnected(ComponentName name) {  
  34.                 Log.d(TAG, "service disconnected");  
  35.             }  
  36.         };  
  37.     }  
  38.       
  39.     @Override  
  40.     protected void onStart() {  
  41.         super.onStart();  
  42.         Log.d(TAG, this.getApplicationContext().getPackageCodePath());  
  43.         Intent service = new Intent(this.getApplicationContext(),LocalService.class);  
  44.         this.bindService(service, mSc, Context.BIND_AUTO_CREATE);  
  45.     }  
  46.   
  47.     @Override  
  48.     protected void onStop() {  
  49.         super.onStop();  
  50.         //must unbind the service otherwise the ServiceConnection will be leaked.  
  51.         <span style="color: rgb(255, 0, 0); ">this.unbindService(mSc);</span>  
  52.     }  
  53. }  

需要注意的是在onStop中要解绑定service, 否则会造成内存泄露的问题。


2. 我们再看一下与另外一个进程中的service进行通信的问题(跨进程通信!)。

如何将servcie运行在另外一个进程呢?在manifest 里面配置个属性就行了。

android:process=":remote" , 代表这个service运行在同一个应用程序的不同进程中。


[html] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     package="com.ckt.wangxin"  
  4.     android:versionCode="1"  
  5.     android:versionName="1.0" >  
  6.   
  7.     <uses-sdk android:minSdkVersion="15" />  
  8.   
  9.     <application  
  10.         android:icon="@drawable/ic_launcher"  
  11.         android:label="@string/app_name" >  
  12.         <activity  
  13.             android:name=".LocalServiceTestActivity"  
  14.             android:label="@string/app_name" >  
  15.            <!--  <intent-filter>  
  16.                 <action android:name="android.intent.action.MAIN" />  
  17.                 <category android:name="android.intent.category.LAUNCHER" />  
  18.             </intent-filter> -->  
  19.         </activity>  
  20.         <service android:name=".LocalService"></service>  
  21.         <!-- android:process=":remote" specify this service run in   
  22.         another process in the same application. -->  
  23.         <service android:name=".RemoteService" android:process=":remote"></service>  
  24.         <activity android:name="RemoteServiceTestActivity">  
  25.             <intent-filter>  
  26.                 <action android:name="android.intent.action.MAIN" />  
  27.                 <category android:name="android.intent.category.LAUNCHER" />  
  28.             </intent-filter>  
  29.               
  30.         </activity>  
  31.     </application>  
  32.   
  33. </manifest>  


客户端可以使用Messenger发送消息到service。

客户端代码:

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. package com.ckt.wangxin;  
  2.   
  3. import android.app.Activity;  
  4. import android.content.ComponentName;  
  5. import android.content.Context;  
  6. import android.content.Intent;  
  7. import android.content.ServiceConnection;  
  8. import android.os.Bundle;  
  9. import android.os.Handler;  
  10. import android.os.IBinder;  
  11. import android.os.Message;  
  12. import android.os.Messenger;  
  13. import android.os.RemoteException;  
  14. import android.util.Log;  
  15. import android.widget.Toast;  
  16.   
  17. public class RemoteServiceTestActivity extends Activity {  
  18.     static final String TAG = "RemoteServiceTestActivity";  
  19.     ServiceConnection mSc;  
  20.     public static final int SAY_HELLO_TO_CLIENT = 0;  
  21.     /** 
  22.      * Handler of incoming messages from service. 
  23.      */  
  24.     class IncomingHandler extends Handler {  
  25.         @Override  
  26.         public void handleMessage(Message msg) {  
  27.             switch (msg.what) {  
  28.                 case SAY_HELLO_TO_CLIENT:  
  29.                     Toast.makeText(RemoteServiceTestActivity.this.getApplicationContext(), "Hello World Remote Client!",  
  30.                             Toast.LENGTH_SHORT).show();  
  31.                     break;  
  32.                 default:  
  33.                     super.handleMessage(msg);  
  34.             }  
  35.         }  
  36.     }  
  37.       
  38.     Messenger messenger_reciever = new Messenger(new IncomingHandler());  
  39.       
  40.     /** Called when the activity is first created. */  
  41.     @Override  
  42.     public void onCreate(Bundle savedInstanceState) {  
  43.         super.onCreate(savedInstanceState);  
  44.         setContentView(R.layout.main);  
  45.           
  46.         mSc = new ServiceConnection(){  
  47.             @Override  
  48.             public void onServiceConnected(ComponentName name, IBinder service) {  
  49.                 Log.d(TAG, "service connected");  
  50.                 <span style="color: rgb(204, 0, 0); ">Messenger messenger = new Messenger(service);  
  51.                 Message msg = new Message();  
  52.                 msg.what = RemoteService.MSG_SAY_HELLO;</span>  
  53.                 msg.replyTo = messenger_reciever;  
  54.                 try {  
  55.                     <span style="color: rgb(255, 0, 0); ">messenger.send(msg);</span>  
  56.                 } catch (RemoteException e) {  
  57.                     e.printStackTrace();  
  58.                 }  
  59.             }  
  60.   
  61.             @Override  
  62.             public void onServiceDisconnected(ComponentName name) {  
  63.                 Log.d(TAG, "service disconnected");  
  64.             }  
  65.         };  
  66.     }  
  67.       
  68.     @Override  
  69.     protected void onStart() {  
  70.         super.onStart();  
  71.         Log.d(TAG, this.getApplicationContext().getPackageCodePath());  
  72.         Intent service = new Intent(this.getApplicationContext(),RemoteService.class);  
  73.         this.bindService(service, mSc, Context.BIND_AUTO_CREATE);  
  74.     }  
  75.   
  76.     @Override  
  77.     protected void onStop() {  
  78.         super.onStop();  
  79.         //must unbind the service otherwise the ServiceConnection will be leaked.  
  80.         this.unbindService(mSc);  
  81.     }  
  82. }  

获得service端传来的binder,用来构建一个Messenger向service发送消息。


service端代码:

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. package com.ckt.wangxin;  
  2.   
  3. import android.app.Service;  
  4. import android.content.Intent;  
  5. import android.os.Handler;  
  6. import android.os.IBinder;  
  7. import android.os.Message;  
  8. import android.os.Messenger;  
  9. import android.os.RemoteException;  
  10. import android.widget.Toast;  
  11.   
  12. public class RemoteService extends Service {  
  13.   
  14.     public static final int MSG_SAY_HELLO = 0;  
  15.   
  16.     @Override  
  17.     public IBinder onBind(Intent intent) {  
  18.       <span style="color: rgb(204, 0, 0); ">  return messager.getBinder();</span>  
  19.     }  
  20.   
  21.     Handler IncomingHandler = new Handler() {  
  22.   
  23.         @Override  
  24.         public void handleMessage(Message msg) {  
  25.             if(msg.replyTo != null){  
  26.                 Message msg_client = this.obtainMessage();  
  27.                 msg_client.what = RemoteServiceTestActivity.SAY_HELLO_TO_CLIENT;  
  28.                 try {  
  29.                     ((Messenger)msg.replyTo).send(msg_client);  
  30.                 } catch (RemoteException e) {  
  31.                     // TODO Auto-generated catch block  
  32.                     e.printStackTrace();  
  33.                 }  
  34.             }  
  35.             switch (msg.what) {  
  36.                 case MSG_SAY_HELLO:  
  37.                     Toast.makeText(RemoteService.this.getApplicationContext(), "Hello World Remote Service!",  
  38.                             Toast.LENGTH_SHORT).show();  
  39.                     break;  
  40.                 default:  
  41.             super.handleMessage(msg);  
  42.             }  
  43.         }  
  44.   
  45.     };  
  46.       
  47.     Messenger  messager = new Messenger (IncomingHandler);  
  48. }  

之前提及过,启动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 代码

Java代码  收藏代码
  1. public class BindServiceWithIBinder extends Service {  
  2.   
  3.     private static final String TAG = "BindServiceWithIBinder";  
  4.   
  5.     private final MyIBinder myIBinder = new MyIBinder();  
  6.   
  7.     /** 
  8.      * bindService() 时,调用的是这个方法,而非 onStartCommnad() 方法 
  9.      */  
  10.     @Override  
  11.     public IBinder onBind(Intent intent) {  
  12.         // 在主 Activity 上的 TextView 中打印出一行LOG  
  13.         MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(  
  14.                 MyServiceActivity.UPDATE_VIEW, TAG + " ----> onBind"));  
  15.         return myIBinder;  
  16.     }  
  17.   
  18.     @Override  
  19.     public void onCreate() {  
  20.         MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(  
  21.                 MyServiceActivity.UPDATE_VIEW, TAG + " ----> onCreate"));  
  22.     }  
  23.   
  24.     @Override  
  25.     public void onDestroy() {  
  26.         MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(  
  27.                 MyServiceActivity.UPDATE_VIEW, TAG + " ----> onDestroy"));  
  28.     }  
  29.   
  30.     @Override  
  31.     public void onRebind(Intent intent) {  
  32.         MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(  
  33.                 MyServiceActivity.UPDATE_VIEW, TAG + " ----> onRebind"));  
  34.     }  
  35.   
  36.     @Override  
  37.     public int onStartCommand(Intent intent, int flags, int startId) {  
  38.         MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(  
  39.                 MyServiceActivity.UPDATE_VIEW, TAG + " ----> onStartCommand"));  
  40.         return START_STICKY;  
  41.     }  
  42.   
  43.     @Override  
  44.     public boolean onUnbind(Intent intent) {  
  45.         MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(  
  46.                 MyServiceActivity.UPDATE_VIEW, TAG + " ----> onUnbind"));  
  47.         return super.onUnbind(intent);  
  48.     }  
  49.   
  50.     /** 
  51.      * 声明一个 Binder 类的实现类,供在 onBind() 方法中返回该类的一个实例 
  52.      * @author 001718 
  53.      * 
  54.      */  
  55.     public class MyIBinder extends Binder {  
  56.         public Service getService() {  
  57.             MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(  
  58.                     MyServiceActivity.UPDATE_VIEW,  
  59.                     "BindServiceWithIBinder.MyIBinder.getService()"));  
  60.             return BindServiceWithIBinder.this;  
  61.         }  
  62.     }  
  63.   
  64.     /** 
  65.      * service 提供的公共方法,在activity中可以调用 
  66.      */  
  67.     public void download() {  
  68.         try {  
  69.             MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(  
  70.                     MyServiceActivity.UPDATE_VIEW, TAG  
  71.                             + " ----> download(): 文件下载中..."));  
  72.             Thread.sleep(3000);  
  73.             MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(  
  74.                     MyServiceActivity.UPDATE_VIEW, TAG  
  75.                             + " ----> download(): 文件下载完成..."));  
  76.         } catch (InterruptedException e) {  
  77.             e.printStackTrace();  
  78.         }  
  79.     }  
  80. }  

 主 Activity 中的相应关键代码为:

Java代码  收藏代码
  1. private void doUnbindService() {  
  2.         if (isBound) {  
  3.             unbindService(myLocalServiceConnection);  
  4.             isBound = false;  
  5.         }  
  6.     }  
  7.   
  8.     private void doBindService() {  
  9.         Log.i("bind""begin to bind");  
  10.         bindService(intent, myLocalServiceConnection, Context.BIND_AUTO_CREATE);  
  11.   
  12.     }  
  13.   
  14.     private ServiceConnection myLocalServiceConnection = new ServiceConnection() {  
  15.         public void onServiceConnected(android.content.ComponentName name,  
  16.                 android.os.IBinder service) {  
  17.             MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(  
  18.                     MyServiceActivity.UPDATE_VIEW,  
  19.                     "myServiceConnection.onServiceConnected"));  
  20.             // 因为 客户端 与 服务 在同一个进程内,这样一来,就可以知道参数 "service"的类型了,也就可以进行显示的强制类型转换了。  
  21.             // 而如果 客户端与服务不在同一个进程中的话,那么此处是不可以进行显示强制类型转换的,  
  22.             // 因为,通过Debug,可以发现此时传进来的 Service 的类型是 BinderProxy  
  23.             MyIBinder myIBinder = (MyIBinder) service;  
  24.             bsi = (BindServiceWithIBinder) myIBinder.getService();  
  25.             isBound = true;  
  26.   
  27.             bsi.download();  
  28.         };  
  29.   
  30.         public void onServiceDisconnected(android.content.ComponentName name) {  
  31.             MyServiceActivity.vh.sendMessage(MyServiceActivity.createMessage(  
  32.                     MyServiceActivity.UPDATE_VIEW,  
  33.                     "myServiceConnection.onServiceDisconnected"));  
  34.             isBound = false;  
  35.         };  
  36.     };  

 

 下面来看运行效果:

连续点击两次 Bind Service



  

从此图中可以看出,bind service 的响应过程。也可以看到,第二次点击时,service 没作任何反应,因为当前 Activity 在第一次点击后就已经跟此service绑定了。

点击 Unbind Service



 至此,该 service 的生命周期结束,它也会被系统给停掉以回收资源。

 


引用了如下博文:

http://blog.csdn.net/xinfuqizao/article/details/7742521

http://rainbow702.iteye.com/blog/1144521


0 0