service启动activity_「 Android 10 四大组件 」系列—Service 的 quot; 启动流程 quot;

527ac3d00cec4c8a2d9e24e544d2b2d5.png
作者:DeepCoder

核心源码

关键类路径

ad1f1f8ecbda101127ccb379255bc286.png

Service 的启动过程相对 Activity 的启动过程来说简单了很多,我们都知道怎么去创建和启动一个 Service, 那么你有没有从源码角度研究过 Service 启动后在系统层是如何运作的 ?

第一次看我文章的小伙伴可以关注一下我,顺便关注一下我的专栏:Android高级开发架构技术专栏,每天更新各种技术干货,分享更多最热程序员圈内事。
Android高级开发架构技术专栏​zhuanlan.zhihu.com
7b87f9a21b79d9b50db81cb0ba1e51a5.png

Activity.startService()

首先我们知道:要启动一个 Service 的时候,一般都是在 Activity 中通过 startService() 来启动:

// frameworks/base/core/java/android/app/ActivityManager.javapublic class ActivityManager {@UnsupportedAppUsagepublic static IActivityManager getService() {return IActivityManagerSingleton.get();}@UnsupportedAppUsageprivate static final Singleton<IActivityManager> IActivityManagerSingleton =new Singleton<IActivityManager>() {@Overrideprotected IActivityManager create() {final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);final IActivityManager am = IActivityManager.Stub.asInterface(b);return am;}};}

但是我们在 Activity 源码中并没有实现 startService() 方法,那它在哪里被调用的?找不到我们就去 Activity 的父类中找。

// frameworks/base/core/java/android/app/Activity.javapublic class Activity extends ContextThemeWrapperimplements LayoutInflater.Factory2,Window.Callback, KeyEvent.Callback,OnCreateContextMenuListener, ComponentCallbacks2,Window.OnWindowDismissedCallback, WindowControllerCallback,AutofillManager.AutofillClient, ContentCaptureManager.ContentCaptureClient {}// frameworks/base/core/java/android/view/ContextThemeWrapper.javapublic class ContextThemeWrapper extends ContextWrapper {}// frameworks/base/core/java/android/content/ContextWrapper.java
public class ContextWrapper extends Context {}

Activity 继承了 ContextThemeWrapper 类, ContextThemeWrapper 又继承了 ContextWrapper类, ContextWrapper 又继承了 Context 类。

ContextWrapper.startService()

在 ContextWrapper 中实现了 startService() 方法:

// frameworks/base/core/java/android/app/ActivityManager.javapublic class ActivityManager {@UnsupportedAppUsagepublic static IActivityManager getService() {return IActivityManagerSingleton.get();}@UnsupportedAppUsageprivate static final Singleton<IActivityManager> IActivityManagerSingleton =new Singleton<IActivityManager>() {@Overrideprotected IActivityManager create() {final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);final IActivityManager am = IActivityManager.Stub.asInterface(b);return am;}};}

所以 startService() 方法其实是由 ContextWrapper 实现的,紧接着又调用了 mBase.startService() 方法, mBase 对象是 Context 的子类 ContextImpl ,所以调用最终进入 ContextImpl类的 startService() 方法。

ContextImpl.startService()

// frameworks/base/services/core/java/com/android/server/am/ActiveServices.javapublic final class ActiveServices {ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)throws TransactionTooLargeException {return startServiceLocked(caller, service, resolvedType, callingPid, callingUid, fgRequired,callingPackage, userId, false);}ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,int callingPid, int callingUid, boolean fgRequired, String callingPackage,final int userId, boolean allowBackgroundActivityStarts)throws TransactionTooLargeException {... ...// 解析 AndroidManifest.xml 文件中配置 Service 的 intent-filter 相关内容信息ServiceLookupResult res = retrieveServiceLocked(service, null, resolvedType, callingPackage,callingPid, callingUid, userId, true, callerFg, false, false);if (res == null) {return null;}... ...ServiceRecord r = res.record;... ...// 调用 startServiceInnerLocked() 方法ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);return cmp;}}

ActivityManager.getService()

// frameworks/base/core/java/android/app/ActivityManager.javapublic class ActivityManager {@UnsupportedAppUsagepublic static IActivityManager getService() {return IActivityManagerSingleton.get();}@UnsupportedAppUsageprivate static final Singleton<IActivityManager> IActivityManagerSingleton =new Singleton<IActivityManager>() {@Overrideprotected IActivityManager create() {final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);final IActivityManager am = IActivityManager.Stub.asInterface(b);return am;}};}

