现在的位置: 首页 > 综合 > 正文

Service与Android系统实现(1)– 应用程序里的Service Service与Android系统实现(1)– 应用程序里的ServiceService与Android系统设计(2)– ParcelService与Android系统设计(3)– ActivityManager的实现Service与Android系统设计(4)– ServiceManagerService与Android系统设计(5)– libbinderService与Android系统设计(6)— Native ServiceService与Android系统设计(7)— Binder驱动

2013年10月03日 ⁄ 综合 ⁄ 共 19650字 ⁄ 字号 评论关闭

特别声明:本系列文章LiAnLab.org著作权所有,转载请注明出处。作者系LiAnLab.org资深Android技术顾问吴赫老师。本系列文章交流与讨论:@宋宝华Barry

分多次连载,讲述Android Service(JAVA Service、Native Service等)背后的实现原理,透析基于Binder的RPC以及Linux Binder驱动。

Service与Android系统实现(1)-- 应用程序里的Service

Service与Android系统设计(2)-- Parcel

Service与Android系统设计(3)-- ActivityManager的实现

Service与Android系统设计(4)-- ServiceManager

Service与Android系统设计(5)-- libbinder

Service与Android系统设计(6)--- Native Service

Service与Android系统设计(7)--- Binder驱动

Service

Service在Android应用程序里四大实体之一。Android的应用程序不光是需要有图形界面来进行交互,有时也会需要在没有交互的情况下进行的操作,比如下载、更新、监听等。比如目前对我们网络生存影响如此之大的社交网络、或是更老一些聊天工具,总需要这类应用程序可以一直在后台运行,以等待可能过来的消息。:

Service拥有一部分Activity所无法完成的能力。一是后台运行,有时我们并不希望有过多对话框来影响用户体验,开机自动启动,便可默默地在后台运行。另一特性,就是不被Activity生命周期所管理,Activity处于完全活跃的周期是onResume()与onPause()之间,如果这周期之外发生了事件,实际上Activity构成的执行部分也不会被执行到,从而无法响应处理,但Service由于本身过于简单,则会通过一定的辅助手段来达到这个目标。

从Android应用程序的设计原理来看,Service同样也是主线程里执行的(这点尤为重要,Service由于在主线程里执行,于是也会因为执行耗时操作而触发ANR)。一个应用程序既然可以通过拓展Activity得到可交互的代码逻辑,同样也可以通过拓展Service来得到不交互在后台执行的逻辑。如下图加强部分所示:

Activity对应用程序来说是最重要的组件,但从Android系统设计的角度来看,Service对系统层实现来说才最重要的。Service是构建系统的根本,支持整个系统运营的环境framework,本身就是由大量Service来构成的。也就是说,Service反而是构建Activity的基础环境。

Android与其他系统设计最大的不同之处在于,它并不是一种传统的系统环境,而是一种更加开放的系统。传统的图形操作系统里,会有基本环境,会有系统管理组件,应用程序只是作为补充性功能实现。但Android并不如此,Android系统是没有应用程序的,达到了“无边界”系统设计的最高境界,“手里无剑,心中有剑”。整个Android系统的设计使自己扮演着支撑系统的运行环境的角色,不再有基本系统的概念,而变成了一种“有或者无”的应用程序的支撑环境,没有系统组件的概念。而我们所谓的系统应用程序,我们只能称它们为“内置”应用程序,只是服务于黑心移动运营商的一种方式而已。

这种设计的精髓在于,系统本身不会处理交互,而只是提供交互的手段。从前面我们对于应用程序运行环境的分析中,我们可以看到,Android的Framework,提供一部分功能供应用程序调用,而除了这些应用程序直接使用的API实现,其他代码逻辑就会全是由Service构成。当然作为系统实现角度的Service,与应用程序编程里实现的Service是有差别的,更强调共享,但基本构架一样。在过渡到Android系统的解析之前,我们先从应用程序的Service概念入手。

本地简单Service

