Android 8.0 Service源码分析:启动流程及后台限制详解

本文深入分析了Android 8.0中Service的启动(startService与bindService)、前台服务创建、后台启动限制以及销毁过程。重点介绍了AMS如何管理Service生命周期,包括Service启动时的检查、前台服务通知的展示/取消,以及Service被stop的各种场景。此外,还探讨了Android 8.0对后台Service的限制和前台服务的特殊性。最后解答了关于Service启动方式区别、Android 8.0后台限制及前台服务限制等问题。
摘要由CSDN通过智能技术生成

转载请注明出处:https://blog.csdn.net/zwjemperor/article/details/82949913
github:https://github.com/rushgit/zhongwenjun.github.com

概述

一直以来都想把App的启动过程和四大组件的启动过程系统的梳理一 遍,但一直都没有沉下心去阅读源码。因为最近要把一个App的targetSdkVersion升级到26(Android 8.0),需要弄清楚Android 8.0对后台服务究竟做了哪些限制。借此契机把Service的启动流程和后台限制相关的源码梳理了一遍。

在使用Service的过程中,相信不少人有过这些疑问:

  • Service的启动流程是怎样的?两种启动方式具体有什么区别?
  • Android 8.0+对后台Service究竟做了什么限制?对所有App一视同仁吗?
  • 前台服务为什么不受限制?前台服务可以不弹前台通知吗?
  • JobService的机制是什么,为什么不受后台限制?

通过阅读本文,了解清楚Service内部机制,你将弄明白这些问题。因涉及到篇幅问题,我将分为两篇文章来介绍Service。

本篇重点介绍:

  1. 先整体介绍与Service交互过程中涉及到的几个角色,以及它们各自承担的职责;
  2. 从源码层面讲解Service的启动过程,包括start和bind两种方式,如何启动前台服务,以及后台启动的限制;
  3. 再从源码层面讲解Service的销毁过程,包括stop和unbind两种方式,以及Service被stop的几种场景、进程状态、ANR机制等;
  4. 最后进行总结,回答上面提出的几个问题。

每一个小节过后都会梳理出流程图,下一篇将介绍JobService机制。

1. Service整体交互结构

Service作为Android四大组件之一,其生命周期是通过system_server进程中的ActivityManagerService(AMS)管理的,所以要理解Service的通信机制,首先要了解Binder机制,对Binder不了解的同学请先阅读关于Binder,作为应用开发者你需要知道的全部,本篇不做过多介绍。

下面先上一张图,大致了解Service通信过程中涉及到的几个主要角色。
service_structure
App端进程:

  • ContextImpl

    Context抽象类所有api的实现,是Service、Activity和其他组件base Context。

  • ActivityThread

    代表着App的主线程,是App的入口,Application、Activity、Service都在ActivityThread中创建,维护着该App所有运行中的Service实例。其中有一个IApplicationThread类型成员mAppThread,用于被AMS跨进程调用。

  • Service
    具体提供服务的Service,被ActivityThread管理。

  • ServiceConnection

    监听Service连接状态的接口,用于bindService。

AMS端:

  • ActivityManagerService

    四大组件的大管家,是Framework中极为重要的一个类。

  • ActiveServices

    AMS中管理Service的具体类。

  • ServiceRecord

    Service结构的具体描述。

  • ServiceMap

    描述了一个用户(App)的所有Service记录,主要用于检索。

  • ConnectionRecord

    Client端与Service端绑定的抽象描述。

2. Service启动过程

2.1 startService

首先看入口:

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

@Override
public ComponentName startService(Intent service) {
   
    warnIfCallingFromSystemProcess();
    return startServiceCommon(service, false, mUser);
}

private ComponentName startServiceCommon(Intent service, boolean requireForeground, UserHandle user) {
   
    validateServiceIntent(service);
    service.prepareToLeaveProcess(this);
    ComponentName cn = ActivityManager.getService().startService(mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(getContentResolver()), requireForeground, getOpPackageName(), user.getIdentifier());
    ......
	return cn;
}