ActivityManagerService.startService()

接下来就执行到 ActivityManagerService 的 startService() 方法:

// frameworks/base/services/core/java/com/android/server/am/ActiveServices.javapublic final class ActiveServices {private final void realStartServiceLocked(ServiceRecord r,ProcessRecord app, boolean execInFg) throws RemoteException {... ...try {... ...app.thread.scheduleCreateService(r, r.serviceInfo,mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo), app.getReportedProcState());... ...}... ...}}

ActiveServices.startServiceLocked()

// frameworks/base/services/core/java/com/android/server/am/ActiveServices.javapublic final class ActiveServices {ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)throws TransactionTooLargeException {return startServiceLocked(caller, service, resolvedType, callingPid, callingUid, fgRequired,callingPackage, userId, false);}ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,int callingPid, int callingUid, boolean fgRequired, String callingPackage,final int userId, boolean allowBackgroundActivityStarts)throws TransactionTooLargeException {... ...// 解析 AndroidManifest.xml 文件中配置 Service 的 intent-filter 相关内容信息ServiceLookupResult res = retrieveServiceLocked(service, null, resolvedType, callingPackage,callingPid, callingUid, userId, true, callerFg, false, false);if (res == null) {return null;}... ...ServiceRecord r = res.record;... ...// 调用 startServiceInnerLocked() 方法ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);return cmp;}}

通过 retrieveServiceLocked() 方法来解析 AndroidManifest.xml 文件中配置 Service 的 intent-filter 相关内容信息。

当解析完 Service 的 intent-filter 相关内容信息后,解析的结果会保存在 res.record 变量中。而 res 变量是一个 ServiceLookupResult 类型的对象,它的 record 变量则是一个 ServiceRecord 类型对象,用来表示一个 Service 。

ActiveServices.startServiceInnerLocked()

ActiveServices 的 startServiceLocked() 方法最后调用了 startServiceInnerLocked() 方法:

//<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.android.documentsui"><applicationandroid:name=".DocumentsApplication"android:label="@string/app_label"android:icon="@drawable/app_icon"android:supportsRtl="true"android:allowBackup="true"android:backupAgent=".prefs.BackupAgent"android:fullBackupOnly="false"><!-- Run FileOperationService in a separate process so that we can use FileLock class towait until jumbo clip is done writing to disk before reading it. See ClipStorage fordetails. --><serviceandroid:name=".services.FileOperationService"android:exported="false"android:process=":com.android.documentsui.services"></service></application>
</manifest>

ActiveServices.bringUpServiceLocked()

调用 bringUpServiceLocked() 方法进一步处理:

// frameworks/base/core/java/android/app/ActivityThread.javapublic final class ActivityThread extends ClientTransactionHandler {class H extends Handler {public void handleMessage(Message msg) {switch (msg.what) {case CREATE_SERVICE:Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj)));handleCreateService((CreateServiceData)msg.obj);    // 调用 handleCreateService() 方法Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);break;}}}
}

bringUpServiceLocked() 方法首先通过 getProcessRecordLocked() 方法去获取 app 对象,它是一个 ProcessRecord 类型的对象,如果它不为空,说明 Service 要运行的进程已经存在。

Service 运行的进程有两种:

(1)一种是默认的,即运行在 Activity 启动 Service 的那个进程里,也就是说在哪个进程里调用了 startService() 方法,启动的 service 就运行在哪个进程里。

(2)一种是给 Service 一个单独的进程运行,比如在 AndroidManifest 文件里配置了如下内容:

//<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.android.documentsui"><applicationandroid:name=".DocumentsApplication"android:label="@string/app_label"android:icon="@drawable/app_icon"android:supportsRtl="true"android:allowBackup="true"android:backupAgent=".prefs.BackupAgent"android:fullBackupOnly="false"><!-- Run FileOperationService in a separate process so that we can use FileLock class towait until jumbo clip is done writing to disk before reading it. See ClipStorage fordetails. --><serviceandroid:name=".services.FileOperationService"android:exported="false"android:process=":com.android.documentsui.services"></service></application>
</manifest>