我们先来在应用程序里写一个简单的Service。打开Eclipse,新建一个Android工程,然后再创建一个新的基于Service基类的类。与Activity的编程方式类似,Service在编程上也是基于回调方式实现的,我们继承基类Service之后所需要做的,就是通过IoC模式替换原来的Service回调的实现:

  1. import android.app.Service;  
  2. import android.content.Intent;  
  3. import android.os.IBinder;  
  4. import android.util.Log;  
  5. public class LianlabServiceextends Service  
  6. {  
  7.     private staticfinal String TAG ="LianlabService";  
  8.    @Override  
  9.     public void onCreate() {  
  10.        super.onCreate();  
  11.        Log.v(TAG, "inonCreate()");  
  12.     }  
  13.    @Override  
  14.     public int onStartCommand(Intent intent,int flags,int startId) {  
  15.        super.onStartCommand(intent, flags, startId);  
  16.        Log.v(TAG, "inonStartCommand()");  
  17.        return START_STICKY;  
  18.     }  
  19.    @Override  
  20.     public void onDestroy()  
  21.     {  
  22.        Log.v(TAG, "inonDestroy().");  
  23.        super.onDestroy();  
  24.     }  
  25. }  

有了Service的具体实现之后,系统并不会自动地识别到这一实现,在Android世界里,一切都通过AndroidManifest.xml来驱动,于是,我们还需要修改AndroidManifest.xml文件,加入Service的定义:

  1. <applicationandroid:labelapplicationandroid:label="@string/app_name">  
  2.   
  3.    <serviceandroid:nameserviceandroid:name=".LianLabService"/>  
  4. ;/application>   

在上面这种方式里实现的Service,可被执行的方式很有限,就是提供一个可执行的线程环境,可以被Intent所驱动,执行onStartCommand()回调。功能有限并不代表无能,在Android系统里,我们可能还经常会碰到这样的需求:比如我们使用GPS里来记录我们行动轨迹时,这时我们很可能需要通过后台的执行的代码来定时检查GPS的定位信息;杀毒或是监控软件可能希望驻留在后台,并可被Intent来驱动开始进行杀毒;我们的聊天或是社交应用,需要在后台定时地与服务发送“心跳”(Heart beat),用来标识自己的在线状态等。这样的例子,大家可以回头到我们画的GPS轨迹跟踪的构成示意图,这样的跟踪软件,必须是通过一个接收启动完成信息的Broadcast
Receiver来监听自己是否应该被执行,而接收到到启动完成的Broadcast Intent之后,则必须触发一直在后台运行的TrackerService的执行。

既然我们在上述方式里实现的Service是由Intent驱动的,于是我们的使用这一Service部分的代码也会很简单。在任何可被执行到的代码里使用startService(Intent)就可以完成,我们可以给某个控件注册点击事件支持onClickListener对象,然后覆盖onClick()回调方法:

  1.  public void onClick(Viewv) {  
  2.       Intent intent = new Intent(this,  
  3.       LianlabService.class);  
  4.     startService(intent);  
  5. }    

我们这里既然使用到了Intent,也就是说我们还可以通过extras这个Bundle对象给我们这里实现的LianLabService来传递运行的参数。于是,这时我们的代码貌似有了pthread多线程执行效果,通过传参,然后我们会执行一个在另一线程里运行的函数,只是函数是固定的onStartCommand()回调方法。但这只是貌似,并非实际情况,Service的执行与后台线程方式极大不同,Service只是一种代码逻辑的抽象,实际上它还是运行在Activity同一线程上下文环境。

于是,我们并不能用Service来进行任何耗时操作,否则会阻塞主线程而造成应用程序的无法响应错误,也就是臭名昭著的ANR错误。Service仅能用于不需要界面交互的代码逻辑。

本地 Bounded Service

这种使用Intent来驱动执行的Service,可用性有限,并不能完全满足我们对于后台服务的需求。对于后台执行的代码,我们更多的应用情境不光是希望进行后台操作,我们可能还希望能进行交互,可以随时检查后台操作的结果,并能暂停或是重启后台执行的服务,可以在使用某一Service时保证它并不会退出执行,甚至一些提交一些参数到后台来进行复杂的处理。这时,我们可以使用Service的另一个访问方式,使用Binder接口来访问。我们的Service基类还提供这类应用的回调方式,onBind()、onUnbind()和onRebind()。使用Binder来访问Service的方式比Intent驱动的应用情境更底层,onBind()回调主要用于返回一个IBinder对象,而这一IBinder对象是Service的引用,将会被调用端用于直接调用这一Service里实现的某些方法。

