本文主要是介绍android Application Component研究之Service,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
本文为原创文章,欢迎转载!转载时请注明出处:http://blog.csdn.net/windskier
前面2篇文章介绍了acitivity的管理,其中保存task的管理,activity生命周期中各个阶段的操作等问题,这篇文章我们来详细的研究一下Android系统中application Service的管理过程。
Service是android中一个非常重要的组件,作为一个从事android开发的人,service是必须掌握的一个组件,这篇文章不是从如何使用Service角度来分析的,而是从AMS如何管理Service的角度来分析AMS的源码,以期理解AMS针对Service的操作的源码实现。
我们知道,Service的启动方式有两种,一种是通过startService()方法来启动,这种方法的启动作为client方,只是作为一个启动者存在的,它只能通过onStartCommand
()方法去要求Service做某些操作,而通过bind方式的Service则不同,而通过bindService()方法启动并bind Service的话,client可以通过Service提供的业务接口,通过IPC的方式来访问Service的相关业务操作。
而两种方式的生命周期也不尽相同,当client通过startService()方法来启动service,client通过调用stopService()或者service本身调用stopSelf()来结束当前的Service,假如多个client启动的这个Service,那么只要一个client stop它,那么就将会被stop掉。
而client通过
bindService()方式启动的Service,那么client将通过unbindService()来结束与service的连接,这种方式的生命周期后面会介绍到。
更多的关于service的应用请参考android的官方文档。
关于通过startService()方式启动serivce的源码,这里就不再分析了,下面着重分析一下通过bindService()方法来启动并bind service的过程,着启动其实已经包含了startService()方式启动serivce的源码。
1. ServiceDispatcher
1.1 创建ServiceDispatcher
@ContextImpl.java
mPackageInfo对象是一个LoadedApk类型,LoadedApk类型存储着当前package的内容。
@ContextImpl.java
@LoadedApk.java
从上面代码可以看出,对于ServiceDispatcher的管理,是以package为载体的,也就是说对于某个package,定义在其中的component如果请求去bind一个service,那么
LoadedApk将为这个component分配一个ServiceDispatcher。因此,ServiceDispatcher是client方的一个概念。component每请求bind一个不同的service,LoadedApk将会为其分配一个ServiceDispatcher,如下图所示。
1.2 InnerConnection
从上面bindService()的代码中可以看出,我们在bind 过程中传递给AMS的并不是ServiceConnection,而是一个IServiceConnection interface,它对应的服务实体类型是InnerConnection,InnerConnection是ServiceDispatcher的一个内部静态类。
疑问1:为什么不直接把ServiceConnection传递给AMS作为回调,而是传递另外一个类型InnerConnection呢?
首要一点是因为,ServiceConnection是一个Interface(java),我们传递的之时implements ServiceConnection之后的一个内部类对象,根据android IBinder的实现机制,在IPC调用过程中,传递的参数必须是Parcel对象,这就要求传递的对象是Parcel类型的,或者这个类型实现了写入Parcel对象的方法,即writeToParcel()。即使ServiceConnection实现了writeToParcel(),它也不可能将其回调函数传递给AMS,因为writeToParcel()主要是对成员变量的打包过程,Parcel并没有实现对方法的打包,因此系统不可能将ServiceConnection内部类传递给AMS,因此需要提供给AMS另外一种方法来回调ServiceConnection中的方法,这个InnerConnection就起到了这个作用。
疑问2:既然决定用InnerConnection来提供给AMS远程调用ServiceConnection的方法,那么为什么还需要ServiceDispatcher?
其实,在我看来ServiceDispatcher完全可以被InnerConnection代替,从功能来看是可以实现的,但是在IPC通信编程中,我们应该尽量的减少IBinder参数的负荷,这么做无可厚非。
2. Client Intent
在android中有这么一种情况,如果某个application包括了"android.uid.system"的sharedUserId,这其中包括system service。这个application需要bind一个service,并且这个service只允许"android.uid.system"的application来bind。这么做是处于安全方面考虑的,某些Service不想处system uid之外的应用来请求服务。
在上述描述的情景中,如果一个第三方的appliction,它不可能有"android.uid.system"的sharedUserId,想要去请求上述的service时,就不能够直接bind 这个servcie,而是只能通过上述的system uid的application来请求。那么如何通过system uid的application来请求service呢?AMS为这种情形的需求实现了一套解决方案。下面我们以InputMethodManagerService为例来说明一下。
先简单介绍一下输入法的逻辑,由于系统中可以存在多个输入法,InputMethodManagerService会去bind系统设置的或者用户选择的输入法service,这些输入法service只能被system uid的application来请求。IMS在bind输入法service时,会提供一个PendingIntent给AMS,由于这个PendingIntent是供Client来访问输入法Service,我们称这个PendingIntent为ClientIntent,相关代码如下所示:
startInputInnerLocked()@InputMethodManagerService.java
由上图所示,输入法service只能被system uid的application请求,系统服务InputMethodManagerService bind了它,并且向AMS提供了一个client Intent,第三方的application如果需要对输入法service进行请求,只能通过IMS提供的这个client Intent指示的activity来进行操作,这个activity是LanguageSettings。这样就达到了避免输入法service不被第三方应用随意操作引起的安全问题。
AMS中对client Intent的管理如下代码所示:
bindService()@ActivityManagerService.java
3. Service相关数据结构
每个Service均可能有不同的应用进程来bind,同时bind该Service的Intent也可能有多个,也就是说可能存在多个进程来bind该service,并且多个不同进程可能使用同一个Intent来bind serice。下图示意了这种存在的可能。
AMS为每个bind 该service的Intent分配了一个IntentBindRecord类型对象,并存储在ServiceRecord.bindings成员变量中;
AMS为每个bind该service的进程分配一个AppBindRecord类型对象,因为不同的应用程序可能使用同一个Intent来bind service,因此AMS将这个AppBindRecord对象存储在IntentBindRecord.apps中,多进程使用同一个Intent的典型案例就是上节所说的Client Intent;
AMS为每次bind的连接分配一个ConnectionRecord类型对象,每个应用进程中可能有多个Component来使用相同的Intent来bind该Service,那么处于同一个进程的ConnectionRecord对象被存储在AppBindRecord.connections中;
而ActivityRecord以及ProcessRecord中的connections则包含了所有的bind不同的Service的ConnectionRecord对象。它与AppBindRecord.connections范围不同。
总而言之,与Service相关的几种数据结构关系如下:
4. 设置Forground属性
在系统空间吃紧的情况下,系统需要杀死一些进程并收回其内存供其他进程使用,当然service所在的进程与不能幸免,但是如果当前的service非常重要,不允许被系统回收,那么就需要在service启动时设置其Forground属性,很简单就通过Service的一个方法startForeground()来设置。系统默认Service是background的。
如果Service不想保持forground属性,那么可以调用Service的stopForeground()方法来实现,使其回到background属性。
在设置Service 为forground属性时,可以要求系统显示一个notification在status bar,user可以通过notification来执行设定的操作。
如果进程中只要有一个Service处在forground属性状态,那么这个进程被回收的oom_adj值就为PERCEPTIBLE_APP_ADJ,从而达到避免被系统回收的目的。关于AMS的oom机制以后专门分析。
computeOomAdjLocked()@ActivityManagerService.java
5. unbind Service
bind Service过程是一个从无到有的集创建和bind的过程,而unbind过程则是集unbind和销毁Service的过程,但是unbind何时才会包含销毁service的过程,这个规则还是有必要研究一下。
5.1 unbind Service
5.2 destroy Service
unbind一个Service过程中,何时需要destroy这个Service,首先还是需要看bind该Service时设置的flag,如果设置了flag BIND_AUTO_CREATE,那么在unbind之后会去检查是否应该destroy这个Service,如下面代码所示:
removeConnectionLocked()@ActivityManagerService.java
如果没有设置flag BIND_AUTO_CREATE,则AMS根本就不考虑destoy service的操作。下面来分析一下如果设置了BIND_AUTO_CREATE,那么AMS满足哪些条件就会去destroy 这个Service。
● unbind过程中,remove掉当前这个ConnectionRecord之后,发现目前Service中尚有其他的ConnectionRecord仍旧设置了BIND_AUTO_CREATE,那么这个Service将不会被destroy;
只有当Service中不再有任何的ConnectionRecord设置BIND_AUTO_CREATE之后,才去destroy这个Service。因此,可以看出BIND_AUTO_CREATE对Service的销毁过程起到了决定性的作用。
bringDownServiceLocked()@ActivityManagerService.java
● 如果Service中没有了任何ConnectionRecord设置了BIND_AUTO_CREATE,那么将会销毁这个Service,在销毁之前,还有通过IServiceConnection.connected()通知所有的Client。
bringDownServiceLocked()@ActivityManagerService.java
并且unbind掉所有的client.
bringDownServiceLocked()@ActivityManagerService.java
1. unbind过程会unbind掉所有使用同一个IServiceConnection bind 该Service的Client;
2. unbind过程是针对Intent进行的,因此只有bind Service的IntentBindRecord中记录的进程个数为0时,才会去执行unbind过程。也就是说假如有多个进程通过同一个Intent来bind Service,那么只有当基于这个Intent的进程均被remove出IntentBindRecord之后采取真正的去unbind。
3. unbind的ConnectionRecord如果设置了BIND_AUTO_CREATE,那么AMS就会尝试去销毁这个Service,如果Service中剩余的所有ConnectionRecord均未设置BIND_AUTO_CREATE,则会去销毁这个Service,换句话说,如果当Service中有多个ConnectionRecord设置了BIND_AUTO_CREATE,那么只有当最后一个设置了BIND_AUTO_CREATE ConnectionRecord在被unbind时,才会去销毁这个Service,即使当前还有其他的ConnectionRecord bind着这个Service。
4. 如果当前的Service是通过startService()来启动的,那么任何的unbind过程均不会去销毁.
这篇关于android Application Component研究之Service的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!