Service与AIDL讲解

来源:互联网 发布:通于心术 知类在穷 编辑:程序博客网 时间:2024/05/16 14:46

转自:http://johnsonxu.iteye.com/blog/1182436


一个Service,从本质上来说,可以有两种形式。 
1、 通过startService()启动。这种情况下,该Service与启动它的组件是完全独立的,即使组件被销毁,Service仍会继续,不会自动结束,而且通常情况下不返回任何结果。因此,应在Service执行完毕后调用stopSelf(int)(最好用带参数的,防止一个Service被多个组件请求的情况下,把还在执行的请求结束了)或其他组件调用stopService关闭Service。 
2、 通过bindService()启动。其生命周期与绑定该Service的组件有关,可以多个组件绑定一个Service,但当所有组件都解绑后,该Service将被销毁。但是当有组件绑定一个Service时,该Service无法以stopService或stopSelf的方式终止。 

默认情况下,任何组件都可以访问Service,但是我们可以在AndroidManifest.xml中将其声明为私有的,从而拒绝其他应用中的组件访问本应用的Service。通过设置intent-filter,可以使得Service能够被隐式调用。 
同时,一个Service默认情况下运行在它的宿主进程的主线程(除非在AndroidManifest.xml中另行设置),因此,若要在一个Service中执行一些复杂的操作,最好在Service的执行代码中新建一个线程,在里面运行。这样能够降低系统出现Application Not Responding (ANR)错误的风险。 
若只想在Activity运行时在后台执行某些操作,可以在Activity的onCreate中新建一个线程,在onStart中启动,在onStop中停止。而不是采用Service的方式。 

创建过程:继承Service类,实现生命周期方法。与Activity不同,Service不需要调用父类方法。 
1、 onStartCommand:当该Service被其他组件以startService的方式调用后,执行该方法。不同的返回值可以设置当系统内存不足时,对该Service的处理方式。START_NOT_STICKY:销毁后不自动创建。START_STICKY:销毁后重新创建Service,并执行onStartCommand,但传入的intent是空的。START_REDELIVER_INTENT:在START_STICKY基础上,传入了最后传入的那个intent。 
2、 onBind:当该Service被其他组件以bindService的方式调用后,执行该方法。绑定后,该Service的生命周期就与绑定它的组件相关联。这种情况下,不用在代码里停止Service。要支持Service绑定,需要返回一个IBinder接口的实例(使用内部类继承Binder类定义并创建,Binder类实现了IBinder接口),在里面定义一些自己需要的方法,以便Service的绑定者与该Service之间的交互。

Java代码  收藏代码
  1. class MyBinder extends Binder{  
  2.     // 比如说,定义一个停止Service的方法  
  3.     public void stopService(){  
  4.         MyService.this.stopSelf();  
  5.     }  
  6.     // 甚至可以定义一个返回该Service对象的方法  
  7.     public MyService getService(){  
  8.         return MyService.this;  
  9.     }  
  10. }  
  11. @Override  
  12. public IBinder onBind(Intent arg0) {  
  13.     Log.d(TAG, "onBind");  
  14.     return new MyBinder();  
  15. }  

 
然后就可以在Service的绑定者(比如说Activity)的代码里对其进行操作

Java代码  收藏代码
  1. MyService myService;  
  2. MyService.MyBinder localBinder;  
  3. // 定义ServiceConnection对象并在Service连接时获取相关对象  
  4. private ServiceConnection mConnection = new ServiceConnection() {  
  5.     @Override  
  6.     public void onServiceConnected(ComponentName className,IBinder binder)  
  7. {  
  8.         localBinder = (MyService.MyBinder) binder;  
  9.         myService = localBinder.getService();  
  10.     }  
  11.     @Override  
  12.     public void onServiceDisconnected(ComponentName arg0) {  
  13.     }  
  14. };  
  15. // 然后进行绑定  
  16. Intent intent2=new Intent();  
  17. intent2.setClass(context, MyService.class);  
  18. context.bindService(intent2, mConnection, Context.BIND_AUTO_CREATE);  

 
3、 onCreate:只在Service第一次被创建时调用,其调用在onStartCommand与onBind之前。 
4、 onDestroy:销毁时调用。 

对于onStartCommand和onBind,可以只实现其中一种。 
不能从BroadcastReceiver中调用bindService。但如果是用registerReceiver的方式注册过的BroadcastReceiver,由于其生命周期与Activity绑定,则可以。 
IntentService是Service的子类,其中采用了一个工作者线程来处理所有的Intent请求(串行);当请求执行完毕后自动调用stopSelf方法;提供返回null的onBind的实现;提供onStartCommand的默认实现,并把请求转发至onHandleIntent方法(需自行实现)。若不需要所有请求同时处理,建议通过继承该类来实现Service。 

前台Service:可以在通知栏中看到的Service。 
当一个Service处于前台模式下时,系统不会在内存不足时销毁它。同时,前台Service需要在通知栏上显示一个图标及相应信息。并支持从通知栏启动相应的Activity。可以在Service的onStartCommand方法中根据传入的intent中的内容的不同,修改状态。

Java代码  收藏代码
  1. // service内执行  
  2. // 此处的title是一个通知生成时,在通知栏显示的提示消息  
  3. Notification notification = new Notification(R.drawable.icon, "title",System.currentTimeMillis());  
  4. // 为了能在通知栏拉下来后,点击通知能够打开Activity,使用了PendingIntent  
  5. Intent notificationIntent = new Intent(this, BroadcastActivity.class);  
  6. PendingIntent pendingIntent = PendingIntent.getActivity(this0, notificationIntent, 0);  
  7. // 此处的title2是把通知栏拉下来后,显示的标题。content即文本内容。  
  8. notification.setLatestEventInfo(this"title2","content", pendingIntent);  
  9. // 设置为前台显示  
  10. startForeground(id, notification);  

 