同样的Service实现,如果通过IBinder来驱动,则会变成下面的样子:

  1. import android.app.Service;  
  2. import android.content.Intent;  
  3. import android.os.IBinder;  
  4. import android.util.Log;  
  5. public class LianlabServiceextends Service  
  6. {  
  7.     private staticfinal String TAG ="LianlabService";  
  8.    
  9.    @Override  
  10.     public void onCreate() {  
  11.        super.onCreate();  
  12.        Log.v(TAG, "inonCreate()");  
  13.     }  
  14.    @Override  
  15.     public intonStartCommand(Intent intent,int flags,int startId) {  
  16.        super.onStartCommand(intent, flags, startId);  
  17.        Log.v(TAG, "in onStartCommand()");  
  18.        return START_STICKY;  
  19.     }  
  20.    @Override  
  21.     public void onDestroy()  
  22.     {  
  23.        Log.v(TAG, "inonDestroy().");  
  24.       super.onDestroy();  
  25. }  
  26.    finalIService.Stub m_binder =newIService.Stub() {  
  27.         ...  
  28.    }  
  29.    @Override  
  30.     public IBinderonBind(Intent intent) {  
  31.        Log.v(TAG, "inonBind().");  
  32.        return mBinder;  
  33.     }  
  34.    @Override  
  35.     public booleanonUnbind(Intent intent) {  
  36.        Log.v(TAG, "inonUnbind().");        
  37.        return mAllowRebind;  
  38.     }  
  39.    @Override  
  40.     public void onRebind(Intentintent) {  
  41.        Log.v(TAG, "inonRebind().");  
  42.     }  
  43. }  

使用IBinder对象来触发的Service,在访问时的代码实现则变得完全不样了。比如我们同样通过onClick()来操作后台的某些操作,但这时并非通过Intent来完成,而是直接使用某个引用这一Service的IBinder对象来直接调用Service里实现的方法。

  1. bindService(intent, m_connection, …);  
  2. private ServiceConnection m_connection =new ServiceConnection() {  
  3.     private IService onServiceConnected(…, IBinder service) {  
  4.          m_service =IService.Stub.asInterface(service);  
  5.      }  
  6. }  

如果Service里实现了某些方法,比如kill(),在上述代码之后,我们对Service的驱动则会变成代码上的直接调用。在onServiceConnected()回调方法被触发之后,我们始终都可以通过m_service.kill()来访问Service里的kill()方法。而bindService()这方法的调用,则会触发onServiceConnected()事件。

       这样就要让人抓狂了,既然如此麻烦,何不直接调用呢?所以,事实上,这里列举的这种代码实现方式,在现实编程里确实不常用。一般而言,如果Service通过IBinder对象来触发,那只会出于一个理由,提供一种可能性,将来可以更灵活地提供给另一进程来访问,这就是我们稍后会说明的Remote Service。

这两种不同的Service的实现方式,将决定Service的不同被调用方式,或者准确地说,将决定Service的不同生命周期。

如图所示,Service的进程存活期是处理onCreate()与onDestroy()回调方法之间。onStartCommand()回调是不受控的,每次Intent都将触发执行一次, onStartCommand()执行完则会退出;而使用onBind()来驱动的Service,其活跃区间是onBind()与onUnbind()之间,其活跃周期始终在控制范围内。

Remote Service

得益于Android的进程间模型,无论是系统实现端的开发,还是应用程序的开发者,可认为自己的代码都将在一种安全的环境下执行。但对于在需要共享的场合,又带来了不方便之处,我们不再可以在一个进程里很方便地调用到另一进程里的实现。在上面两种Service实现里,基本上只能自已服务自己,而不能将功能共享给更多地使用者。于是,Android在设计初期,便引入了另一个概念,Remote Service。

Remote Service定义了如何从一个进程,直接访问到另一个进程里实现的方法,这样的机制在通用编程领域被称为RPC,Remote Procedure Call,在Android环境里,因为这样的RPC与Service的概念揉合到了一起,所以被称为Remote Service。