在这段配置里有 android:process="xxx" 声明,这个声明用来实现 service 单独运行在 "xxx" 进程里。这样做的好处是, 即使应用程序无法工作,由于 service 单独运行在一个进程里,所以会继续工作 。

回到前面的方法,如果变量 app 为空,就代表 service 要运行的进程还没有启动,于是调用 startProcessLocked() 方法去启动一个新的进程。

如果 app 不为空,采用默认的方式启动 Service,最终调用到 realStartServiceLocked() 方法:

ActiveServices.realStartServiceLocked()

// frameworks/base/services/core/java/com/android/server/am/ActiveServices.javapublic final class ActiveServices {private final void realStartServiceLocked(ServiceRecord r,ProcessRecord app, boolean execInFg) throws RemoteException {... ...try {... ...app.thread.scheduleCreateService(r, r.serviceInfo,mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo), app.getReportedProcState());... ...}... ...}}

在这个方法中,app 对象的 thread 变量是一个 ApplicationThread Binder 对象,调用它的scheduleCreateService() 方法之后,会进入客户端的 ActivityThread 中。

ActivityThread.scheduleCreateService()

// frameworks/base/core/java/android/app/ActivityThread.javapublic final class ActivityThread extends ClientTransactionHandler {// ApplicationThread 是一个 Binderprivate class ApplicationThread extends IApplicationThread.Stub {public final void scheduleCreateService(IBinder token,ServiceInfo info, CompatibilityInfo compatInfo, int processState) {updateProcessState(processState, false);CreateServiceData s = new CreateServiceData();s.token = token;s.info = info;s.compatInfo = compatInfo;sendMessage(H.CREATE_SERVICE, s);}}}

ApplicationThread 的 scheduleCreateService() 方法通过调用 sendMessage() 方法来发送一个 msg 消息 ,当 Handler 接收到了 msg 消息之后,它会调用 handleCreateService() 方法来做进一步处理。

H.handleMessage()

// frameworks/base/core/java/android/app/ActivityThread.javapublic final class ActivityThread extends ClientTransactionHandler {class H extends Handler {public void handleMessage(Message msg) {switch (msg.what) {case CREATE_SERVICE:Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj)));handleCreateService((CreateServiceData)msg.obj);    // 调用 handleCreateService() 方法Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);break;}}}
}

ActivityThread.handleCreateService()

// frameworks/base/core/java/android/app/ActivityThread.javapublic final class ActivityThread extends ClientTransactionHandler {private void handleCreateService(CreateServiceData data) {... ...LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo, data.compatInfo);Service service = null;try {java.lang.ClassLoader cl = packageInfo.getClassLoader();service = packageInfo.getAppFactory().instantiateService(cl, data.info.name, data.intent);} catch (Exception e) {if (!mInstrumentation.onException(service, e)) {throw new RuntimeException("Unable to instantiate service " + data.info.name+ ": " + e.toString(), e);}}try {if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);ContextImpl context = ContextImpl.createAppContext(this, packageInfo);context.setOuterContext(service);Application app = packageInfo.makeApplication(false, mInstrumentation);service.attach(context, this, data.info.name, data.token, app, ActivityManager.getService());service.onCreate();    // 进入 Service.onCreate() 方法mServices.put(data.token, service);try {ActivityManager.getService().serviceDoneExecuting(data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}} catch (Exception e) {if (!mInstrumentation.onException(service, e)) {throw new RuntimeException("Unable to create service " + data.info.name+ ": " + e.toString(), e);}}}}

在 ActivityThread 类的 handleCreateService() 方法中,首先通过 ClassLoader 类把 Service 加载进来,而参数 data.info.name 表示这个 Service 的名字, instantiateService() 方法是创建一个 Service 实例。接着, 创建一个 Context 对象 ,作为上下文环境之用。

handleCreateService() 方法最后调用了 service 的 onCreate() 方法 ,当这个方法被调用之后, 就会进入应用程序里 Service 的 onCreate() 方法 。

至此,Service 的启动就分析完毕,这个过程与启动 Activity 相比简单了很多。

总结

看到最后,希望这篇文章能帮你梳理清楚 “Service 的启动流程”

我整理了一些Activity的资料,如果你有需要可以私信我获取

28fe5bcd95941f4e1523a4f82dd12c60.png

还有Android学习PDF+架构视频+面试文档+源码笔记,高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这些都是我闲暇还会反复翻阅的精品资料

