IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> startService流程详解 -> 正文阅读

[移动开发]startService流程详解

1.Service整体交互结构

Service作为Android四大组件之一,其生命周期是通过system_server进程中的ActivityManagerService(AMS)管理的,大致了解Service通信过程中涉及到的几个主要角色。

在这里插入图片描述

  • 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

首先看入口:

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之后限制隐式启动),然后调用了AMS的startService方法。再看AMS中的实现:

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;
    }
}

直接调用了ActiveServices的startServiceLocked方法。

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,app和app.thread在create时赋值,所以不为空表示service已经create
    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. 如果Service已经被绑定,则调用onBind方法;
  4. 调用sendServiceArgsLocked
  5. 这个方法主要做了2个事情:
    1)如启动前台服务,则发送一个5s的延时消息,如5s内未调用Service.startForeground,应用将ANR;
    2)通知ActivityThread调用Service的onStartCommand方法;

在这里插入图片描述

参考 : https://www.jianshu.com/p/77fe505e2287

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-12-05 12:09:21  更:2021-12-05 12:10:46 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 6:24:51-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码