如下图所示,Remote Service基本上算是在Bounded Service实现上的一种拓展。调用端(一般是与用户交互的Activity)会通过bindService()发一个Intent到Service进程,而Service进程会在onBind()回调方法里返回自己的Service处理接口。当调用端得到相应的处理接口,也就是下图所示的Stub.Proxy对象之后,就可以调用远端Stub对象里实现的方法。

当然,由于这种方式需要在每一次调用时都进行一次远程调用,于是实现起来并不简单,甚至可以说是会很麻烦。在上图逻辑里要实现一个Remote Service,在这一Remote Service里实现一个方法,就需要加入多个对象。于是,在Android里引入了AIDL,可以通过AIDL来简化通用代码的处理,在上图中Stub对象、Stub.Proxy对象都将由aidl自动管理。

实现一个Remote Service,基本上可以分三步完成:

  1.  定义aidl
  2.  实现被调用的Remote Service(提供可实例化的Stub对象)
  3.  调用Remote Service

定义AIDL

引入了AIDL文件之后,在编码时就会更加简洁,比如加入一个接口类ITaskService,而在ITaskService里提供一个getPid()方法:
  1. packageorg.lianlab.service;  
  2. interface ITaskService {  
  3.     int getPid ( );  
  4. }  

实现Remote Service

有了AIDL定义之后,我们便可实现这这一接口类的定义,主要是提供一个Stub类的实现,在这个Stub对象里,提供getPid(void)的具体实现。

  1. private finalITaskService.Stub mTaskServiceBinder =new ITaskService.Stub() {  
  2.    public int getPid() {  
  3.        return Process.myPid();  
  4.    }  
  5. };  

于是,当应用程序通过某种方式可以取得ITaskServiceStub所对应的IBinder对象之后,就可以在自己的进程里调用IBinder对象的getPid(),但这一方法实际上会是在别一个进程里执行。虽然我们对于执行IBinder的Stub端代码的执行环境并没有严格要求,可以在一个运行中的进程或是线程里创建一个Stub对象就可以了,但出于通用性设计的角度考虑,实际上我们会使用Service类来承载这个Stub对象,通过这样的方式,Stub的运行周期便被Service的生存周期所管理起来。这样实现的原因,我们可以回顾我们前面所描述的功耗控制部分。当然,使用Service带来的另一个好处是代码的通用性更好,我们在后面将感受到这种统一化设计的好处。

于是,对应于.aidl的具体实现,我们一般会使用一个Service对象把这个Stub对象的存活周期“包”起来。我们可以通过在Service里使用一个 final类型的Stub对象,也可以通过在onBind()接口里进行创建。这两种不同实现的结果是,使final类型的Stub对象,类似于我们的Singleton设计模式,客户端访问过来的总会是由同一个Stub对象来处理;而在onBind()接口里创建新的Stub,可以让我们对每个客户端的访问都新建一个Stub对象。出于简化设计的考虑,我们一般会使用final的唯一Stub对象,于是我们得到的完整的Service实现如下:

  1. package org.lianlab.services;  
  2. import android.app.Service;  
  3. import android.content.Intent;  
  4. import android.os.IBinder;  
  5. import android.os.Process;  
  6. public class TaskService extends Service {1  
  7.     @Override  
  8.     public IBinderonBind(Intent intent) {  
  9.        if (ITaskService.class.getName().equals(intent.getAction())) {2  
  10.            return mTaskServiceBinder;  
  11.        }  
  12.         return null;  
  13.     }  
  14.    
  15.     private finalITaskService.Stub mTaskServiceBinder =new ITaskService.Stub() {  3  
  16.        public int getPid() { 4  
  17.            return Process.myPid();  
  18.        }  
  19.     };  
  20. }  

  1. 由于实现上的灵活性,我们一般使用Service来承载一个AIDL的Stub对象,于是这个Stub的存活周期,会由Service的编写方式决定。当我们的Stub对象是在onBind()里返回时,Stub对象的存活周期是Service处于Bounded的周期内;如果使用final限定,则Stub对象的存活周期是Service在onCreate()到onDestroy()之间
  2. 用于处理bindService()发出Intent请求时的Action匹配,这一行一般会在AndroidManifest.xml文件里对Service对Intent filter设置时使用。我们这种写法,则使这一Service只会对Intent里Action属性是“org.lianlab.services.ITaskService”的bindService()请求作出响应。这一部分我们在后面的使用这一Service的Activity里可以看到
  3. 创建ITaskService.Stub对象,并实现Stub对象所要求的方法,也就是AIDL的实现。Stub对象可以像我们这样静态创建,也可以在onBind()里动态创建,但必须保证创建在onBind()返回之前完成。在onBind()回调之后,实际上在客户端则已经发生了onServiceConnected()回调,会造成执行出错。
  4. 实现,这时我们最终给客户端提供的远程调用,就可以在Stub对象里实现。我们可以实现超出AIDL定义的部分,但只有AIDL里定义过的方法才会通过Binder暴露出来,而AIDL里定义的接口方法,则必须完整实现。

       有了这样的定义之后,我们还需要在AndroidManifest.xml文件里将Service声明出来,让系统里其他部分可以调用到:

  1. <application  
  2.       …  
  3.        <service android:name=".TaskService">  
  4.            <intent-filter>  
  5.                 <action android:name="org.lianlab.services.ITaskService"/>  
  6.           </intent-filter>  
  7.        </service>  
  8. </application>  

