Android Service AIDL

1. Service介绍

服务(Service)是Android四大应用组件之一,A Service is an application componentthat can perform long-running operations in the background and does not providea user interface. Service没有实际的界面,而是一直在Android系统的后台运行。Service主要用于两个目的:后台运行和跨进程访问。通过启动一个服务,可以在不显示界面的前提下在后台运行指定的任务,这样可以不影响用户做其他事情。通过AIDL服务可以实现不同进程之间的通信。Service默认是运行在应用的主线程中的,如果需要在Service中做耗时操作,需要在Service中启动线程来处理。

2.   Service生命周期

       Service不能自己运行,需要通过一个Activity或者其他Context对象来调用,Context.startService()和Context.bindService()。

方式一:通过Context.startService()

       Service会经过onCreate()—> onStartCommand(),Context.stopService()的时候或者Service自己StopSelfResult()时,直接onDestroy()。如果是调用者自己直接退出而没有调用stopService(),Service会一直在后台运行,下次调用者启动时可以stopService()。

方式二:通过Context.bindService()

       当Service的调用者bindService时,如果Service没有被创建,Service会先onCreate(),再onBind();多个调用者可以绑定到一个Service中;当调用者执行unBindService方法时,Service就会调用onUnbind–> onDestroy,当然如果还有其他服务绑定了该Service,则不会调用onDestroy方法。所谓的绑定就是Service和调用这共存亡。并且这种方式还可以使服务的调用方调用服务上的其他方法。

        这两种启动方式,Service的onCreate方法都只会被调用一次,Service只被创建一次,当然也只会调用一次onDestroy方法,我们需要在onCreate方法中初始化好Service,在onDestroy方法中完成该Service相关资源的清理,比如停止其子线程,注销监听器等等。

        这两种模式不是完全分离的,一个Service既可以被启动(start),也可以被连接(bind),这时Service的生命周期取决于它被创建的方式。如果是通过Context.startService()创建的则和第一种情况一样;如果是通过Context.bindService()启动,并使用参数Context.BIND_AUTO_CREATE创建的,则情况和第二种一样。

 两种启动方式的生命周期图在

http://developer.android.com/guide/topics/fundamentals/services.html

3. Service使用介绍

Service有两种类型:

本地服务(Local Service):用于应用程序内部

远程服务(Remote Service):用于android系统内部的应用程序之间。

前者用于实现应用程序自己的一些耗时操作,比如查询升级信息,并不占用应用程序比如Activity所属线程,而是单开线程后台执行,这样用户体验比较好。后者可以被其他应用程序服用,比如天气预报服务,其他应用程序不需要在写这样的服务,调用已有的服务即可。

 1、 编写不需要和Activity交互的本地服务实例。

本地服务编写比较简单。首先,创建一个Service类,继承android的Service类,这里写了一个计数服务的类,每秒钟为计数器加一。在服务类的内部,还创建了一个线程,用于实现后台执行上述业务逻辑。

[java]  view plain copy
  1. public class CountService extends Service {  
  2.     private boolean threadDisable;  
  3.     private int count;  
  4.     @Override  
  5.     public IBinder onBind(Intent intent){  
  6.         return null;  
  7.     }  
  8.     @Override  
  9.     public void onCreate() {  
  10.         super.onCreate();  
  11.         new Thread(new Runnable() {  
  12.    
  13.             @Override  
  14.             public void run() {  
  15.                 while (!threadDisable) {  
  16.                     try {  
  17.                         Thread.sleep(1000);  
  18.                     } catch(InterruptedException e) {  
  19.                     }  
  20.                     count++;  
  21.                     Log.v("CountService""Count is " + count);  
  22.                 }  
  23.             }  
  24.         }).start();  
  25.     }  
  26.     @Override  
  27.     public void onDestroy() {  
  28.         super.onDestroy();  
  29.         this.threadDisable = true;  
  30.         Log.v("CountService""on destroy");  
  31.     }  
  32.     public int getCount() {  
  33.         return count;  
  34.     }  
  35. }  

需要将该服务注册到配置文件AndroidManifest.xml中,否则无法找到。

[html]  view plain copy
  1. <service android:name="CountService">  
  2.     <intent-filter>  
  3.         <action android:name="com.channelsoft.service.CountService"/>  
  4.      </intent-filter>  
  5. </service>  
Service属性包括:

<service android:enabled=["true"| "false"]  Service是否激活,为false,Service将不可用

         android:exported=["true" | "false"] 其他的应用程序是否能够调用该Service

         android:icon="drawableresource"   Service运行时的显示图标

            android:label="string resource"     Service展现给用户的显示名称

         android:name="string"          Service的全路径名,或者相对路径名

         android:permission="string"        调用这必须拥有该权限

         android:process="string">         Service运行的进程名称