总之也是在这里帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习

如果你有需要的话,可以点赞+评论关注我,然后私信我【学习】获取

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/472121.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

天池 在线编程 输入流

文章目录1. 题目2. 解题1. 题目 描述 给出两个输入流 inputA 和 inputB(包含退格符), 如果两个输入流最后的结果相等&#xff0c;输出 YES&#xff0c;否则输出 NO。 输入字符只包括小写字母和<。 输入流长度不超过10000。示例 样例1 输入: inputA "abcde<<&q…

vim使用教程(全)-最好的编辑器 2015-04-09 18:26 40人阅读 评论(0) 收藏...

vim的学习曲线相当的大&#xff0c;所以&#xff0c;如果你一开始看到的是一大堆VIM的命令分类&#xff0c;你一定会对这个编辑器失去兴趣的。下面的文章翻译自《Learn Vim Progressively》&#xff0c;我觉得这是给新手最好的VIM的升级教程了&#xff0c;没有列举所有的 vim的…

天池 在线编程 最大子树(自底向上)

文章目录1. 题目2. 解题1. 题目 描述 给你一棵二叉树&#xff0c;找二叉树中的一棵子树&#xff0c;他的所有节点之和最大。 返回这棵子树的根节点。 我会把你返回的节点作为最优子树的树根来打印。 数据保证有且仅有唯一的解。 示例 样例 1: 输入: {1,-5,2,0,3,-4,-5} 输出…

sql随机抽取数据50条_MySQL中随机抽样

2.9 随机抽样在审计或IT审计中&#xff0c;常常使用抽样&#xff0c;也就是对具有审计相关性的总体中低于百分之百的项目实施审计程序&#xff0c;使所有抽样单元都有被选取的机会&#xff0c;为注册会计师针对整个总体得出结论提供合理基础。简单来说&#xff0c;就是我们需要…

spring手动回滚事务_Spring总结---gt;03

什么是事务逻辑上的一组操作&#xff0c;组成这组操作的各个单元&#xff0c;要么全都成功&#xff0c;要么全都失败。事务的特性原子性&#xff1a;事务不可分割一致性&#xff1a;事务执行前后数据完整性保持一致隔离性&#xff1a;一个事务的执行不应该受到其他事务的干扰 持…

java-设计模式(结构型)-【代理模式】

1.代理模式(ProxyMode) 定义&#xff1a;外观模式是对类与类之间关系的描述&#xff0c;而代理模式是对原来的方法添加其他的操作。 代理有“网关”的含义&#xff0c;比如用户访问论坛&#xff0c;则要经过代理(网关)授权给你才能访问&#xff0c;即 游客和注册用户的权限不同…

mysql建表_128、mysql建表和简单sql

EMPLOYEES, 有四个字段&#xff0c;EMPLOYEE_ID:员工表(主键)、DEPT_ID:部门号、EMPLOYEE_NAME:员工姓名、EMPLOYEE_SALARY:员工工资。建表语句CREATE TABLE EMPLOYEES(EMPLOYEE_ID int not null primary key,DEPT_ID int,EMPLOYEE_NAME char(40),EMPLOYEE_SALARY double);检索…

LeetCode 1893. 检查是否区域内所有整数都被覆盖(差分)

文章目录1. 题目2. 解题1. 题目 给你一个二维整数数组 ranges 和两个整数 left 和 right 。每个 ranges[i] [starti, endi] 表示一个从 starti 到 endi 的 闭区间 。 如果闭区间 [left, right] 内每个整数都被 ranges 中 至少一个 区间覆盖&#xff0c;那么请你返回 true &a…

数据中台建设与应用_Gartner对于建设数据中台的建议