在AndroidManifest.xml文件里,会在<application>标签里通过<service>标签来申明这一应用程序存在某个Service实现,在service命名里,如果service的名称与application名称的包名不符,我们还可以使用完整的“包名+类名”这样命名方式来向系统里注册特殊的Service。在<service>标签里,我们也可以注册<intent-filter>来标明自己仅接收某一类的Intent请求,比如我们例子里的action会匹配“org.lianlab.services.ITaskService”,如果没有这样的intent-filter,则任何以TaskService(ComponentName的值是”org.lianlab.services.ITaskService”)为目标的Intent请求会触发TaskService的onBind()回调。当然,我们在<service>标签内还可以定义一些权限,像我们例子里的这个TaskService是没有任何权限限制的。有了这个AndroidManifest.xml文件,我们再来看看客户端的写法。

访问Remote Service

在客户端进行访问时,由于它必须也使用同一接口类的定义,于是我们可以直接将同一.aidl文件拷贝到客户端应用程序的源代码里,让这些接口类的定义在客户端代码里也可以被自动生成,然后客户端便可以自动得到Proxy端的代码。因为我们这里使用了Service,又是通过onBind()返回IBinder的对象引用,这时客户端在使用IBinder之前,需要通过bindService()来触发Service端的onBind()回调事件,这时会通过客户端的onServiceConnected()回调将Stub所对应的Binder对象返回。我们在稍后看AIDL的底层实现时会发现,在此时客户端的Binder对象只是底层Binder
IPC的引用,此时我们还需要创建一个基于这一Stub接口的Proxy,于是在客户端会需要调用asInterface()创建Proxy对象,这一Proxy对象被转义成具体的Service,在我们的例子里,客户端此时就得到了ITaskService对象。从这时开始,在客户端里通过ITaskService.getPid()的调用,都会通过Binder IPC将操作请求发送到Service端的Stub实现。于是,我们可以得到客户端的代码,在Android里我们一般用Activity来完成这样的操作,如下代码所示:

  1. package org.lianlab.hello;  
  2. import android.os.Bundle;  
  3. import android.os.IBinder;  
  4. import android.os.RemoteException;  
  5. import android.app.Activity;  
  6. import android.content.ComponentName;  
  7. import android.content.Context;  
  8. import android.content.Intent;  
  9. import android.content.ServiceConnection;  
  10. import android.util.Log;  
  11. import android.view.View;  
  12. import android.view.View.OnClickListener;  
  13.    
  14. import android.widget.TextView;  
  15.    
  16. import org.lianlab.services.ITaskService;1  
  17. import org.lianlab.hello.R;  
  18.    
  19. public class Helloworld extends Activity  
  20. {  
  21.     /** Called when the activity is first created.*/  
  22.    
  23.     ITaskService mTaskService = null2  
  24.    
  25.     @Override  
  26.     public voidonCreate(Bundle savedInstanceState)  
  27.     {  
  28.        super.onCreate(savedInstanceState);  
  29.        setContentView(R.layout.main);  
  30.    
  31.            bindService(new Intent(ITaskService.class.getName()),mTaskConnection,  
  32.                     Context.BIND_AUTO_CREATE);   3  
  33.          
  34.        ((TextView) findViewById(R.id.textView1)).setOnClickListener(  
  35.                 new OnClickListener() {  
  36.                    @Override  
  37.                    public void onClick(Viewv) {  
  38.                        if (mTaskService !=null) {  
  39.                            try {      4  
  40.                                int mPid = -1;  
  41.                                mPid = mTaskService.getPid();    
  42.                                Log.v("get Pid "," = " +mPid);  
  43.                                ((TextView)findViewById(R.id.textView1)).setText("Servicepid is" + mPid);  
  44.                            } catch(RemoteException e) {  
  45.                               e.printStackTrace();  
  46.                           }  
  47.                        }  
  48.                        else {  
  49.                            ((TextView)findViewById(R.id.textView1)).setText("Noservice connected");  
  50.                        }  
  51.                    }  
  52.           });  
  53.    
  54.     }  
  55.    
  56.     privateServiceConnectionmTaskConnection = new ServiceConnection() {  5  
  57.        public void onServiceConnected(ComponentName className, IBinder service) {  
  58.            mTaskService = ITaskService.Stub.asInterface(service);  6  
  59.        }  
  60.    
  61.        public void onServiceDisconnected(ComponentName className) {  
  62.            mTaskService = null;  
  63.        }  
  64. };  
  65.    
  66.    @Override  
  67.     public void onDestroy()  
  68.     {  
  69.        super.onDestroy();  
  70.        if (mTaskService !=null) {  
  71.            unbindService(mTaskConnection);  7  
  72.        }  
  73.     }  
  74.      
  75. }  

  1. 必须导入远端接口类的定义。我们必须先导入类或者函数定义,然后才能使用,对于任何编程语言都是这样。但我们在AIDL编程的环境下,实际这一步骤变得更加简单,我们并非需要将Service实现的源文件拷贝到应用程序源代码里,也是只需要一个AIDL文件即可,这一文件会自动生成我们所需要的接口类定义。所以可以注意,我们导入的并非Service实现的”org.lianlab.services.TaskService”,而AIDL接口类的”org.lianlab.services.ITaskService”。这种方式更灵活,同时我们在AIDL环境下还得到了另一个好处,那就是可以隐藏实现。
  2. 对于客户端来说,它并不知道TaskService的实现,于是我们统一使用ITaskService来处理对远程对象的引用。跟步骤1对应,这时我们会使用ITaskService来访问远程对象,就是我们的mTaskService。
  3. 我们必须先创建对象,才能调用对象里的方法,对于AIDL编程而言,所谓的创建对象,就是通过bindService()来触发另一个进程空间的Stub对象被创建。bindService()的第一参数是一个Intent,这一Intent里可以通过ComponentName来指定使用哪个Service,但此时我们会需要Service的定义,于是在AIDL编程里这一Intent会变通为使用ITaskService作为Action值,这种小技巧将使bindService()操作会通过IntentFilter,帮我们找到合适的目标Service并将其绑定。bindService()的第二个参数的类型是ServiceConnection对象,bindService()成功将使用这样一个ServiceConnection对象来管理onServiceConnected()与onServiceDisconnected()两个回调,于是一般我们会定义一个私有的ServiceConnection对象来作为这一参数,见5。最后的第三个参数是一个整形的标志,说明如何处理bindService()请求,我们这里使用Context.BIND_AUTO_CREATE,则发生bindService()操作时,如果目标Service不存在,会触发Service的onCreate()方法创建。
  4. 我们这里使用onClickListener对象来触发远程操作,当用户点击时,就会尝试去执行mTaskService.getPid()方法。正如我们看到的,getPid()是一个RPC方法,会在另一个进程里执行,而Exception是无法跨进程捕捉的,如果我们希望在进行方法调用时捕捉执行过程里的异常,我们就可以通过一个RemoteException来完成。RemoteException实际上跟方法的执行上下文没有关系,也并非完整的Exception栈,但还是能帮助我们分析出错现场,所以一般在进行远端调用的部分,我们都会try来执行远程方法然后捕捉RemoteException。
  5. 如3所述,bindService()会使用一个ServiceConnection对象来判断和处理是否连接正常,于是我们创建这么一个对象。因为这个私有ServiceConnection对象是作为属性存在的,所以实际上在HelloActivity对象的初始化方法里便会被创建。
  6. 在onServiceConnected()这一回调方法里,将返回引用到远程对象的IBinder引用。在Android官方的介绍里,说是这一IBinder对象需要通过asInterface()来进行类型转换,将IBinder再转换成ITaskService。但在实现上并非如此,我们的Proxy在内部是被拆分成Proxy实现与Stub实现的,这两个实现都使用同一IBinder接口,我们在onServiceConnected()里取回的就是这一对象IBinder引用,asInterface()实际上的操作是通过IBinder对象,得到其对应的Proxy实现。
  7. 通过bindService()方法来访问Service,则Service的生存周期位于bindService()与unbindService()之间的Bounded区域,所以在bindService()之后,如果不调用unbindService()则会造成内存泄漏,Binder相关的资源无法得到回收。所以在合适的点调用unbindService()是一种好习惯。

