Android 综合揭秘 —— 全面剖释 Service 服务
引言
Service 服务是 Android 系统最常用的四大部件之一,Android 支持 Service 服务的原因主要目的有两个,一是简化后台任务的实现,二是实现在同一台设备当中跨进程的远程信息通信。
Service 服务主要分为 Local Service 本地服务与 Remote Service 远程服务两种,本地服务只支持同一进程内的应用程序进行访问,远程服务可通过AIDL(Android Interface Definition Language)技术支持跨进程访问。服务可以通过Context.startService()和Context.bindService()进行启动,一般Local Service本地服务可使用其中一种方法启动,但Remote Service远程服务只能使用Context.bindService()启动,而两种调用方式在使用场景与活动流程中都存在差异。还有通过多线程技术处理 Service 服务的延时操作等技术,下文将针对Android 系统的 Service 服务的一系列操作进行深入探讨。
目录
一、Service 服务的概念与说明
二、Service 服务的生命周期
三、Local Service 的应用原理与开发实例
四、通过多线程方式处理 Service 服务的延时性操作
五、浅谈 Remote Service 远程服务原理
一、Android Service的概念与说明
1.1 Service 服务的定义
Android Service 是 Android 平台最常用的部件之一,其概念与 Windows Service 类似,熟悉Windows开发的朋友应该对此概念会有所了解。当 Android 系统需要对现有的程序数据进行监听,或者对现有 Actitvity 提供数据服务支撑时,就会使用到 Android Service 。例如:对用户地理位置的检测,对SD卡定时扫描,对当地气候的定期检测都会使用到 Service 服务,Service 一般都是运行于后台,不需要用户界面支撑。Service 服务不会自动创建线程,如果开发人员没有为Service服务添加异步操作,那Service服务将运行于主线程当中。
1.2 Service 服务的类型
1.2.1 按照 Service 的生命周期模型一共分为两种类型
第一类是直接通过Context.startService()启动,通过Context.stopService() 结束Service,其特点在于调用简单,方便控制。缺点在于一旦启动了 Service 服务,除了再次调用或结束服务外就再无法对服务内部状态进行操控,缺乏灵活性。
第二类是通过Context.bindService()启动,通过Context.unbindService() 结束,相对其特点在运用灵活,可以通过 IBinder 接口中获取 Service 的句柄,对 Service 状态进行检测。
从 Android 系统设计的架构上看,startService() 是用于启动本地服务,bindService() 更多是用于对远程服务进行绑定。当然,也可以结合两者进行混合式应用,先通过startService()启动服务,然后通过 bindService() 、unbindService()方法进行多次绑定,以获取 Service 服务在不同状态下的信息,最后通过stopService()方法结束Service运行,在下面文章里将举例一一说明。
1.2.2 按照 Service 的寄存方式分为两种类型
本地服务 (Local Service) 寄存于当前的进程当中,当前进程结束后 Service 也会随之结束,Service 可以随时与 Activity 等多个部件进行信息交换。Service服务不会自动启动线程,如果没有人工调用多线程方式进行启动,Service将寄存于主线程当中。
远程服务 (Remote Service ) 独立寄存于另一进程中, 通过 AIDL (Android Interface Definition Language)接口定义语言,实现Android设备上的两个进程间通信(IPC)。AIDL 的 IPC 机制是基于 RPC (Remote Proceduce Call) 远程过程调用协议建立的,用于约束两个进程间的通讯规则,供编译器生成代码。进程之间的通信信息,首先会被转换成AIDL协议消息,然后发送给对方,对方收到AIDL协议消息后再转换成相应的对象,其使用方法在下文将会详细说明。
回到目录
二、Android Service 的生命周期
2.1 Service 服务的常用方法
表2.1
细说onStartCommand 方法
由于手机的RAM、内部资源有限,所以很多Service都会因为资源不足而被Kill掉,这时候返回值就决定了Service被Kill后的处理方式,一般 int onStartCommand(intent,flags,startId)的返回值分为以下几种:
START_STICKY
如果service进程被kill掉,系统会尝试重新创建Service,如果在此期间没有任何启动命令被传递到Service,那么参数intent将为null。
START_NOT_STICKY
使用这个返回值时,如果在执行完onStartCommand()后,服务被异常kill掉,系统不会自动重启该服务。
START_REDELIVER_INTENT
使用这个返回值时,如果在执行完onStartCommand()后,服务被异常kill掉,系统会自动重启该服务,并将intent的值传入。
START_STICKY_COMPATIBILITY
START_STICKY的兼容版本,但不保证服务被kill后一定能重启。
而输入参数flags正是代表此次onStartCommand()方法的启动方式,正常启动时,flags默认为0,被kill后重新启动,参数分为以下两种:
START_FLAG_RETRY
代表service被kill后重新启动,由于上次返回值为START_STICKY,所以参数 intent 为null
START_FLAG_REDELIVERY
代表service被kill后重新启动,由于上次返回值为START_REDELIVER_INTENT,所以带输入参数intent
2.2 Service 的运作流程
上文曾经提到 Service 的启动方法有Context.startService(intent),Context.bindService(intent,serviceConnection,int) 两种,下面详细介绍一下它们工作流程。
当系统调用Context.startService()方法时,先会触发Service的onCreate()方法,这一般用于对Service的运行条件作初始化处理,且在Service的生命周期内只会被触发一次。然后系统将触发Service的onStartCommand()方法,用户每次调用startService()方法,都会触发onStartCommand()方法。之后,Service 除非在资源不足的情况下被系统 kill 掉,否则Service不会自动结束,直至系统调用Context.stopService()方法时,Service 才会结束。在Service结束时将自动启动onDestory()方法对运转中的Service作最后处理。
注意:即使系统多次调用 startService()或 bindService()方法, onCreate() 方法只会在第一次调用时被触发。同理 onDestory () 方法也只会在服务完结时被触发,其原理可看第2.1节该方法的详细说明。
当系统调用Context.bindService()方法时,也会触发Service的onCreate()方法对Service对象的运行条件作初始化处理,然后触发Service 的 onBind ()方法对服务进行绑定,成功获取Service的句柄后,系统就会通过用户自定义的serviceConnection对象onServiceConnected(ComponentName name, IBinder service)方法,对 Service 对象作出处理。最后当系统调用Context.unbindService()结束服务时,就会激发Service的onDestory()方法对运转中的 Service 作最后的处理。
注意:系统调用 Context.bindService()方法,完成 Service.onBind() 绑定后就会触发 serviceConnection对象的 onServiceConnected()方法,但只要系统未使用 Context.unbindService()方法对 service 服务进行解绑,即使多次调用bindService(),系统也只会在第一次绑定时调用onBind() 和 onServiceConnected方()法一次。这正是 startService()与 bindService()方法其中的区别,单从字面上理解 startService () 启动服务是可以多次执行,所以多次调用 startService()方法都会触发 onStartCommand()事件,而bindService() 是绑定服务,所以只要服务已经被绑定,在未解绑时也不会多次执行onServiceConnected()绑定后的操作,这也是两者在使用场景上的区别所在。
Service 生命周期 图2.2
Service 的运转流程就先介绍到这里,具体的使用方法将在下面的章节中详细介绍。
回到目录
三、Local Service 应用原理与开发实例
3.1 通过 Context.startService 启动 Service 服务
首先建立MyService继承Service,实现onCreate()、onDestory()、onStartCommand()、onStart()等几个方法,使用日志记录其运作信息。在Activity中通过Intent绑定Service服务,通过Context.startService()启动服务,通过Context.stopService()结束服务。
publicclassMainActivityextendsActivity{@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}publicvoidbtnStart_onclick(Viewview){//通过Intent绑定MyService,加入输入参数Intentintent=newIntent(MainActivity.this,MyService.class);intent.putExtra("Name","Leslie");Log.i(Context.ACTIVITY_SERVICE,"----------onClickstartService-----------");//启动MyServicestartService(intent);}publicvoidbtnStop_onclick(Viewview){Intentintent=newIntent(MainActivity.this,MyService.class);Log.i(Context.ACTIVITY_SERVICE,"----------onClickstopService------------");//停止MyServicestopService(intent);}}publicclassMyServiceextendsService{@OverridepublicvoidonCreate(){Log.i(Context.ACTIVITY_SERVICE,"ServiceonCreate");super.onCreate();}@OverridepublicvoidonDestroy(){Log.i(Context.ACTIVITY_SERVICE,"ServiceonDestroy");super.onDestroy();}@OverridepublicvoidonStart(Intentintent,intstartId){Log.i(Context.ACTIVITY_SERVICE,"ServiceonStart");super.onStart(intent,startId);}@OverridepublicintonStartCommand(Intentintent,intflags,intstartId){Log.i(Context.ACTIVITY_SERVICE,"ServiceonStartCommand");Stringname=intent.getStringExtra("Name");Log.i(Context.ACTIVITY_SERVICE,"Hisnameis"+name);returnsuper.onStartCommand(intent,flags,startId);}}
AndroidManifest.xml 文件绑定
<activityandroid:name=".MainActivity"android:label="@string/title_activity_main"><intent-filter><actionandroid:name="android.intent.action.MAIN"/><categoryandroid:name="android.intent.category.LAUNCHER"/></intent-filter></activity><serviceandroid:name="android.services.MyService"android:enabled="true"></service>
Service 配置说明:
android:name 服务类名,注意如果Service与Activity不在同一个包中,在android:name上必须写上Service的全路径
android:label 服务的名字,如果为空,默认显示的服务名为类名
android:icon 服务的图标
android:permission 申明此服务的权限,这意味着只有提供了该权限的应用才能控制或连接此服务
android:process 表示该服务是否运行在另外一个进程,如果设置了此项,那么将会在包名后面加上这段字符串表示另一进程的名字
android:enabled 如果此项设置为 true,那么 Service 将会默认被系统启动,默认值为 false
android:exported 表示该服务是否能够被其他应用程序所控制或连接,默认值为 false
查看处理结果可清楚看到,多次调用startService()后,使用stopService()结束Service服务,onCreate()、onDestory()只会在Service启动和结束时被调用一次。只有Service中的onStartCommand()方法会被多次调用。而Android 2.0以下旧版的方法onStart()会在onStartCommand()调用过程中被激发。
3.2 通过Context.bindService启动Service服务
在介绍Context.bindService()前,先讲解一下与此相关的常用类 Binder、ServiceConnection,首先 IBinder 是 Binder 远程对象的基本接口,是为高性能而设计的轻量级远程调用机制的核心部分。这个接口定义了与远程对象交互的协议,但它不仅用于远程调用,也用于进程内调用。系统可以通过它以获取Service的句柄,在此先简单介绍它的基本用法,在下面关于Remote Service远程服务对象时再详细讲述IBinder的主体功能。ServiceConnection主要用于通过Binder绑定Service句柄后,对Service对象进行处理,它主要有两个方法void onServiceConnected(ComponentName name, IBinder service)和void onServiceDisconnected(ComponentName name)。在Context.bindService()完成绑定后,系统就会调用 onServiceConnected() 方法,用户可以通过 IBinder 参数获取Service句柄,对Service进行处理。而 onServiceDisconnected() 方法一般不会被调用,只有Service被绑定后,由于内存不足等问题被意外 kill 时才会被调用。下面举个例子说明一下bindService()的用法。
publicclassMainActivityextendsActivity{privateMyServiceConnectionserviceConnection;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);serviceConnection=newMyServiceConnection();}publicvoidbtnBind_onclick(Viewview){//绑定MyServiceIntentintent=newIntent(this,MyService.class);Log.i(Context.ACTIVITY_SERVICE,"----------onClickbindService-----------");//通过bindService(intent,serviceConnection,int)方式启动ServicebindService(intent,this.serviceConnection,Context.BIND_AUTO_CREATE);}publicvoidbtnUnbind_onclick(Viewview){Log.i(Context.ACTIVITY_SERVICE,"----------onClickunbindService----------");unbindService(serviceConnection);}}publicclassMyServiceextendsService{privateMyBindermyBinder;@OverridepublicIBinderonBind(Intentintent){Log.i(Context.ACTIVITY_SERVICE,"ServiceonBind");returnthis.myBinder;}@OverridepublicbooleanonUnbind(Intentintent){Log.i(Context.ACTIVITY_SERVICE,"ServiceonUnbind");returnsuper.onUnbind(intent);}@OverridepublicvoidonCreate(){super.onCreate();Log.i(Context.ACTIVITY_SERVICE,"ServiceonCreate");myBinder=newMyBinder();}@OverridepublicvoidonDestroy(){Log.i(Context.ACTIVITY_SERVICE,"ServiceonDestroy");super.onDestroy();}publicStringgetDate(){Calendarcalendar=Calendar.getInstance();returncalendar.getTime().toString();}publicclassMyBinderextendsBinder{publicMyServicegetService(){returnMyService.this;}}}publicclassMyServiceConnectionimplementsServiceConnection{@OverridepublicvoidonServiceConnected(ComponentNamename,IBinderservice){Log.i(Context.ACTIVITY_SERVICE,"ServiceConnected");Stringdata=null;//通过IBinder获取Service句柄MyService.MyBindermyBinder=(MyService.MyBinder)service;MyServicemyService=myBinder.getService();data=myService.getDate();Log.i(Context.ACTIVITY_SERVICE,data);}@OverridepublicvoidonServiceDisconnected(ComponentNamename){Log.i(Context.ACTIVITY_SERVICE,"ServiceDisconnected");}}
在运行时多次点击按钮激发btnBind_ view)方法后再使用btnUnbind_ view)结束服务,请留意处理结果。当系统调用Context .bindService()后,Service将跟随onCreate()、onBind()、onUnbind()、onDestory()的流程走下去。在成功完成onBind()绑定后,就会激发ServiceConnection对象的onServiceConnected()方法,在此用户可对Service进行处理。记得第2.2节所提过的问题,即使多次调用Context.bindService()方法,只要没有调用unbindService()结束绑定,系统只会在第一次调用时激发Service.onBind()和onServiceConnected()方法,这点从运行结果中可得到证实。
注意:调用 Context .bindService() 启动 Service 后 ,只能调用 unbindService() 一次,如重复多次调用此方法系统将会抛出错误异常。所以最简单的处理方式是设置一个静态变量 boolean connected,在调用 unbindService() 前先作出判断
publicclassMainActivityextendsActivity{privateMyServiceConnectionserviceConnection;privatestaticbooleanconnected;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);serviceConnection=newMyServiceConnection();}publicvoidbtnBind_onclick(Viewview){connected=true;//绑定MyServiceIntentintent=newIntent(this,MyService.class);Log.i(Context.ACTIVITY_SERVICE,"----------onClickbindService-----------");//通过bindService(intent,serviceConnection,int)方式启动ServicebindService(intent,this.serviceConnection,Context.BIND_AUTO_CREATE);}publicvoidbtnUnbind_onclick(Viewview){Log.i(Context.ACTIVITY_SERVICE,"----------onClickunbindService----------");if(connected){unbindService(serviceConnection);connected=false;}}}
3.3 Service 服务的综合运用
在前两节只是从初级阶段介绍了Service服务的使用原理,无论是使用startService()或者bindService()启动服务,Service服务的运行都是阶段性,当使用stopService()、unbindService()后,Service服务就会结束。然而从现实应用层面上看,Service 服务很多时候是长驻后台的,它会记录程序运行的流程,当今的状态等重要信息。此时,更多的使用方式就是结合startService()、bindService()两种方式调用Service服务,startService()负责管理Service服务的启动,输入初始化参数,bindService()负责定时对Service服务进行检测。而且流程是有规律性,以startService()启动服务后,每使用bindService()绑定服务,就通过serviceConnection对服务进行检测,然后以unbindService()结束绑定。注意,此时服务并未结束,而是长期运行于后台,直到系统以stopService()方法结束服务后,Service才会最终完结。
publicclassMainActivityextendsActivity{privateMyServiceConnectionserviceConnection;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);serviceConnection=newMyServiceConnection();}publicvoidbtnBind_onclick(Viewview){//绑定MyServiceIntentintent=newIntent(this,MyService.class);Log.i(Context.ACTIVITY_SERVICE,"----------onClickbindService-----------");//通过bindService(intent,serviceConnection,int)方式启动ServicebindService(intent,this.serviceConnection,Context.BIND_AUTO_CREATE);}publicvoidbtnUnbind_onclick(Viewview){Log.i(Context.ACTIVITY_SERVICE,"----------onClickunbindService----------");unbindService(serviceConnection);}publicvoidbtnStart_onclick(Viewview){//通过Intent绑定MyService,加入初始参数Intentintent=newIntent(MainActivity.this,MyService.class);intent.putExtra("param",0.88);Log.i(Context.ACTIVITY_SERVICE,"----------onClickstartService-----------");//启动MyServicestartService(intent);}publicvoidbtnStop_onclick(Viewview){Intentintent=newIntent(MainActivity.this,MyService.class);Log.i(Context.ACTIVITY_SERVICE,"----------onClickstopService------------");//停止MyServicestopService(intent);}}publicclassMyServiceextendsService{privateMyBindermyBinder;privatedoubleparam;@OverridepublicvoidonStart(Intentintent,intstartId){Log.i(Context.ACTIVITY_SERVICE,"ServiceonStart");super.onStart(intent,startId);}@OverridepublicintonStartCommand(Intentintent,intflags,intstartId){Log.i(Context.ACTIVITY_SERVICE,"ServiceonStartCommand");//获取Context.startService设置的param初始值this.param=intent.getDoubleExtra("param",1.0);returnsuper.onStartCommand(intent,flags,startId);}@OverridepublicIBinderonBind(Intentintent){Log.i(Context.ACTIVITY_SERVICE,"ServiceonBind");returnthis.myBinder;}@OverridepublicbooleanonUnbind(Intentintent){Log.i(Context.ACTIVITY_SERVICE,"ServiceonUnbind");returnsuper.onUnbind(intent);}@OverridepublicvoidonCreate(){super.onCreate();Log.i(Context.ACTIVITY_SERVICE,"ServiceonCreate");myBinder=newMyBinder();}@OverridepublicvoidonDestroy(){Log.i(Context.ACTIVITY_SERVICE,"ServiceonDestroy");super.onDestroy();}//获取处理后的值publicdoublegetValue(intvalue){returnvalue*param;}publicclassMyBinderextendsBinder{publicMyServicegetService(){returnMyService.this;}}}publicclassMyServiceConnectionimplementsServiceConnection{@OverridepublicvoidonServiceConnected(ComponentNamename,IBinderservice){Log.i(Context.ACTIVITY_SERVICE,"ServiceConnected");//通过IBinder获取Service句柄MyService.MyBindermyBinder=(MyService.MyBinder)service;MyServicemyService=myBinder.getService();//生成随机数输入Randomrandom=newRandom();doublevalue=myService.getValue(random.nextInt(10)*1000);//显示计算结果Log.i(Context.ACTIVITY_SERVICE,String.valueOf(value));}@OverridepublicvoidonServiceDisconnected(ComponentNamename){Log.i(Context.ACTIVITY_SERVICE,"ServiceDisconnected");}}
通过startService() 启动服务后,多次使用bindService()绑定服务,unbindService()解除绑定,最后通过stopService()结束服务后,可以看到下面的结果
这时候 Service 的onBind()方法和onUnbind()方法只在第一个bindService流程中触发,其后多次调用bindService(),此事件都不会被触发,而只会触发onServiceConnected()事件。这是因为在默认情况下,系统在绑定时会先搜索IBinder接口,如果Service已经绑定了Binder对象,系统就会直接跳过onBind()方法。
既然 onBind(),onUnbind()方法只会在第一次启动绑定时被调用,如果在多次绑定时需要有不同的处理方式又该如何,还好Android为大家预备了一个备用方法void onRebind(intent),Service服务中 boolean onUnbind(intent)的默认返回值为false,只要将此方法的返回值修改为true,则系统在第二次调用Context.bindService()开始,就会激活Service.onRebind(intent)方法。在此对上面的方法作出少量修改,就会看到下面的处理结果。
publicclassMyServiceextendsService{...........@OverridepublicvoidonRebind(Intentintent){Log.i(Context.ACTIVITY_SERVICE,"ServiceonRebind");super.onRebind(intent);}@OverridepublicbooleanonUnbind(Intentintent){Log.i(Context.ACTIVITY_SERVICE,"ServiceonUnbind");//将返回值设置为truereturntrue;}......................}
运行结果
注意:此使用方法只适用 startService()、bindServcie()同时被调用的情况下,如果只调用其中一个方法,无论onUnbind()返回值为何值都无法触发onRebind()方法
回到目录
四、通过多线程方式处理 Service 的延时性操作
4.1 以 Runnable接口实现 Service 多线程操作
由于Android 系统的资源有限,而且对屏幕显示,事件发应,用户体现都有较高的要求,所以在CPU、RAM、GPU、GPU都有独立的运行机制。当主线程中存在大文件读取、图片批量处理、网络连接超时等操作时,一旦时间超过5秒,Android 系统就会出现 “设置运行缓慢” 的提示,Logcat日志上也会显示 “The application may be doing too much work on its main thread” 等提示。在开发Service服务时,若存在此类操作时,开发人员就应该尝试使用多线程方式进行开发,避免主线程被长时间占用。下文将以简单的 Runnable 接口方式实现多线程作为例子。
publicclassMainActivityextendsActivity{@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}publicvoidbtnStart_onclick(Viewview){Intentintent=newIntent(MainActivity.this,MyService.class);Log.i(Context.ACTIVITY_SERVICE,"----------onClickstartService-----------------");startService(intent);}publicvoidbtnStop_onclick(Viewview){Intentintent=newIntent(MainActivity.this,MyService.class);Log.i(Context.ACTIVITY_SERVICE,"----------onClickstopService------------------");stopService(intent);}}publicclassMyServiceextendsService{@OverridepublicvoidonCreate(){Log.i(Context.ACTIVITY_SERVICE,"ServiceonCreate");super.onCreate();}@OverridepublicintonStartCommand(Intentintent,intflags,intstartId){Log.i(Context.ACTIVITY_SERVICE,"ServiceonStartCommand");Log.i(Context.ACTIVITY_SERVICE,"Mainthreadidis"+Thread.currentThread().getId());//以异步方式进行模拟操作Threadbackground=newThread(newAsyncRunnable());background.start();returnsuper.onStartCommand(intent,flags,startId);}@OverridepublicvoidonDestroy(){Log.i(Context.ACTIVITY_SERVICE,"ServiceonDestroy");super.onDestroy();}}publicclassAsyncRunnableimplementsRunnable{@Overridepublicvoidrun(){try{Log.i(Context.ACTIVITY_SERVICE,"Asyncthreadidis"+Thread.currentThread().getId());//虚拟操作for(intn=0;n<8;n++){Thread.sleep(1000);Log.i(Context.ACTIVITY_SERVICE,"****DoWork****");}}catch(InterruptedExceptione){//TODO自动生成的catch块e.printStackTrace();}}}
请留意运行结果,主线程与onStartCommand()方法内部操作存在于不同的线程当中完成
4.2 IntentService 服务简介
在Service服务中出现延时性操作是普遍遇到的情况,有见及此 Android 系统早为开发人员提供了一个Service的子类IntentService,当IntentService执行 startService()方法时,系统将使用一个循环程序将该服务加入到一个子线程队列当中,以便执行服务当中的操作。下面为大家提供 IntentService的源代码,让各位更好的理解IntentService的运行方式。
publicabstractclassIntentServiceextendsService{privatevolatileLoopermServiceLooper;privatevolatileServiceHandlermServiceHandler;privateStringmName;privatebooleanmRedelivery;privatefinalclassServiceHandlerextendsHandler{publicServiceHandler(Looperlooper){super(looper);}@OverridepublicvoidhandleMessage(Messagemsg){onHandleIntent((Intent)msg.obj);stopSelf(msg.arg1);}}publicIntentService(Stringname){super();mName=name;}publicvoidsetIntentRedelivery(booleanenabled){mRedelivery=enabled;}@OverridepublicvoidonCreate(){super.onCreate();HandlerThreadthread=newHandlerThread("IntentService["+mName+"]");thread.start();mServiceLooper=thread.getLooper();mServiceHandler=newServiceHandler(mServiceLooper);}@OverridepublicvoidonStart(Intentintent,intstartId){Messagemsg=mServiceHandler.obtainMessage();msg.arg1=startId;msg.obj=intent;mServiceHandler.sendMessage(msg);}@OverridepublicintonStartCommand(Intentintent,intflags,intstartId){onStart(intent,startId);returnmRedelivery?START_REDELIVER_INTENT:START_NOT_STICKY;}@OverridepublicvoidonDestroy(){mServiceLooper.quit();}@OverridepublicIBinderonBind(Intentintent){returnnull;}protectedabstractvoidonHandleIntent(Intentintent);}
从代码中可以看到,系统没有在onStartCommand()中创建新线程,而是在onCreate()方法中建立了独立的工作线程,这是由于onCreate()方法只会在新建服务时被调用一次,可见这样的目的是为了让系统在单个线程中执行多个异步任务。当系统调用Context.startService()方法时,系统将通过onStart()方法使用异步方式,调用ServiceHandler.handleMessage(msg)进行处理,而handleMessage(msg)正是调用了虚拟方法onHandleIntent(intent),然后以stopSelf()结束服务。所以用户只需要在继承类中重写onHandleIntent(intent)方法,便可以以异步方法执行IntentService。
4.3 IntentService 应用
以下面一个简单的例子说明一下IntentService的应用,建立一个MyIntentService类继承IntentService,实现onHandleIntent(Message msg)方法。然后在MainActivity活动分别3次以不同参数调用intentService,观察其运行的线程状态。
publicclassMyIntentServiceextendsIntentService{publicMyIntentService(){super(null);}@OverrideprotectedvoidonHandleIntent(Intentintent){Stringmsg=intent.getStringExtra("msg");Log.i(Context.ACTIVITY_SERVICE,msg+"'sthreadidis"+Thread.currentThread().getId());}}publicclassMainActivityextendsActivity{@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}publicvoidbtnStart_onclick(Viewview){Log.i(Context.ACTIVITY_SERVICE,"----------onClickstartService--------------");Log.i(Context.ACTIVITY_SERVICE,"Mainthreadidis"+Thread.currentThread().getId());Intentintent1=newIntent(this,MyIntentService.class);intent1.putExtra("msg","intentService1");startService(intent1);Intentintent2=newIntent(this,MyIntentService.class);intent2.putExtra("msg","intentService2");startService(intent2);Intentintent3=newIntent(this,MyIntentService.class);intent3.putExtra("msg","intentService3");startService(intent3);}publicvoidbtnStop_onclick(Viewview){Intentintent=newIntent(MainActivity.this,MyIntentService.class);Log.i(Context.ACTIVITY_SERVICE,"----------onClickstopService-------------");stopService(intent);}}
在AndroidManifest.xml 文件设置服务
<application>..........<serviceandroid:name="android.services.MyIntentService"android:enabled="true"></service></application>
从运行结果中可以看出,同一时间多次启动startService()调用intentService,它们都将运行于同一个异步线程当中,这一点在这里得到了证实。
回到目录
五、浅谈 Remote Service 原理
5.1 跨进程通信的使用场景
以上章节所举的例子都是使用Local Service 技术,Serivce服务端与Client客户端都是在于同一进程当中,当APP被卸御,Service服务也被同时卸御。要是想把服务端与客户端分别放在不同的进程当中进行跨进程信息交换的话,就需要使用到下面介绍的远程通信服务 Remote Service。使用Remote Service可以把服务端与客户端分离,当一方被卸御,另一方不会被影响。当今有很多企业都有多个独立的APP,如阿里巴巴旗下就天猫、淘宝、聚划算、支付宝等多个APP,这时候就有需要把Service服务放在一独立的后台进程当中,作为多个APP之间信息交换的桥梁。这样如用户信息,用户登录,身份验证等多个共用的模块都可以在Service服务中实现,以供不同的APP进行调用。而且当APP被关闭时,Service服务还会寄存在后台当中,对用户的操作进行检测。如今越来越多的企业都使用这种开发方式,以收集用户像所在地点,通信录,短信,彩信等个人信息,方便企业针对用户的个人资料进行产品推广。
5.2 Remote Service 技术背景
Android 系统与 Windows 系统的通信原则基本一致,进程就是安全策略的边界,不同的APP属于不同进程 Process,一个进程不能直接访问其他进程的资源。需要实现多进程间的通信,就要使用IPC(Inter Process Commnication)进程间通信技术。Android 系统的 IPC 机制是基于 RPC (Remote Proceduce Call) 远程过程调用协议建立的,与 Java 使用的 RMI(Rmote Methed Invocation)远程方法调用相比,不同之处在于Android的IPC机制是基于AIDL(Android Interface Definition Language)接口定义语言定制进程间的通讯规则的。系统会基于 AIDL 规则把信息进行序列化处理,然后发送到另一个进程当中,Android 系统把这种基于跨进程通信的服务称作 Remote Service 。
5.3 IPC 运作原理
从底层架构分析, Android 系统中 IPC 的运作主要依赖于 “ServiceManager” 和 “Binder Driver” 两个核心元件,下面给大家简单介绍一下它们的运作原理:
ServiceManager 简介
ServiceManager是Android系统内的服务管理器,主要负责管理 Service 服务的管理,注册,调用等任务。在Google提供的Android原始代码中可以找到(文件夹路径:frameworks/base/cmds/servicemanager),有C语言开发基础且有兴趣的朋友可以下载看一下,当中包含了几个核心的函数:
int svcmgr_handler(struct binder_state *bs, struct binder_txn *txn, struct binder_io *msg, struct binder_io *reply)
int do_add_service(struct binder_state *bs, uint16_t *s, unsigned len, void *ptr, unsigned uid)
void *do_find_service(struct binder_state *bs, uint16_t *s, unsigned len)
voidbinder_loop(structbinder_state*bs,binder_handlerfunc)
ServiceManager 启动后会通过 binder_loop 循环对 Binder Driver 进行监听,当发现了有新的Service服务请求后,就会调用 svcmgr_handler() 函数对检测的Service服务进行处理,通过*do_find_service()函数可以在svclist集中检测Service服务,若当前svclist服务集中未存在当前服务,就会通过do_add_service()进行注册,把当前服务及其唯一标识符加入到svclist中,这样当前的 Service 服务被绑定后就完成在ServiceManager的注册。Binder Driver 会按照规定的格式把它转化为 Binder 实体发送到内核当中,当被 Client 调用时 ServiceManager 会根据 Service 服务的标识符在 svclist 中找到该 Binder 实体,并把 Binder 实体的引用发送给Client。完成计算后 Binder Driver 会进行数据处理,把计算结果发回到Client客户端。由于Binder实体是以强类型的形式存在,所以即使被多次引用,系统都会指向同一个Binder实体,除非所有都结束链接,否则Binder实体会一直存在。
图 5.3
Binder Driver简介
Binder Driver运行于Android 内核当中,它以 “字符驱动设备” 中的 “misc设备注册” 存在于设备目录 dev/binder,由于权限问题,在一般手机中没有权限进行复制,对此有兴趣的朋友可以在google 提供的 android 源代码中查看。它提供open(),mmap(),poll(),ioctl() 等函数进行标准化文件操作,负责进程之间Binder通信的建立,Binder实体在进程之间的传递,Binder实体引用的计数管理,数据包在进程之间的传递与交互等一系列底层操作。
5.4 Remote Service 常用接口
在 5.3 节以 Android 底层结构的方式简单介绍了一下 IPC 通信的原理,下面将以 JAVA 应用层方式再作介绍。
IBinder 接口
IBinder 是 Remote Service 远程服务的常用接口,Binder是它的实现类,它是为高性能而设计的轻量级远程调用机制的核心部分。IBinder 内部比较重要的方法就是boolean transact(int code, Parcel data, Parcel reply, int flags) ,它负责在服务器与客户端之间进行信息交换,调用远程方法进行处理,然后把返回值转换成可序列化对象送回客户端。
5.5 Remote Service 开发实例
首先新建一个项目作为服务端, 建立 AIDL 文件 ITimerService.aidl,系统会根据接口描述自动在gen文件夹内生成对应的类文件 ITimerService.java ,当中 Stub 扩展了 android.os.Binder 并利用 transact ()实现了 ITimerService 接口中方法的远程调用。
packagecom.example.remoteservice;interfaceITimerService{StringgetTimeNow();}
gen\com\example\remoteservice\ITimerService.java (自动生成)
packagecom.example.remoteservice;publicinterfaceITimerServiceextendsandroid.os.IInterface{/**Local-sideIPCimplementationstubclass.*/publicstaticabstractclassStubextendsandroid.os.Binderimplementscom.example.remoteservice.ITimerService{privatestaticfinaljava.lang.StringDESCRIPTOR="com.example.remoteservice.ITimerService";/**Constructthestubatattachittotheinterface.*/publicStub(){this.attachInterface(this,DESCRIPTOR);}/***CastanIBinderobjectintoancom.example.remoteservice.ITimerServiceinterface,*generatingaproxyifneeded.*/publicstaticcom.example.remoteservice.ITimerServiceasInterface(android.os.IBinderobj){if((obj==null)){returnnull;}android.os.IInterfaceiin=obj.queryLocalInterface(DESCRIPTOR);if(((iin!=null)&&(iininstanceofcom.example.remoteservice.ITimerService))){return((com.example.remoteservice.ITimerService)iin);}returnnewcom.example.remoteservice.ITimerService.Stub.Proxy(obj);}@Overridepublicandroid.os.IBinderasBinder(){returnthis;}@OverridepublicbooleanonTransact(intcode,android.os.Parceldata,android.os.Parcelreply,intflags)throwsandroid.os.RemoteException{switch(code){caseINTERFACE_TRANSACTION:{reply.writeString(DESCRIPTOR);returntrue;}caseTRANSACTION_getTimeNow:{data.enforceInterface(DESCRIPTOR);java.lang.String_result=this.getTimeNow();reply.writeNoException();reply.writeString(_result);returntrue;}}returnsuper.onTransact(code,data,reply,flags);}privatestaticclassProxyimplementscom.example.remoteservice.ITimerService{privateandroid.os.IBindermRemote;Proxy(android.os.IBinderremote){mRemote=remote;}@Overridepublicandroid.os.IBinderasBinder(){returnmRemote;}publicjava.lang.StringgetInterfaceDescriptor(){returnDESCRIPTOR;}@Overridepublicjava.lang.StringgetTimeNow()throwsandroid.os.RemoteException{android.os.Parcel_data=android.os.Parcel.obtain();android.os.Parcel_reply=android.os.Parcel.obtain();java.lang.String_result;try{_data.writeInterfaceToken(DESCRIPTOR);mRemote.transact(Stub.TRANSACTION_getTimeNow,_data,_reply,0);_reply.readException();_result=_reply.readString();}finally{_reply.recycle();_data.recycle();}return_result;}}staticfinalintTRANSACTION_getTimeNow=(android.os.IBinder.FIRST_CALL_TRANSACTION+0);}publicjava.lang.StringgetTimeNow()throwsandroid.os.RemoteException;}
然后建立服务TimerService,建立内置类TimerServiceImpl实现接口ITimerService中的方法,由于使用 Remote Service 只能使用 bindService()方式对服务进行远程绑定,所以TimerService中须利用 onBind() 方法绑定 TimerServiceImpl 对象。
publicclassTimerServiceextendsService{@OverridepublicIBinderonBind(Intentintent){//TODO自动生成的方法存根returnnewTimerServiceImpl();}publicclassTimerServiceImplextendsITimerService.Stub{@OverridepublicStringgetTimeNow()throwsRemoteException{//获取当时时间与服务器端的进程IdDatedate=newDate();SimpleDateFormatformatter=newSimpleDateFormat("Eyyyy.MM.dd'at'hh:mm:ssazzz");return"Timenowis"+formatter.format(date)+"\nServiceprocessIdis"+Process.myPid();}}}
在 AndroidManifest.xml 文件设置服务绑定,在 action 项的 android:name 中绑定当前服务的接口
<application>........<serviceandroid:name=".TimerService"android:process=":remote"><intent-filter><actionandroid:name="com.example.remoteservice.ITimerService"/></intent-filter></service></application>
服务器端完成配置后建立一个客户端项目,把ITimerService.aidl文件copy到客户端,此时客户端也会在gen文件夹中自动生成ITimerService.java文件。在Activity中调用Remote Service时请注意,android 4.0 及以下版本,可通过 Intent(string action) 构造函数生成后直接调用。android 5.0 及以上版本需通过intent.setPackage(string packageName)指定action的包名称。
publicclassMainActivityextendsActivity{privateMyServiceConnectionserviceConnection;privatebooleanconnected;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//建立ServiceConnection对象serviceConnection=newMyServiceConnection();}publicvoidbtnBind_onclick(Viewview){Intentintent=newIntent();//绑定远程服务接口intent.setAction("com.example.remoteservice.ITimerService");intent.setPackage("com.example.remoteservice");this.connected=true;Log.i(Context.ACTIVITY_SERVICE,"-------onClickbindService--------");bindService(intent,this.serviceConnection,Context.BIND_AUTO_CREATE);}publicvoidbtnUnbind_onclick(Viewview){Log.i(Context.ACTIVITY_SERVICE,"-------onClickunbindService---------");if(connected){unbindService(serviceConnection);connected=false;}}}publicclassMyServiceConnectionimplementsServiceConnection{@OverridepublicvoidonServiceConnected(ComponentNamename,IBinderservice){//TODO自动生成的方法存根Log.i(Context.ACTIVITY_SERVICE,"ServiceConnected");//获取远程对象ITimerServicetimerService=ITimerService.Stub.asInterface(service);Stringdata=null;try{data=timerService.getTimeNow()+"\nClientprocessIdis"+Process.myPid();}catch(RemoteExceptione){//TODO自动生成的catch块e.printStackTrace();}Log.i(Context.ACTIVITY_SERVICE,data);}}
从运行结果可清晰看到Service与Client运行于不同的进程当中
5.6 Remote Service 复杂类型数据传输
当 Remote Service 需要使用自定义类型的数据进行传输时,数据对象需要经过序列化处理,而 Android 对象的序列化处理有两种方式,一是常用方式Serializable 接口,另一个是 Android 独有的Parcelable 接口。由于常用的 Serializable 接口,会使用大量的临时变量耗费内存而导致大量的GC垃圾回收,引起手机资源不足,因此 Android 研发出 Parcelable 接口实现对象的序列化。它可被看作为一个 Parcel 容器,通过 writeToParcel() 与 createFormParcel() 方法把对象读写到 Parcel 当中,Parcelable接口如下:
publicinterfaceParcelable{//内容描述接口publicintdescribeContents();//对象序列化方式publicvoidwriteToParcel(Parceldest,intflags);//反序列化对象,使用泛型方式在Parcel中构造一个实现了Parcelable的类的实例处理。//接口分别定义了单个实例和多个实例publicinterfaceCreator<T>{publicTcreateFromParcel(Parcelsource);publicT[]newArray(intsize);}}
首先建立服务端,新建Person.aidl文件
packagecom.example.remoteservice;parcelablePerson;
建立Person类,实现Parcelable接口
publicclassPersonimplementsParcelable{privateStringname;privateIntegerage;privateStringdesc;publicPerson(){}publicPerson(Stringname,Integerage,Stringdesc){//TODO自动生成的构造函数存根this.name=name;this.age=age;this.desc=desc;}publicStringgetName(){returnthis.name;}publicvoidsetName(Stringname){this.name=name;}publicIntegergetAge(){returnthis.age;}publicvoidsetAge(Integerage){this.age=age;}publicStringgetDesc(){returnthis.desc;}publicvoidsetDesc(Stringdesc){this.desc=desc;}@OverridepublicintdescribeContents(){//TODO自动生成的方法存根return0;}@OverridepublicvoidwriteToParcel(Parceldest,intflags){//TODO自动生成的方法存根dest.writeString(name);dest.writeInt(age);dest.writeString(desc);}publicstaticfinalParcelable.Creator<Person>CREATOR=newCreator<Person>(){/***创建一个要序列号的实体类的数组,数组中存储的都设置为null*/@OverridepublicPerson[]newArray(intsize){returnnewPerson[size];}/****根据序列号的Parcel对象,反序列号为原本的实体对象*读出顺序要和writeToParcel的写入顺序相同*/@OverridepublicPersoncreateFromParcel(Parcelsource){Stringname=source.readString();intage=source.readInt();Stringdesc=source.readString();PersonPerson=newPerson();Person.setName(name);Person.setAge(age);Person.setDesc(desc);returnPerson;}};}
建立服务IPersonService.aidl文件
packagecom.example.remoteservice;importcom.example.remoteservice.Person;interfaceIPersonService{PersongetPerson(Stringnumber);}
此时在gen\com\example\remoteservice文件夹内将自动生成成IPersonService.java类
/**Thisfileisauto-generated.DONOTMODIFY.*Originalfile:D:\\Java_Projects\\RemoteService\\src\\com\\example\\remoteservice\\IPersonService.aidl*/packagecom.example.remoteservice;publicinterfaceIPersonServiceextendsandroid.os.IInterface{/**Local-sideIPCimplementationstubclass.*/publicstaticabstractclassStubextendsandroid.os.Binderimplementscom.example.remoteservice.IPersonService{privatestaticfinaljava.lang.StringDESCRIPTOR="com.example.remoteservice.IPersonService";/**Constructthestubatattachittotheinterface.*/publicStub(){this.attachInterface(this,DESCRIPTOR);}/***CastanIBinderobjectintoancom.example.remoteservice.IPersonServiceinterface,*generatingaproxyifneeded.*/publicstaticcom.example.remoteservice.IPersonServiceasInterface(android.os.IBinderobj){if((obj==null)){returnnull;}android.os.IInterfaceiin=obj.queryLocalInterface(DESCRIPTOR);if(((iin!=null)&&(iininstanceofcom.example.remoteservice.IPersonService))){return((com.example.remoteservice.IPersonService)iin);}returnnewcom.example.remoteservice.IPersonService.Stub.Proxy(obj);}@Overridepublicandroid.os.IBinderasBinder(){returnthis;}@OverridepublicbooleanonTransact(intcode,android.os.Parceldata,android.os.Parcelreply,intflags)throwsandroid.os.RemoteException{switch(code){caseINTERFACE_TRANSACTION:{reply.writeString(DESCRIPTOR);returntrue;}caseTRANSACTION_getPerson:{data.enforceInterface(DESCRIPTOR);java.lang.String_arg0;_arg0=data.readString();com.example.remoteservice.Person_result=this.getPerson(_arg0);reply.writeNoException();if((_result!=null)){reply.writeInt(1);_result.writeToParcel(reply,android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);}else{reply.writeInt(0);}returntrue;}}returnsuper.onTransact(code,data,reply,flags);}privatestaticclassProxyimplementscom.example.remoteservice.IPersonService{privateandroid.os.IBindermRemote;Proxy(android.os.IBinderremote){mRemote=remote;}@Overridepublicandroid.os.IBinderasBinder(){returnmRemote;}publicjava.lang.StringgetInterfaceDescriptor(){returnDESCRIPTOR;}@Overridepubliccom.example.remoteservice.PersongetPerson(java.lang.Stringnumber)throwsandroid.os.RemoteException{android.os.Parcel_data=android.os.Parcel.obtain();android.os.Parcel_reply=android.os.Parcel.obtain();com.example.remoteservice.Person_result;try{_data.writeInterfaceToken(DESCRIPTOR);_data.writeString(number);mRemote.transact(Stub.TRANSACTION_getPerson,_data,_reply,0);_reply.readException();if((0!=_reply.readInt())){_result=com.example.remoteservice.Person.CREATOR.createFromParcel(_reply);}else{_result=null;}}finally{_reply.recycle();_data.recycle();}return_result;}}staticfinalintTRANSACTION_getPerson=(android.os.IBinder.FIRST_CALL_TRANSACTION+0);}publiccom.example.remoteservice.PersongetPerson(java.lang.Stringnumber)throwsandroid.os.RemoteException;}
然后建立服务PersonService,建立内置类PersonServiceImpl实现接口IPersonService中的方法,在 PersonService 中须利用 onBind() 方法绑定 PersonServiceImpl 对象。
publicclassPersonServiceextendsService{@OverridepublicIBinderonBind(Intentintent){//TODO自动生成的方法存根returnnewPersonServiceImpl();}publicclassPersonServiceImplextendsIPersonService.Stub{@OverridepublicPersongetPerson(Stringnumber)throwsRemoteException{//TODO自动生成的方法存根switch(number){case"0":returnnewPerson("JackMokei",34,"ProjectManager");case"1":returnnewPerson("MikeTlea",24,"TeamLeader");default:returnnull;}}}}
在 AndroidManifest.xml 文件设置服务绑定,在 action 项的 android:name 中绑定当前服务的接口
<application>........<serviceandroid:name=".PersonService"android:process=":remote"><intent-filter><actionandroid:name="com.example.remoteservice.IPersonService"/></intent-filter></service></application>
服务器端完成配置后建立一个客户端项目,把Person.aidl、IPersonService.aidl文件copy到客户端,此时客户端也会在gen文件夹中自动生成 Person.java和 IPersonService.java文件
publicclassMainActivityextendsActivity{privateMyServiceConnectionserviceConnection;privatebooleanconnected;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//建立ServiceConnection对象serviceConnection=newMyServiceConnection();}publicvoidbtnBind_onclick(Viewview){Intentintent=newIntent();//绑定远程服务接口intent.setAction("com.example.remoteservice.IPersonService");intent.setPackage("com.example.remoteservice");this.connected=true;Log.i(Context.ACTIVITY_SERVICE,"----------onClickbindService----------");bindService(intent,this.serviceConnection,Context.BIND_AUTO_CREATE);}publicvoidbtnUnbind_onclick(Viewview){Log.i(Context.ACTIVITY_SERVICE,"----------onClickunbindService--------");if(connected){unbindService(serviceConnection);connected=false;}}}publicclassMyServiceConnectionimplementsServiceConnection{@OverridepublicvoidonServiceConnected(ComponentNamename,IBinderservice){Log.i(Context.ACTIVITY_SERVICE,"ServiceConnected");//绑定远程对象IPersonServicepersonService=IPersonService.Stub.asInterface(service);Stringdata=null;try{Personperson=personService.getPerson("0");data=person.getName()+"'sageis"+person.getAge();}catch(RemoteExceptione){//TODO自动生成的catch块e.printStackTrace();}Log.i(Context.ACTIVITY_SERVICE,data);}}
运行结果
回到目录
本章总结
通过文章的例子大家可以了解到Local Service本地服务与Remote Service远程服务之间的区别,以及Context.startService()方法以及Context.bindService()方法不同的使用场景。希望文章有帮于大家对Service服务有更深入的了解,在不同的开发环境中灵活运用。由于时间仓促,文章当中有不明确的地方或有错漏敬请点明。
Android开发笔记
全面剖释 Android Service 服务
Cordova(PhoneGap)通过CordovaPlugin插件调用 Activity 实例
最新版Cordova 5.1.1(PhoneGap)搭建开发环境
作者:风尘浪子
http://www.cnblogs.com/leslies2/p/5401813.html
原创作品,转载时请注明作者及出处
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。