从源码角度看 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

  • @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

  • 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

  • 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的流程不再讲了,具体请看我以前写过的文章

  • 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

  • @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

```java
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 {
top Created with Sketch.