不想在前台显示时,在Service中执行stopForeground(true);即可。 

只有Activity,Service,ContentProvider能够绑定Service,BroadcastReceiver不可以。 
如果想要在每次绑定时都执行一遍onBind,要在onUnbind中返回true。否则,当下一次有组件绑定该服务时,将运行onRebind,而不是onBind。如下图: 


 

进程间通信 
当要在Service中支持IPC(进程间通信)时,可以使用Messenger(不需要多线程)或AIDL(需要支持多线程访问服务)。 
Messenger: 
1、 在Service中实现一个Handler,在它的handleMessage方法中处理包含不同Message的请求。 
2、 在Service中创建Messenger对象(公共,参数是自定义Handler对象),在onBind时,使用Messenger的getBinder方法返回绑定的IBinder对象。 
3、 调用时,在ServiceConnection类的onServiceConnected方法中,利用IBinder对象创建Messenger对象。然后在想要调用服务的地方使用它的send方法发送Message对象给Service。 
AIDL 
注:对AIDL的访问相当于函数调用,无法保证其运行在哪个线程。 
对AIDL的调用是同步调用,调用完毕函数才返回。如果是比较耗时的工作,注意不要在主线程中调用,防止界面卡死(ANR)。 
实现过程: 
1、 定义AIDL文件,定义完后,Eclipse会自动在gen的相应目录生成一个.java文件。

Java代码  收藏代码
  1. interface MyAidl {  
  2.     int getPid();  
  3.     void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,double aDouble, String aString);  
  4.     void stopService();  
  5. }  

 
2、 在自己实现的Service中,创建一个Stub类(Binder的子类,在.java中自动生成的)对象,给出相应方法的实现。

Java代码  收藏代码
  1. public class AidlBinder extends MyAidl.Stub{  
  2.     @Override  
  3. public void basicTypes(int anInt, long aLong, boolean   
  4. aBoolean,float aFloat, double aDouble, String aString)  
  5. throws RemoteException  
  6. {  
  7.         Log.d(TAG, "basicTypes");  
  8.     }  
  9.     @Override  
  10.     public int getPid() throws RemoteException {  
  11.         Log.d(TAG, "getPid");  
  12.         return 0;  
  13.     }  
  14.     @Override  
  15.     public void stopService() throws RemoteException {  
  16.         MyService.this.stopSelf();  
  17.     }  
  18. }  

 
3、 将Stub对象通过onBind传给调用者

Java代码  收藏代码
  1. @Override  
  2.     public IBinder onBind(Intent arg0) {  
  3.         return myAidlBinder;  
  4.     }  

 
4、 在调用者的代码中,利用asInterface方法获取接口对象(若处于不同应用,调用者应能获取aidl文件,以便自动生成接口类)。

Java代码  收藏代码
  1. MyAidl aidl;  
  2. private ServiceConnection mConnection = new ServiceConnection() {  
  3.     @Override  
  4.     public void onServiceConnected(ComponentName   
  5.     className,IBinder binder) {  
  6.         aidl=MyAidl.Stub.asInterface(binder);  
  7.     }  
  8.     @Override  
  9.     public void onServiceDisconnected(ComponentName arg0) {  
  10.     }  
  11. };  

 

若要在AIDL中使用基本类型、String、CharSequence以外的数据类型,需要使用Parcelable,并指明方向。过程如下: 
1、 定义一个类继承Parcelable,实现writeToParcel,以便将该类写入Parcel中(类似一个输出流) 
2、 添加一个实现了Parcelable.Creator接口的名为CREATOR的静态变量

Java代码  收藏代码
  1. public class MyParcelable implements Parcelable {  
  2.     int num;  
  3.     double n2;  
  4.     String string;  
  5.     public static final Parcelable.Creator<MyParcelable> CREATOR = new Parcelable.Creator<MyParcelable>() {  
  6.         public MyParcelable createFromParcel(Parcel in) {  
  7.             return new MyParcelable(in);  
  8.         }  
  9.         public MyParcelable[] newArray(int size) {  
  10.             return new MyParcelable[size];  
  11.         }  
  12.     };  
  13.     public MyParcelable() {  
  14.     }  
  15.     private MyParcelable(Parcel in) {  
  16.         num = in.readInt();  
  17.         n2 = in.readDouble();  
  18.         string = in.readString();  
  19.     }  
  20.     @Override  
  21.     public int describeContents() {  
  22.         return 0;  
  23.     }  
  24.     @Override  
  25.     public void writeToParcel(Parcel out, int flag) {  
  26.         out.writeInt(num);  
  27.         out.writeDouble(n2);  
  28.         out.writeString(string);  
  29.     }  
  30. }  

 
3、 创建一个MyParcelable.aidl文件,声明该类

Java代码  收藏代码
  1. package com.learn.aidl;  
  2. parcelable MyParcelable;  

 
4、 在使用该类作为参数的aidl文件中import

Java代码  收藏代码
  1. import com.learn.aidl.MyParcelable;  
  2. interface MyAidl {  
  3.     MyParcelable getParcelable();  
  4. void setParcelable(in MyParcelable myParcelable);  
  5. }  

0 0