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

特别声明:本系列文章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来得到不交互在后台执行的逻辑。如下图加强部分所示:

Service与Android系统实现(1)--应用程序里的servie( 一)_第1张图片

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回调的实现:

[java] view plain copy
  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的定义:

[html] view plain copy
  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()回调方法:

[java] view plain copy
  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来驱动,则会变成下面的样子:

[java] view plain copy
  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里实现的方法。

[java] view plain copy
  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与Android系统实现(1)--应用程序里的servie( 一)_第2张图片

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


你可能感兴趣的:(Service与Android系统实现(1)--应用程序里的servie( 一))