通过这样的方式,我们就得到了耦合度很低的Service方案,我们的这个Activity它即可以与Service处于同一应用程序进程,也可以从另一个进程进行跨进程调用来访问这一Service里暴露出来的方法。而在这种执行环境的变动过程中,代码完全不需要作Service处理上的变动,或者说Activity本身并不知道AIDL实现上的细节,在同一个进程里还是不在同一进程里。

这种方式很简单易行,实际上在编程上,AIDL在编程上带来的额外编码上的开销非常小,但得到了灵活性设计的RPC调用。

双向Remote Service

在AIDL编程环境里实际上是支持反向调用的,原理跟我们实现一个Remote Service一样,就是通过把Proxy与Stub反过来,就得到了这样的回调式的aidl编程。唯一的区别是,当我们的Stub在Activity时实现时,我们实际上跟后台线程执行也没有区别,Callback并非是在主线程里执行的,于是不能进行重绘界面的工作。于是,我们必须像后台线程编程一样,使用Handler来处理界面显示处理。

定义AIDL

前面我们说过aidl是可以互相引用的,于是我们可以借用这样的机制,通过引用另一个新增的aidl文件来加强我们前面的单向的TaskService版本。我们先增加一个新的ITaskServiceCallback.aidl文件,与ITaskService保持同一目录:

  1. package org.lianlab.services;  
  2. onewayinterface ITaskServiceCallback {  
  3.    void valueCounted(int value);  
  4. }  

