Android 四大组件之Service (二)

简介

Service在四大组件的中属于那种默默无闻的劳动者,能够在App即使没有显示的界面的情况下保持很高的优先级来保证进程不被杀死,同时也能在主线程上执行一些相对比较耗时的任务。它即不像Activity那么复杂、和用户那么亲近;又不像广播那样是在系统中无孔不入,但却也是Android系统中不可或缺的一个角色。

本文章按照惯例,会先列出一张涉及Service方方面面的数据结构图,这张图包含着从App端到AMS端用到的Service相关联的各种类与对象,可以让大家对Service有个初步的认识。然后我会细讲startService、bindService的流程和Service的一些内部机制,每个小章节后面都有一张总结性质的逻辑图,方便大家直观的看清逻辑走向。最后我将以一个例子的逻辑图作为总结结束这篇文章,希望大家看完最大对Service的源码有个大体的印象。

Service中其它方面的知识点或者说有我没有讲清楚的概念,也欢迎大家留言,我会再对文章进行修改~

结构图

查看大图

App端:

  • ActivityThread: 维护着所有运行中的Service
  • ServiceDispatcher: 负责派发onServiceConnected和onServiceDisconnected方法

AMS端:

  • ConnectionRecord: 客户端/服务端绑定关系的抽象,四大组件中Service与Provider会用到
  • AppBindRecord: Service与一个应用绑定
  • IntentBindService: Intent与Service绑定
  • ActiveServices: 进行Service相关操作的大管家
  • ServiceRecord: Service在AMS的抽象
  • ServiceLookupResult: Service查询结果
  • ServiceMap: 将ServiceRecord进行缓存并提供多种方式查询的类

startService的流程

frameworks/base/core/java/android/app/ContextImpl.java

 
     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 
     
@Override
public ComponentName startService(Intent service) {
warnIfCallingFromSystemProcess();
return startServiceCommon(service, mUser);
}
private ComponentName startServiceCommon(Intent service, UserHandle user) {
try {
validateServiceIntent(service);
service.prepareToLeaveProcess();
ComponentName cn = ActivityManagerNative.getDefault().startService(
mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
getContentResolver()), getOpPackageName(), user.getIdentifier());
return cn;
} catch (RemoteException e) {
throw new RuntimeException( "Failure from system", e);
}
}

使用ActivityManagerProxy进行binder call到AMS

frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

 
     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 
     
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, String callingPackage, int userId)
throws TransactionTooLargeException {
// 查询AMS ServiceMap中的缓存ServiceRecord
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType, callingPackage,
callingPid, callingUid, userId, true, callerFg);
ServiceRecord r = res.record;
return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
}
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
// 进入Service的启动操作
String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false);
if (error != null) {
return new ComponentName( "!!", error);
}
return r.name;
}

这一步binder call到AMS中后,在Service拉起之前先进行判断

frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

 
     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
 
     
private final String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
boolean whileRestarting) throws TransactionTooLargeException {
// 如果Service已经执行onCreate过了,直接执行onStartCommand方法
if (r.app != null && r.app.thread != null) {
sendServiceArgsLocked(r, execInFg, false);
return null;
}
final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
final String procName = r.processName;
ProcessRecord app;
if (!isolated) {
app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
if (app != null && app.thread != null) {
try {
app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
// Service所在进程一切正常,将Service拉起
realStartServiceLocked(r, app, execInFg);
return null;
} catch (TransactionTooLargeException e) {
throw e;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting service " + r.shortName, e);
}
}
}
// 如果Service所在的进程尚未启动,先创建进程
if (app == null) {
if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
"service", r.name, false, isolated, false)) == null) {
bringDownServiceLocked(r);
return msg;
}
if (isolated) {
r.isolatedProc = app;
}
}
return null;
}

确认Service的运行环境是否已经就绪,如果没有准备则需要先把进程拉起,再启动Service

startProcessLocked的流程不再讲了,具体请看我以前写过的文章

 
     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 
     
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
r.app = app;
r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
boolean created = false;
try {
// 调用Service.onCreate方法
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
r.postNotification();
created = true;
} catch (DeadObjectException e) {
} finally {
}
// 调用Service.onBind方法
requestServiceBindingsLocked(r, execInFg);
updateServiceClientActivitiesLocked(app, null, true);
// 调用Service.onStartCommand方法
sendServiceArgsLocked(r, execInFg, true);
}

realStartServiceLocked方法的核心就是将Service启动,调用onCreate, onStartCommand方法

查看大图

bindService的流程

frameworks/base/core/java/android/app/ContextImpl.java

 
     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
 
     
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
warnIfCallingFromSystemProcess();
return bindServiceCommon(service, conn, flags, Process.myUserHandle());
}
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
UserHandle user) {
int res = ActivityManagerNative.getDefault().bindService(
mMainThread.getApplicationThread(), getActivityToken(), service,
service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, getOpPackageName(), user.getIdentifier());
}

客户端binder call到AMS调用bindService之前会在本地检查下一些基本的参数

frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

 
     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
 
     
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection, int flags,
String callingPackage, int userId) throws TransactionTooLargeException {
final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
ActivityRecord activity = null;
if (token != null) {
// 找到绑定的activity
activity = ActivityRecord.isInStackLocked(token);
if (activity == null) {
Slog.w(TAG, "Binding with unknown activity: " + token);
return 0;
}
}
final boolean callerFg = callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE;
// 从ServiceMap中查找到ServiceRecord
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType, callingPackage,
Binder.getCallingPid(), Binder.getCallingUid(), userId, true, callerFg);
ServiceRecord s = res.record;
final long origId = Binder.clearCallingIdentity();
try {
// 进行绑定并保存信息到AMS
mAm.startAssociationLocked(callerApp.uid, callerApp.processName,
s.appInfo.uid, s.name, s.processName);
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent);
IBinder binder = connection.asBinder();
ArrayList<ConnectionRecord> clist = s.connections.get(binder);
if (clist == null) {
clist = new ArrayList<ConnectionRecord>();
s.connections.put(binder, clist);
}
clist.add(c);
b.connections.add(c);
if (activity != null) {
if (activity.connections == null) {
activity.connections = new HashSet<ConnectionRecord>();
}
activity.connections.add(c);
}
b.client.connections.add(c);
clist = mServiceConnections.get(binder);
if (clist == null) {
clist = new ArrayList<ConnectionRecord>();
mServiceConnections.put(binder, clist);
}
clist.add(c);
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
s.lastActivity = SystemClock.uptimeMillis();
// Service尚未启动,则需要先拉起Service
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false) != null) {
return 0;
}
}
if (s.app != null && b.intent.received) {
try {
// Service的环境如果已经准备好,则直接调用onServiceConnected方法
c.conn.connected(s.name, b.intent.binder);
} catch (Exception e) {
Slog.w(TAG, "Failure sending service " + s.shortName
+ " to connection " + c.conn.asBinder()
+ " (in " + c.binding.client.processName + ")", e);
}
}
} finally {
Binder.restoreCallingIdentity(origId);
}
return 1;
}

拉起Service的bringUpServiceLocked方法和startService的方法是一致的,唯一不同的是,bindService的onBind方法会返回Binder的子类实例,随后需要publishService这个过程,完成服务”发布后”,其它APP就可以和这个Service进行绑定然后传递信息了

查看大图

Service内部机制

将Service切换为前台: startForeground

startForeground方法是在Service内部进行调用的,它可以将普通的Service转换为进程优先级更高的前台Service。它的使用只需要传入一个notification实例。在旧版的Android中,这个接口是有着严重的BUG的,常常被第三方应用当作恶意保活的手段,在最新版的Android中,若是想要让Service变后台为前台就必须要传入有效的Notification让用户知晓应用正在占用资源

frameworks/base/core/java/android/app/Service.java

 
     
1
2
3
4
5
6
7
8
 
     
public final void startForeground(int id, Notification notification) {
try {
mActivityManager.setServiceForeground(
new ComponentName( this, mClassName), mToken, id,
notification, true);
} catch (RemoteException ex) {
}
}

调用startForeground之后,直接就binder call到AMS。方法名就叫setServiceForeground,名字很直观

frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

 
     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 
     