ContextImpl中只做了validateServiceIntent校验(target 21之后限制隐式启动),然后调用了AMSstartService方法。再看AMS中的实现:

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

@Override
public ComponentName startService(IApplicationThread caller, Intent service, String resolvedType, boolean requireForeground, String callingPackage, int userId)
    throws TransactionTooLargeException {
   
    ......
    synchronized(this) {
   
        final int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        ComponentName res;
        try {
   
            res = mServices.startServiceLocked(caller, service, resolvedType, callingPid, callingUid, requireForeground, callingPackage, userId);
        } finally {
   
            Binder.restoreCallingIdentity(origId);
        }
        return res;
    }
}

直接调用了ActiveServicesstartServiceLocked方法。

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

ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType, int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
    throws TransactionTooLargeException {
   
    ......
    // 1. 从mServiceMap中查询SerivceRecord缓存,如果没有则创建一个
    ServiceLookupResult res = retrieveServiceLocked(service, resolvedType, callingPackage, callingPid, callingUid, userId, true, callerFg, false);
    ......
    ServiceRecord r = res.record;
    ......
    // If this isn't a direct-to-foreground start, check our ability to kick off an arbitrary service
    // fgRequired为false,即不是启动前台服务
    if (!r.startRequested && !fgRequired) {
   
        // 2. 检查是否允许启动方应用启动Service
        final int allowed = mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName, r.appInfo.targetSdkVersion, callingPid, false, false);
        // app mode不为APP_START_MODE_NORMAL表示应用处于后台,而不在后台不受限的白名单中
        if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
   
            Slog.w(TAG, "Background start not allowed: service " + service + " to " + r.name.flattenToShortString() + " from pid=" + callingPid + " uid=" + callingUid + " pkg=" + callingPackage);
            // 不允许启动后台Service
            return new ComponentName("?", "app is in background uid " + uidRec);
        }
    }
	......
    r.startRequested = true;
    ......
    final ServiceMap smap = getServiceMapLocked(r.userId);
    boolean addToStarting = false;
    // 3. 非前台调用,且非启动前台服务,且app进程未启动
    if (!callerFg && !fgRequired && r.app == null && mAm.mUserController.hasStartedUserState(r.userId)) {
   
        ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
        if (proc == null || proc.curProcState > ActivityManager.PROCESS_STATE_RECEIVER) {
   
            // 如果调用方进程不在前台,而且正在启动的后台Service过多,该Service会被延时启动,避免在短时间内启动大量进程。
            if (smap.mStartingBackground.size() >= mMaxStartingBackground) {
   
                smap.mDelayedStartList.add(r);
                r.delayed = true;
                return r.name;
            }
            addToStarting = true;
        }
        ......
    } 
    ......

    ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
    return cmp;
}

这个方法中做了几个检查操作:

  1. mServiceMap中查询SerivceRecord缓存,如果没有则创建一个;
  2. 如果不是启动前台服务,会检查启动方是否能启动Service,如果启动方应用不在前台,且未在允许后台启动Service的白名单中,将禁止启动。(白名单的逻辑在后面介绍
  3. 如非前台调用,也非启动前台服务,且app进程未启动,且正在启动的后台Service过多,该Service会被延时启动,避免在短时间内启动大量进程。
  4. 通过了前面的检查,调用startServiceInnerLocked
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r, boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
   
    ......
    // 调用bringUpServiceLocked启动Service
    String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
    ......
    return r.name;
}

startServiceInnerLocked调用bringUpSrviceLocked启动Service。