在这一定义里,我们新增加了一个ITaskServiceCallback的接口类,基本上与我们前面的ITaskService.aidl一样,在这个接口类里,我们新加了一个valueCounted()方法,这一方法将会被Service所使用。

在这个文件里,唯一与ITaskService.aidl不同之处在于,我们使用了一个oneway的标识符,oneway可以使aidl调用具有异步调用的效果。在默认情况下,基于aidl的调用都会等待远端调用完成之后再继续往下执行,但有时我们可能希望在跨进程调用会有异步执行的能力,我们在发出调用请求后会立即返回继续执行,调用请求的结果会通过其他的callback返回,或是我们干脆并不在乎成功与否,此时就可以使用oneway。当然,从我们前面分析aidl底层进行的工作,我们可以知道,所谓的远程调用,只不过是通过Binder发送出去一个命令而已,所以在aidl里面如果使用了oneway限定符,也就是发送了命令就收工。

然后,我们修改一下我们的ITaskService.aidl,使我们可以使用上这个新加入的回调接口:

  1. package org.lianlab.services;  
  2. import org.lianlab.services.ITaskServiceCallback;  
  3. interface ITaskService {  
  4.     intgetPid (ITaskServiceCallback callback);  
  5. }  

在Service端调用回调方法

我们会引用前面定义好的ITaskServiceCallback.aidl文件,通过包名+接口的方式进行引用。为了省事,我们直接在原来的getPid()方法里进行修改,将新定义的ITaskServiceCallback接口类作为参数传递给getPid()接口。于是,在Service端Stub对象里实现的getPid()方法,将可以使用这一回调对象:

  1. package org.lianlab.services;  
  2. import android.app.Service;  
  3. import android.content.Intent;  
  4. import android.os.IBinder;  
  5. import android.os.Process;  
  6. import android.os.RemoteException;  
  7.    
  8. public class TaskService extends Service {  
  9.    
  10.    static private int mCount = 0;  
  11.      
  12.    @Override  
  13.    public IBinder onBind(Intent intent) {  
  14.        if (ITaskService.class.getName().equals(intent.getAction())) {  
  15.            return mTaskServiceBinder;  
  16.        }  
  17.        return null;  
  18.     }  
  19.    
  20.    private final ITaskService.Stub mTaskServiceBinder = newITaskService.Stub() {  
  21.        public int getPid(ITaskServiceCallback callback) { 1  
  22.            mCount ++ ;     2  
  23.            try {   3  
  24.                 callback.valueCounted(mCount);    4  
  25.            } catch (RemoteException e) {  
  26.                 e.printStackTrace();  
  27.            }  
  28.            return Process.myPid();  
  29.        }  
  30.     };  
  31. }  