(文&#xff1a;Gartner高级研究总监 孙鑫)数据中台是中国本土诞生的一个名词&#xff0c;很多企业在“什么是数据中台”和“我要上XX中台”徘徊。其炒作程度跟当年的“大数据” 一词有的一拼&#xff0c;如果用Gartner的炒作周期图来看&#xff0c;数据中台目前已经逼近炒作的…

LeetCode 1894. 找到需要补充粉笔的学生编号

文章目录1. 题目2. 解题1. 题目 一个班级里有 n 个学生&#xff0c;编号为 0 到 n - 1 。 每个学生会依次回答问题&#xff0c;编号为 0 的学生先回答&#xff0c;然后是编号为 1 的学生&#xff0c;以此类推&#xff0c;直到编号为 n - 1 的学生&#xff0c;然后老师会重复这…

C# Excel处理工具

需求&#xff1a;选择一个Excel文件&#xff0c;然后对该Excel文件进行处理&#xff0c;再导出一个处理后的Excel文件。 效果图 声明&#xff1a;我对winform开发不熟&#xff0c;但是我看到许多开发人员做东西只管交差&#xff0c;从不考虑用户体验&#xff0c;也不考虑容错处…

mysql 坚向变横向_(排班表一)使用SQL语句使数据从坚向排列转化成横向排列

知识重点&#xff1a;1.extract(day from schedule01::timestamp)13Extract 属于 SQL 的 DML(即数据库管理语言)函数&#xff0c;同样&#xff0c;InterBase 也支持 Extract&#xff0c;它主要用于从一个日期或时间型的字段内抽取年、月、日、时、分、秒数据&#xff0c;因此&a…

LeetCode 1897. 重新分配字符使所有字符串都相等

文章目录1. 题目2. 解题1. 题目 给你一个字符串数组 words&#xff08;下标 从 0 开始 计数&#xff09;。 在一步操作中&#xff0c;需先选出两个 不同 下标 i 和 j&#xff0c;其中 words[i] 是一个非空字符串&#xff0c;接着将 words[i] 中的 任一 字符移动到 words[j] 中…

2015/4/14课堂练习

1.设计题目 书店针对《哈利波特》系列书籍进行促销活动&#xff0c;一共5卷&#xff0c;用编号0、1、2、3、4表示&#xff0c;单独一卷售价8元&#xff0c; 具体折扣如下所示&#xff1a; 本     数量 折扣 …

python 图片背景前景分离_【绝了】用 Python 把朋友头像变表情包!

正文在日常生活中&#xff0c;我们经常会存取一些朋友们的丑照&#xff0c;在这个项目中&#xff0c;我们以萌萌哒的熊猫头作为背景&#xff0c;然后试着在背景图上加入朋友们的照片&#xff0c;效果如下图所示。实现步骤导入朋友的照片(前景照片)&#xff1b;处理前景照片(缩放…

LeetCode 1899. 合并若干三元组以形成目标三元组

文章目录1. 题目2. 解题1. 题目 三元组 是一个由三个整数组成的数组。 给你一个二维整数数组 triplets &#xff0c;其中 triplets[i] [ai, bi, ci] 表示第 i 个 三元组 。 同时&#xff0c;给你一个整数数组 target [x, y, z] &#xff0c;表示你想要得到的 三元组 。 为了…

openpyxl 读写 excel

import openpyxl book openpyxl.load_workbook(b.xlsx) # 读取 # sheet book["name_tab"] sheet book.active # rowsheet.max_row # 行数 columnsheet.max_column # 列数 print(row) print(column) for r in range(2, row1): # 跳过表头&#xff0c;行号是从1开…

opengl计算帧率_或许是迄今为止第一篇讲解 fps 计算原理的文章吧

前言fps&#xff0c;是 frames per second 的简称&#xff0c;也就是我们常说的“帧率”。在游戏领域中&#xff0c;fps 作为衡量游戏性能的基础指标&#xff0c;对于游戏开发和手机 vendor 厂商都是非常重要的数据&#xff0c;而计算游戏的 fps 也成为日常测试的基本需求。目前…

041、基于CNN的样式迁移

之——基于CNN的滤镜 目录 之——基于CNN的滤镜 杂谈 正文 1.基于CNN的样式迁移 2.实现 杂谈 通过CNN的特征提取&#xff0c;可以实现将一个图片的样式模式特征迁移到另一张图像上。 正文 1.基于CNN的样式迁移 就是在某些层的输出上用其他的图片进行监督。 2.实现 一般来…

LeetCode 483. 最小好进制(二分查找)

文章目录1. 题目2. 解题1. 题目 对于给定的整数 n, 如果 n 的 k&#xff08;k>2&#xff09;进制数的所有数位全为1&#xff0c;则称 k&#xff08;k>2&#xff09;是 n 的一个好进制。 以字符串的形式给出 n, 以字符串的形式返回 n 的 最小 好进制。 示例 1&#xff…