目录
- 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为false
private ComponentName startServiceCommon(Intent service, boolean requireForeground,
UserHandle user) {
try {
validateServiceIntent(service);
service.prepareToLeaveProcess(this);
// binder call至ams
ComponentName 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 {
callerFg = true;
}
// 拿到要启动的service的信息
// 主要是从packageManager