本文主要是介绍Android Service原理分析之startService(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
- Service概述
- Service分类
- startService时序图
- 源码解析
- ActivityManagerService中的处理
- ActiveServices中的处理
- Service.onCreate的执行
- Service.onStartCommand的执行
- 总结
1. Service概述
Service作为Android四大组件之一,在开发过程中非常常用,它虽然没有ui,但是同样可以在后台做很多重要的事情,我们平时使用启动service主要通过startService
以及bindService
来启动service以便已经相关操作。本文将介绍startService
的原理,后续将分别介绍bindService
的原理,以及在Android O上对service的新增限制管控。
注
:本文基于Android 8.1
2. Service分类
- 从启动方式上,可以分别通过
startService
以及bindService
启动,两者之间最重要的区别在于bindService可以建立binder连接,更加方便通信。 - 从运行方式上,可以分为前台service(foregroundService,下面简称
fg-service
)与后台service,两者的区别在于有前台service的进程对应的优先级会更高,不容易被系统清理掉,但是前台service需要在通知栏里面显示一个常驻的通知。
3. startService时序图
startService的启动大致分为三种情况:
- 对应进程不存在(先启动进程然后启动service并执行onCreate和onStartCommand)
- 进程存在但是service不存在(启动service并执行onCreate和onStartCommand)
- 进程和service都存在(只执行onStartCommand)
上面的时序图主要针对进程存在,但是service不存在的情况,其他两种在主要流程上没有太大区别,关键在于其中有一些特殊判断,在文章过程中也会提到。
4. 源码解析
4.1 ContextImpl.startService
public ComponentName startService(Intent service) {warnIfCallingFromSystemProcess();return startServiceCommon(service, false, mUser);}public ComponentName startForegroundService(Intent service) {warnIfCallingFromSystemProcess();return startServiceCommon(service, true, mUser);}// 最终的入口都在这个方法中// foreground service只是传参requireForeground为true,普通service为falseprivate ComponentName startServiceCommon(Intent service, boolean requireForeground,UserHandle user) {try {validateServiceIntent(service);service.prepareToLeaveProcess(this);// binder call至amsComponentName cn = ActivityManager.getService().startService(mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(getContentResolver()), requireForeground,getOpPackageName(), user.getIdentifier());if (cn != null) {if (cn.getPackageName().equals("!")) {throw new SecurityException("Not allowed to start service " + service+ " without permission " + cn.getClassName());} else if (cn.getPackageName().equals("!!")) {throw new SecurityException("Unable to start service " + service+ ": " + cn.getClassName());} else if (cn.getPackageName().equals("?")) {throw new IllegalStateException("Not allowed to start service " + service + ": " + cn.getClassName());}}return cn;} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}
最终通过startServiceCommon调用至ams,注意这里返回值有进行特殊处理,然后抛出了一些异常,主要返回值的component的packageName有"!"
,"!!"
,"?"
三种,后面在介绍源码过程中可以看到具体是哪里会返回这些异常。
"!"
:权限校验没通过,不允许启动
"!!"
:无法启动
"?"
:不允许启动
4.2 ActivityManagerService.startService
public ComponentName startService(IApplicationThread caller, Intent service,String resolvedType, boolean requireForeground, String callingPackage, int userId)throws TransactionTooLargeException {enforceNotIsolatedCaller("startService");// 进行一些简单的校验if (service != null && service.hasFileDescriptors() == true) {throw new IllegalArgumentException("File descriptors passed in Intent");}// callingPackage不能为空if (callingPackage == null) {throw new IllegalArgumentException("callingPackage cannot be null");}synchronized(this) {final int callingPid = Binder.getCallingPid();final int callingUid = Binder.getCallingUid();final long origId = Binder.clearCallingIdentity();ComponentName res;try {// mService正是ActiveServices对象res = mServices.startServiceLocked(caller, service,resolvedType, callingPid, callingUid,requireForeground, callingPackage, userId);} finally {Binder.restoreCallingIdentity(origId);}return res;}}
这块只是简单校验之后,就调用到了ActiveServices.startServiceLocked
。
参数解析:
caller
:发起startService的进程对应的IApplicationThread对象
service
:要启动的service的intent
resolvedType
:service类型,启动的时候可以设置data已经schema等
requireForeground
:是否发起启动foregroundService
callingPackage
:发起startService的进程的packageName
userId
:当前用户id
4.3 ActiveServices.startServiceLocked
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)throws TransactionTooLargeException {final boolean callerFg;// 获取发起binder call的caller进程,如果不存在则抛异常if (caller != null) {final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);if (callerApp == null) {throw new SecurityException("Unable to find app for caller " + caller+ " (pid=" + callingPid+ ") when starting service " + service);}// 根据当前这个进程所处的调度环境判断是前台还是后台callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;} else {
这篇关于Android Service原理分析之startService(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!