public void setServiceForegroundLocked(ComponentName className, IBinder token,
int id, Notification notification, boolean removeNotification) {
try {
// 找到Service
ServiceRecord r = findServiceLocked(className, token, userId);
if (r != null) {
if (id != 0) {
notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
r.foregroundNoti = notification;
r.isForeground = true;
// 将Notifcation的展示入队
r.postNotification();
// 更新Service的前后台转台
if (r.app != null) {
updateServiceForegroundLocked(r.app, true);
}
getServiceMap(r.userId).ensureNotStartingBackground(r);
}
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
private void updateServiceForegroundLocked(ProcessRecord proc, boolean oomAdj) {
mAm.updateProcessForegroundLocked(proc, anyForeground, oomAdj);
}

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

 
     
1
2
3
4
5
6
7
8
9
10
11
12
 
     
final void updateProcessForegroundLocked(ProcessRecord proc, boolean isForeground,
boolean oomAdj) {
if (isForeground != proc.foregroundServices) {
// 设置前台Service的flag
proc.foregroundServices = isForeground;
// 更新进程的优先级
if (oomAdj) {
updateOomAdjLocked();
}
}
}

startForeground的逻辑很简单,无非就是展示一个Notification,随后将Service更新为前台Service

查看大图

Service执行完成: serviceDoneExecuting

frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

 
     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 
     
void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) {
boolean inDestroying = mDestroyingServices.contains(r);
if (r != null) {
if (type == ActivityThread.SERVICE_DONE_EXECUTING_START) {
// onStartCommand之后会走这里
switch (res) {
// 根据res来设置stopIfKilled的flag,决定进程死亡后Service是否能够自动重启
case Service.START_STICKY_COMPATIBILITY:
case Service.START_STICKY: {
r.stopIfKilled = true;
}
}
}
final long origId = Binder.clearCallingIdentity();
// 完成剩余的Service完成操作
serviceDoneExecutingLocked(r, inDestroying, inDestroying);
Binder.restoreCallingIdentity(origId);
} else {
Slog.w(TAG, "Done executing unknown service from pid "
+ Binder.getCallingPid());
}
}
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
boolean finishing) {
if (r.executeNesting <= 0) {
if (r.app != null) {
r.app.execServicesFg = false;
r.app.executingServices.remove(r);
if (r.app.executingServices.size() == 0) {
// 移除Service ANR的定时Message
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
}
// 更新进程优先级
mAm.updateOomAdjLocked(r.app);
}
}
}

serviceDoneExecuting的触发时机:

  • Service.onCreate之后
  • Service.onRebind之后
  • Service.onUnbind之后
  • Service.onStartCommand之后
  • Service.onStop之后

值得注意的是,serviceDoneExecuting期间会掉哦用handler.removeMessage来移除Service ANR的定时Message。也就是说如果在serviceDoneExecuting之前,Service的这一系列操作执行了20s(前台)200s (后台),那么就会发生service的ANR

查看大图

中止Service: stopSelf

stopSelf也是在Service内部进行调用的,用来主动的去终止Service。最终会调用到onStop方法,整个过程会将进程中的这个Service销毁掉

frameworks/base/core/java/android/app/Service.java

 
     
1
2
3
4
5
6
7
 
     
public final void stopSelf(int startId) {
try {
mActivityManager.stopServiceToken(
new ComponentName( this, mClassName), mToken, startId);
} catch (RemoteException ex) {
}
}

frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

 
     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
 
     
boolean stopServiceTokenLocked(ComponentName className, IBinder token,
int startId) {
// 找到对应的ServiceRecord
ServiceRecord r = findServiceLocked(className, token, UserHandle.getCallingUserId());
if (r != null) {
// 调用bringDown方法进行Service的销毁操作
bringDownServiceIfNeededLocked(r, false, false);
Binder.restoreCallingIdentity(origId);
return true;
}
return false;
}
private final void bringDownServiceLocked(ServiceRecord r) {
for ( int conni=r.connections.size()- 1; conni>= 0; conni--) {
ArrayList<ConnectionRecord> c = r.connections.valueAt(conni);
for ( int i= 0; i<c.size(); i++) {
ConnectionRecord cr = c.get(i);
cr.serviceDead = true;
try {
// 绑定的service进行断连操作
cr.conn.connected(r.name, null);
} catch (Exception e) {
}
}
}
// Tell the service that it has been unbound.
if (r.app != null && r.app.thread != null) {
for ( int i=r.bindings.size()- 1; i>= 0; i--) {
IntentBindRecord ibr = r.bindings.valueAt(i);
if (ibr.hasBound) {
try {
// 对service进行unbind
r.app.thread.scheduleUnbindService(r,
ibr.intent.getIntent());
} catch (Exception e) {
}
}
}
}
// 销毁ServiceMap中的Service缓存
final ServiceMap smap = getServiceMap(r.userId);
smap.mServicesByName.remove(r.name);
smap.mServicesByIntent.remove(r.intent);
r.totalRestartCount = 0;
unscheduleServiceRestartLocked(r, 0, true);
// Also make sure it is not on the pending list.
for ( int i=mPendingServices.size()- 1; i>= 0; i--) {
if (mPendingServices.get(i) == r) {
mPendingServices.remove(i);
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Removed pending: " + r);
}
}
// 移除notification
r.cancelNotification();
r.isForeground = false;
r.foregroundId = 0;
r.foregroundNoti = null;
// Clear start entries.
r.clearDeliveredStartsLocked();
r.pendingStarts.clear();
if (r.app != null) {
if (r.app.thread != null) {
updateServiceForegroundLocked(r.app, false);
try {
// 通知客户端调用Service.onStop方法
r.app.thread.scheduleStopService(r);
} catch (Exception e) {
}
}
}
}

Service的销毁有以下几个重要的操作:

  • 解除所有的ServiceConnection绑定,调用onServiceDisconencted方法通知客户端Service已经被销毁
  • 解除所有的Activity与Service的绑定,调用onUnbind通知Service解绑
  • 删除所有在AMS的缓存数据
  • 移除前台Service的Notification
  • 停止自启动的Service
  • 通知客户端的Service调用onStop方法

总结 && 示例流程图

以下给出一个例子的流程图,这个例子贯穿了大部分Service的知识点:

  1. App1调用bindService,尝试绑定App2的Service并实现通信
  2. App2的Service开启了一个Notification,变为前台Service
  3. 一段时间后App2自动将Service停止

转载出自: http://navyblue.top/2017/12/10/%E4%BB%8E%E6%BA%90%E7%A0%81%E8%A7%92%E5%BA%A6%E7%9C%8BService/#more

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值