private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg, boolean whileRestarting, boolean permissionsReviewRequired)
    throws TransactionTooLargeException {
   
    // 1.如果此Service已经被启动,直接调用onStartCommand
    if (r.app != null && r.app.thread != null) {
   
        sendServiceArgsLocked(r, execInFg, false);
        return null;
    }
	......
    if (!isolated) {
   
        app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
        // 2.Service所属进程已经启动
        if (app != null && app.thread != null) {
   
            try {
   
                app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
                // 进入真正启动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);
            }
        }
    }

    // 3.如果Service所属进程尚未启动,则先启动进程
    if (app == null && !permissionsReviewRequired) {
   
        if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags, hostingType, r.name, false, isolated, false)) == null) {
   
            bringDownServiceLocked(r);
            return msg;
        }
    }
    // 加入Pengding列表
    if (!mPendingServices.contains(r)) {
   
        mPendingServices.add(r);
    }
	......
    return null;
}

这个方法做了3件事情:

  1. 如果此Service已经被启动,直接调用onStartCommand
  2. 如果此Service未启动,但所属进程已启动,则调用realStartServiceLocked进入真正启动Service的流程;
  3. 如果Service所属进程尚未启动,则先启动进程,如app进程启动失败则销毁此Service;如启动成功,则加入Pengding启动列表,待App进程启动结束后再启动Service。

下面看真正启动Service的方法realStartServiceLocked

private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException {
   
    .....
    final boolean newService = app.services.add(r);
    bumpServiceExecutingLocked(r, execInFg, "create");
    mAm.updateLruProcessLocked(app, false, null);
    updateServiceForegroundLocked(r.app, /* oomAdj= */ false);
    // 1.调整应用进程优先级
    mAm.updateOomAdjLocked();

    boolean created = false;
    try {
   
        ......
        mAm.notifyPackageUse(r.serviceInfo.packageName, PackageManager.NOTIFY_PACKAGE_USE_SERVICE);       app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
        // 2.通知ActivityThread创建Service,调用onCreate
        app.thread.scheduleCreateService(r, r.serviceInfo, mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo), app.repProcState);
        // 3.如果已经设置通知,创建前台通知
        r.postNotification();
        created = true;
    }
    ......

    // 通知ActivityThread调用Service的onBind方法
    requestServiceBindingsLocked(r, execInFg);
    updateServiceClientActivitiesLocked(app, null, true);
    // 1.如启动前台服务,则发送一个5s的延时消息,如5s内未调用Service.startForeground,应用将ANR
    // 2.通知ActivityThread调用Service的onStartCommand方法
    sendServiceArgsLocked(r, execInFg, true);
	......
}
  1. 通知AMS调整应用进程优先级

  2. 跨进程调用,通过Service所属进程的IApplicationThread,即ActivityThread创建Service实例,再调用其onCreate方法;

  3. 如果已经设置通知,则创建前台通知;

  4. 如果Service已经被绑定,则调用onBind方法;

  5. 调用sendServiceArgsLocked

    这个方法主要做了2个事情:

    1)如启动前台服务,则发送一个5s的延时消息,如5s内未调用Service.startForeground,应用将ANR;

    2)通知ActivityThread调用Service的onStartCommand方法;

大家对第3、4步可能会有一个疑问:Service刚创建,肯定没有调用startForeground设置前台通知,也没有被bind,为什么要检查是否创建前台通知、调用onBind呢?

在前一个方法bringUpServiceLocked中我们已经介绍,当应用进程未启动时,AMS首先是去启动Service所属进程,同时ServiceRecord放进了Pengding列表。在应用主线线被创建后,再启动之前被挂起的Service,所以是存在已经设置前台通知或者被bind的情况的。

本篇重点介绍Service的启动流程,Pengding Service被启动的逻辑不做详细介绍,调用过程如下,有兴趣的同学可以查阅源码。

  1. ActivityThread.attach
  2. AMS.attachApplication
  3. AMS.attachApplicationLocked
  4. ActiveServices.attachApplicationLocked
  5. ActiveServices.realStartServiceLocked

流程图:start

start_service

2.2 bindService

下面看bindService的流程,入口同样在ContextImpl中。

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

public boolean bindService(Intent service, ServiceConnection conn, int flags) {
   
    warnIfCallingFromSystemProcess();
    return 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值