在Activity或者其他组件中启动Service:

[java]  view plain copy
  1. public class LocalServiceDemoActivity extends Activity {  
  2.     /** Calledwhen the activity is first created. */  
  3.     @Override  
  4.     public void onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.         setContentView(R.layout.main);  
  7.         this.startService("com.channelsoft.service.CountService");  
  8.     }  
  9.     @Override  
  10.     protected void onDestroy() {  
  11.         super.onDestroy();  
  12.         this.stopService(new Intent(this,CountService.class));  
  13.     }  
  14. }  

2、 编写本地Service与Activity交互的实例

上面的示例是通过startService和stopService启动关闭服务的。适用于服务和activity之间没有调用交互的情况。如果之间需要传递参数或者方法调用。需要使用bind和unbind方法。

具体做法是,服务类需要增加接口,比如ICountService,另外,服务类需要有一个内部类,这样可以方便访问外部类的封装数据,这个内部类需要继承Binder类并实现ICountService接口。还有,就是要实现Service的onBind方法,不能只传回一个null了。

这是新建立的接口代码:

[java]  view plain copy
  1. public interface ICountService {  
  2.         public abstract int getCount();  
  3. }  
  4. 修改后的CountService代码:  
  5. public class CountService extends Service implements ICountService{  
  6.     private boolean threadDisable;  
  7.     private int count;  
  8.     private ServiceBinder serviceBinder=new ServiceBinder();  
  9.     public class ServiceBinder extends Binder implements ICountService{  
  10.         @Override  
  11.         public int getCount() {  
  12.             return count;  
  13.         }  
  14.     }  
  15.     @Override  
  16.     public IBinder onBind(Intent intent){  
  17.         return serviceBinder;  
  18.     }  
  19.     @Override  
  20.     public void onCreate() {  
  21.         super.onCreate();  
  22.         new Thread(new Runnable() {  
  23.             @Override  
  24.             public void run() {  
  25.                 while (!threadDisable) {  
  26.                     try {  
  27.                         Thread.sleep(1000);  
  28.                     } catch(InterruptedException e) {  
  29.                     }  
  30.                     count++;  
  31.                     Log.v("CountService""Count is " + count);  
  32.                 }  
  33.             }  
  34.         }).start();  
  35.     }  
  36.     @Override  
  37.     public void onDestroy() {  
  38.         super.onDestroy();  
  39.         this.threadDisable = true;  
  40.         Log.v("CountService""on destroy");  
  41.     }  
  42.     /* (non-Javadoc) 
  43.      * @seecom.easymorse.ICountService#getCount() 
  44.      */  
  45.     public int getCount() {  
  46.         return count;  
  47.     }  
  48. }  

同样需要在manifest.xml文件中注册,注册方式相同。

Acitity代码不再通过startSerivce和stopService启动关闭服务,而是需要通过ServiceConnection的内部类实现来连接Service和Activity。

[java]  view plain copy
  1. public class LocalServiceDemoActivity extends Activity {  
  2. private ServiceConnection serviceConnection = new ServiceConnection() {  
  3.         @Override  
  4. public void onServiceConnected(ComponentName name,IBinder service) {  
  5.             countService = (ICountService)service;  
  6.             Log.v("CountService""on serivce connected, count is "  
  7.                     + countService.getCount());  
  8.         }  
  9.         @Override  
  10.         public voidonServiceDisconnected(ComponentName name) {  
  11.             countService = null;  
  12.         }  
  13.     };  
  14.     private ICountService countService;  
  15.     /** Calledwhen the activity is first created. */  
  16.     @Override  
  17.     public void onCreate(Bundle savedInstanceState) {  
  18.         super.onCreate(savedInstanceState);  
  19.         setContentView(R.layout.main);  
  20. this.bindService(new Intent("com.channelsoft.service.CountService"),  
  21.                 this.serviceConnection, BIND_AUTO_CREATE);  
  22.     }  
  23.     @Override  
  24.     protected void onDestroy() {  
  25.           this.unbindService(serviceConnection);        
  26.           super.onDestroy();       //注意先后  
  27.     }  
  28. }  

3、编写传递基本型数据的远程服务

上面的示例,可以扩展为,让其他应用程序复用该服务。这样的服务叫远程(remote)服务,实际上是进程间通信(RPC)。这时需要使用android接口描述语言(AIDL)来定义远程服务的接口,而不是上述那样简单的java接口。扩展名为aidl而不是java。可用上面的ICountService改动而成ICountSerivde.aidl,eclipse会自动生成相关的java文件。

ICountSerivde.aidl文件内容:

packagecom.channelsoft.service;