加入了回调之后的代码结构并没有大变,只增加了3部分的内容,通过这三部分的内容,我们此时便可以记录我们的getPid()总共被调用了多少次。

  1. getPid()方法,是通过aidl定义来实现的,否则会报错。所以我们这里新的getPid()会按照aidl里的定义加入ITaskServiceCallback对象作为参数,与ITaskService对象相反,这一对象实际上是由客户端提供给Service端调用的。
  2. 为了记录下getPid()被调用了多少次,我们使用了一个mCount来进行计数,这一int为static类型,于是在Service生存周期里会始终有效。但这部分的改动与我们的回调改进并无直接关系。
  3. 在使用回调接口ITaskServiceCall之前,因为这是一个远程引用,我们会需要捕捉Remote Exception,由客户端抛出的异常将在这里被捕获处理。
  4. 调用ITaskServiceCall里定义的回调方法,将处理发送给客户端。此时,因为是oneway,这时很多就会从回调方法里返回,继续执行原来的getPid(),再将处理结果以返回值的形式发送回客户端。

加入了回调之后,对Service端的实现并没有增加多大的工作量,因为作为回调,实现是放在客户端上来完成的。

在Client端加入回调实现

因为我们的aidl接口已经发生了变动,于是需要将新加的ITaskServiceCall.aidl与改变过的ITaskService.aidl文件拷贝到应用程序工程里。我们再来看一下客户端实现代码需要作怎样的调整:

  1. package org.lianlab.services;  
  2. import android.os.Bundle;  
  3. import android.os.Handler;  
  4. import android.os.IBinder;  
  5. import android.os.RemoteException;  
  6. import android.app.Activity;  
  7. import android.content.ComponentName;  
  8. import android.content.Context;  
  9. import android.content.Intent;  
  10. import android.content.ServiceConnection;  
  11. import android.view.View;  
  12. import android.view.View.OnClickListener;  
  13. import android.widget.TextView;  
  14.    
  15. import org.lianlab.services.R;  
  16.    
  17. public class MainActivity extends Activity {  
  18.    
  19.    ITaskService mTaskService = null;  
  20.    private TextView mCallbackText;  
  21.    private Handler mHandler = new Handler();  
  22.    
  23.    @Override  
  24.    public void onCreate(Bundle savedInstanceState) {  
  25.        super.onCreate(savedInstanceState);  
  26.    
  27.        try {  
  28.            bindService(newIntent(ITaskService.class.getName()), mTaskConnection,  
  29.                     Context.BIND_AUTO_CREATE);  
  30.        } catch (SecurityException e) {  
  31.            e.printStackTrace();  
  32.        }  
  33.    
  34.        setContentView(R.layout.activity_main);  
  35.    
  36.         ((TextView)findViewById(R.id.textView1)).setOnClickListener(new OnClickListener() {  
  37.            @Override  
  38.            public void onClick(View v) {  
  39.                 if (mTaskService != null) {  
  40.                     try {  
  41.                         

抱歉!评论已关闭.