interfaceICountService {

        intgetCount();

}

编写服务(Service)类,稍有差别,主要在binder是通过远程获得的,需要通过Stub来获取。Stub对象是远程对象的本地代理,继承IBinder

[java]  view plain copy
  1. public class CountService extends Service {  
  2.     private boolean threadDisable;  
  3.     private int count;  
  4.     private ICountService.Stub serviceBinder = new ICountService.Stub(){  
  5.         @Override  
  6.         public int getCount() throws RemoteException {  
  7.             return count;  
  8.         }  
  9.     };  
  10.     @Override  
  11.     public IBinder onBind(Intentintent) {  
  12.         return serviceBinder;  
  13.     }  
  14.     @Override  
  15.     public void onCreate() {  
  16.         super.onCreate();  
  17.         new Thread(new Runnable() {  
  18.             @Override  
  19.             public void run() {  
  20.                 while (!threadDisable) {  
  21.                     try {  
  22.                         Thread.sleep(1000);  
  23.                     } catch(InterruptedException e) {  
  24.                     }  
  25.                     count++;  
  26.                     Log.v("CountService""Count is " + count);  
  27.                 }  
  28.             }  
  29.         }).start();  
  30.     }  
  31.     @Override  
  32.     public void onDestroy() {  
  33.         super.onDestroy();  
  34.         this.threadDisable = true;  
  35.         Log.v("CountService""on destroy");  
  36.     }  
  37. }  

同样需要在manifest.xml文件中注册,注册方式相同,如果在同一个应用程序中演示该实例,需要给<service>中加入android:process=":remote"  进程名称随便

在Activity中使用服务的差别不大,只需要对ServiceConnection中的调用远程服务的方法时,要捕获异常。

[java]  view plain copy
  1. private ServiceConnectionserviceConnection = new ServiceConnection() {  
  2.     @Override  
  3.     public void onServiceConnected(ComponentName name,IBinder service) {  
  4.         countService = (ICountService) service;  
  5.         try {  
  6.             Log.v("CountService""on serivce connected, count is "  
  7.                     + countService.getCount());  
  8.         } catch (RemoteException e) {  
  9.             throw new RuntimeException(e);  
  10.         }  
  11.     }  
  12.     @Override  
  13.     public void onServiceDisconnected(ComponentName name) {  
  14.         countService = null;  
  15.     }  
  16. };  

这样就可以在同一个应用程序中使用远程服务的方式和自己定义的服务交互了。如果是另外的应用程序使用远程服务,需要做的是复制上面的aidl文件和相应的包构到应用程序中,其他调用等都一样。

4、编写传递复杂数据类型的远程服务

远程服务往往不只是传递java基本数据类型。这时需要注意android的一些限制和规定:

android支持String和CharSequence ;

如果需要在aidl中使用其他aidl接口类型,需要import,即使是在相同包结构下;

android允许传递实现Parcelable接口的类,需要import;

android支持集合接口类型List和Map,但是有一些限制,元素必须是基本型或者上述三种情况,不需要import集合接口类,但是需要对元素涉及到的类型import;

非基本数据类型,也不是String和CharSequence类型的,需要有方向指示,包括in、out和inout,in表示由客户端设置,out表示由服务端设置,inout是两者均可设置。

这里将前面的例子中返回的int数据改为复杂数据类型:

[java]  view plain copy
  1. public class CountBean implements Parcelable {  
  2.     public static final Parcelable.Creator<CountBean>CREATOR = new Creator<CountBean>() {  
  3.         @Override  
  4.         public CountBeancreateFromParcel(Parcel source) {  
  5.             CountBean bean = new CountBean();  
  6.             bean.count = source.readInt();  
  7.             return bean;  
  8.         }  
  9.         @Override  
  10.         public CountBean[] newArray(int size) {  
  11.             return new CountBean[size];  
  12.         }  
  13.     };  
  14.     public int count;  
  15.     @Override  
  16.     public void writeToParcel(Parcel dest, int flags) {  
  17.         dest.writeInt(this.count);  
  18.     }  
  19.     @Override  
  20.     public int describeContents() {  
  21.         return 0;  
  22.     }  
  23. }  

然后,需要在相同包下建一个同名的aidl文件,用于android生成相应的辅助文件:

package com.channelsoft.aidlserver;

parcelable Book;

然后,需要在服务的aidl文件中修改如下:

package com.channelsoft.service;

import com.channelsoft.service.CountBean;

interface ICountService {

        CountBean getCount();

   }

相关文章
相关标签/搜索
每日一句
    每一个你不满意的现在,都有一个你没有努力的曾经。
公众号推荐
   一个历史类的公众号,欢迎关注
一两拨千金