展开说说:Android服务之startService源码解析
通过上一篇文章我们掌握了Android四种的基本使用,本篇从源码层面总结一下startService的执行过程。
本文依然按着是什么?有什么?怎么用?啥原理?的步骤来分析。
1、是什么
上一篇总结了“Service是Android系统中的四大组件之一,它是一种没有可视化界面,运行于后台的一种服务程序。属于计算型组件,用来在后台执行持续性的计算任务,重要性仅次于Activity活动”。
2、有什么
Service和Activity一样也有自己的生命周期,也需要在AndroidManifest.xml中注册。
2.1 在AndroidManifest.xml中注册
<service android:name="com.example.testdemo.service.ServiceJia" />
2.2 Service生命周期
Service的生命周期有很多,本文只谈startService和stopService涉及到的。
onCreate
它只在Service刚被创建的时刻被调用,Service在运行中,这个方法将不会被调用。也就是只有经历过onDestroy生命周期以后再次startService(intent) 才会执行。
onStartCommand
OnStartCommand方法是最重要的方法,因为它在我们需要启动Service的时候被调用。形参时当初startService时传递进来的Intent,这样就可以给Service传值。在这个方法中,可以做服务启动后要执行的任务,但是切记这里也是在主线程运行,耗时的操作必须创建一个子线程来执行,否则可能引发ANR导致程序闪退。
关于onStartCommand的返回值介绍:
START_STICKY:此时Service被杀死以后将会被重新创建。但是不会重新传入原来的Intent对象,而是传入intent为null。
START_NOT_STICKY:此时Service被杀死以后不会被重新创建。
START_REDELIVER_INTENT:功能与START_STICKY类似在Service被杀死以后将会被重新创建。厉害的一点时,该返回值时Intent会重新传递给Service。
onDestroy
onDestory是在Service即将被销毁时执行的生命名周期,Service和Activity生命周期不一样,Service没有onStop生命周期。
日志打印:
调用startService后的生命周期:
2024-07-01 10:20:59.756 20505-20505/com.example.testdemo3 E/com.example.testdemo3.service.ServiceJia: onCreate:
2024-07-01 10:20:59.757 20505-20505/com.example.testdemo3 E/com.example.testdemo3.service.ServiceJia: onStartCommand:
调用stopService后的生命周期:
2024-07-01 10:21:06.861 20505-20505/com.example.testdemo3 E/com.example.testdemo3.service.ServiceJia: onDestroy:
- 怎么用
关于使用方法上一篇已经总结,这里不在赘述。
4、啥原理
Service的启动方法是调用
Intent serviceIntent = new Intent(ServiceActivity.this, ServiceJia.class);
startService(serviceIntent);
然后我们顺着startService方法开始解析源码,SDK版本API 30:
4.1 从ContexWrapper的startService开始:
@Override
public ComponentName startService(Intent service) {return mBase.startService(service);
}
4.2 ContextImpl类startService
mBase的类型是Context,但实际代码逻辑是在它的实现类ContextImpl类。
@Overridepublic ComponentName startService(Intent service) {warnIfCallingFromSystemProcess();return startServiceCommon(service, false, mUser);
}
//private ComponentName startServiceCommon(Intent service, boolean requireForeground,UserHandle user) {try {validateServiceIntent(service);service.prepareToLeaveProcess(this);ComponentName cn = ActivityManager.getService().startService(mMainThread.getApplicationThread(), service,service.resolveTypeIfNeeded(getContentResolver()), requireForeground,getOpPackageName(), getAttributionTag(), user.getIdentifier());if (cn != null) {if (cn.getPackageName().equals("!")) {throw new SecurityException("Not allowed to start service " + service+ " without permission " + cn.getClassName());} else if (cn.getPackageName().equals("!!")) {throw new SecurityException("Unable to start service " + service+ ": " + cn.getClassName());} else if (cn.getPackageName().equals("?")) {throw new IllegalStateException("Not allowed to start service " + service + ": " + cn.getClassName());}}return cn;} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}
在ContextImpl类的startService中调用了startServiceCommon方法,而其中的关键代码是ActivityManager.getService().startService方法调用。
4.3 来到ActivityManager类
/*** @hide*/@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.java@Overridepublic ComponentName startService(IApplicationThread caller, Intent service,String resolvedType, boolean requireForeground, String callingPackage,String callingFeatureId, int userId)throws TransactionTooLargeException {enforceNotIsolatedCaller("startService");// Refuse possible leaked file descriptorsif (service != null && service.hasFileDescriptors() == true) {throw new IllegalArgumentException("File descriptors passed in Intent");}if (callingPackage == null) {throw new IllegalArgumentException("callingPackage cannot be null");}if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,"*** startService: " + service + " type=" + resolvedType + " fg=" + requireForeground);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, callingFeatureId, userId);} finally {Binder.restoreCallingIdentity(origId);}return res;}
}
4.4 进入ActiveServices类,用来辅助ActivityServiceManager管理Service的启动和停止等。
mServices = new ActiveServices(this);
上面先调用ActiveServices的startServiceLocked方法;
res = mServices.startServiceLocked(caller, service,resolvedType, callingPid, callingUid,requireForeground, callingPackage, callingFeatureId, userId);
然后startServiceLocked又调用本类的startServiceInnerLocked方法:
ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
然后startServiceInnerLocked又调用本类的bringUpServiceLocked方法:
String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
然后在调用bringUpServiceLocked方法realStartServiceLocked:
realStartServiceLocked(r, app, execInFg);
4.5调用ApplicationThread的scheduleCreateService方法
app.thread.scheduleCreateService(r, r.serviceInfo,mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),app.getReportedProcState());
之后调用了sendServiceArgsLocked(r, execInFg, true);他就是onStartCommand生命周期,此处伏笔,下面4.8中详聊。
4.6 进入ApplicationThread类:
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);}
这里sendMessage方法不是Handler的哈,是封装以后的,继续看:
void sendMessage(int what, Object obj) {sendMessage(what, obj, 0, 0, false);
}private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {if (DEBUG_MESSAGES) {Slog.v(TAG,"SCHEDULE " + what + " " + mH.codeToString(what) + ": " + arg1 + " / " + obj);}Message msg = Message.obtain();msg.what = what;msg.obj = obj;msg.arg1 = arg1;msg.arg2 = arg2;if (async) {msg.setAsynchronous(true);}mH.sendMessage(msg);}
4.7 这里发送了消息,然后去找对应what = H.CREATE_SERVICE的处理:
在handleMessage方法中处理:case CREATE_SERVICE:if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,("serviceCreate: " + String.valueOf(msg.obj)));}handleCreateService((CreateServiceData)msg.obj);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);break;
关键时刻来咯:
@UnsupportedAppUsageprivate void handleCreateService(CreateServiceData data) {// If we are getting ready to gc after going to the background, well// we are back active so skip it.unscheduleGcIdler();LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo, data.compatInfo);Service service = null;try {if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);ContextImpl context = ContextImpl.createAppContext(this, packageInfo);Application app = packageInfo.makeApplication(false, mInstrumentation);java.lang.ClassLoader cl = packageInfo.getClassLoader();service = packageInfo.getAppFactory().instantiateService(cl, data.info.name, data.intent);// Service resources must be initialized with the same loaders as the application// context.context.getResources().addLoaders(app.getResources().getLoaders().toArray(new ResourcesLoader[0]));context.setOuterContext(service);service.attach(context, this, data.info.name, data.token, app,ActivityManager.getService());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);}}}
这里先创建了一个ContextImpl实例,然后创建了Application(这里是个工厂模式等于空了才创建);创建Service实例并与ContextImpl关联;然后调用onCreate的生命周期方法。最后有个mServices.put,后面其他场景会从里面取来使用。
4.8再看onStartCommand生命周期
先生是否记得上面4.4小节买过伏笔sendServiceArgsLocked(r, execInFg, true);
sendServiceArgsLocked的r.app.thread.scheduleServiceArgs(r, slice);这行代码很熟悉了,和调用onCreate时候是一样的模式。
进入ApplicationThread类:
public final void scheduleServiceArgs(IBinder token, ParceledListSlice args) {List<ServiceStartArgs> list = args.getList();for (int i = 0; i < list.size(); i++) {ServiceStartArgs ssa = list.get(i);ServiceArgsData s = new ServiceArgsData();s.token = token;s.taskRemoved = ssa.taskRemoved;s.startId = ssa.startId;s.flags = ssa.flags;s.args = ssa.args;sendMessage(H.SERVICE_ARGS, s);}}
二次封装的发消息:
sendMessage(H.SERVICE_ARGS, s);和onCreate一样。
handleMessage处理消息:
case SERVICE_ARGS:if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,("serviceStart: " + String.valueOf(msg.obj)));}handleServiceArgs((ServiceArgsData)msg.obj);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);break;
从mServices取出onCreate是存的service,然后调用onStartCommand生命周期。
private void handleServiceArgs(ServiceArgsData data) {Service s = mServices.get(data.token);if (s != null) {try {if (data.args != null) {data.args.setExtrasClassLoader(s.getClassLoader());data.args.prepareToEnterProcess();}int res;if (!data.taskRemoved) {res = s.onStartCommand(data.args, data.flags, data.startId);} else {s.onTaskRemoved(data.args);res = Service.START_TASK_REMOVED_COMPLETE;}QueuedWork.waitToFinish();try {ActivityManager.getService().serviceDoneExecuting(data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}} catch (Exception e) {if (!mInstrumentation.onException(s, e)) {throw new RuntimeException("Unable to start service " + s+ " with " + data.args + ": " + e.toString(), e);}}}}
至此StartService的启动该流程分析完毕!
才疏学浅,如有错误,欢迎指正,多谢。
相关文章:

展开说说:Android服务之startService源码解析
通过上一篇文章我们掌握了Android四种的基本使用,本篇从源码层面总结一下startService的执行过程。 本文依然按着是什么?有什么?怎么用?啥原理?的步骤来分析。 1、是什么 上一篇总结了“Service是Android系统中的四…...

Java + MySQL 实现存储完整 Json
Java MySQL 实现存储完整 Json 一、应用场景二、数据库配置三、后端代码配置1、maven 依赖2、实体类3、Service 实现类4、xml 文件 四、测试1、新增接口2、查询接口3、数据表内容 一、应用场景 将前端传过来的 Json 完整存储到 MySQL 中,涉及技术栈为 Java、MyBat…...

解决刚申请下来的AWS EC2,无法用finalshell连接的问题
在AWS的命令页面创建一个root用户 切换到root 模式,输入密码 su root 不知道密码的可以使用一下命令来设置root用户的密码: su passwd root 再切换到root用户 su 修改配置文件 输入 vim /etc/ssh/sshd_config进入文件,键入’i’ ,进行…...

如何在PD虚拟机中开启系统的嵌套虚拟化功能?pd虚拟机怎么用 Parallels Desktop 19 for Mac
PD虚拟机是一款可以在Mac电脑中运行Windows系统的应用软件。使用 Parallels Desktop for Mac 体验 macOS 和 Windows 的最优性能,解锁强大性能和无缝交互。 在ParallelsDesktop(PD虚拟机)中如何开启系统的嵌套虚拟化功能?下面我们…...

vue中实现button按钮的重复点击指令
// 注册一个全局自定义指令 v-debounce Vue.directive(debounce, {// 当被绑定的元素插入到 DOM 中时...inserted: function (el, binding) {let timer;el.addEventListener(click, () > {clearTimeout(timer);timer setTimeout(() > {binding.value(); // 调用传给指令…...

智能与伦理:Kimi与学术道德的和谐共舞
学境思源,一键生成论文初稿: AcademicIdeas - 学境思源AI论文写作 Kimi,由月之暗面科技有限公司开发的智能助手,擅长中英文对话,能处理多种文档和网页内容。在论文写作中,Kimi可提供资料查询、信息整理、语…...

mac有什么解压软件 mac怎么解压7z软件 苹果电脑好用的压缩软件有哪些
众所周知,macOS集成解压缩软件归档实用工具,可直接解压zip格式的压缩包。但对于其他比较常见的格式,诸如RAR、7z、TAR等,则无能为力,不过,我们可以选择大量第三方解压缩软件,帮助我们更好地完成…...

C语言_练习题
求最小公倍数 思路:假设两个数,5和7,那么最小至少也要7吧,所以先假定最小公倍数是两个数之间较大的,然后看7能不能同时整除5和7,不能就加1继续除 int GetLCM(int _num1, int _num2) {int max _num1>_n…...

Type-C接口快充取电技术的实现
Type-C接口快充取电技术的实现 Type-C接口快充取电技术主要通过USB PD(Power Delivery)协议实现。这种技术利用了Type-C接口的物理特性和PD协议的智能性,实现了高效、安全、快速的充电过程。具体实现过程如下: 接口连接与检测&a…...

压测工具---Ultron
压测工具:Ultron 类型:接口级和全链路 接口级 对于接口级别的压测我们可以进行 http接口压测、thrift压测、redis压测、kafka压测、DDMQ压测、MySQL压测等,选对对应的业务线、选择好压测执行的时间和轮数就可以执行压测操作了 全链路 对…...

Kubernetes 负载均衡器解决方案 MetalLB实践
Kubernetes 负载均衡解决方案 MetalLB实践 MetalLB 是一个用于在 Kubernetes 集群中提供外部 IP 地址的负载均衡器实现。 准备工作 1. 安装需知 安装 MetalLB 有一些前置条件: Kubernetesv1.13.0 或者更新的版本 集群中的 CNI 要能兼容 MetalLB,最新…...

力扣爆刷第159天之TOP100五连刷61-65(翻转单词、对称二叉树、遍历求和)
力扣爆刷第159天之TOP100五连刷61-65(翻转单词、对称二叉树、遍历求和) 文章目录 力扣爆刷第159天之TOP100五连刷61-65(翻转单词、对称二叉树、遍历求和)一、151. 反转字符串中的单词二、129. 求根节点到叶节点数字之和三、104. 二…...

(七)[重制]C++命名空间与标准模板库(STL)
引言 在专栏C教程的第六篇C中的结构体与联合体中,介绍了C中的结构体和联合体,包括它们的定义、初始化、内存布局和对齐,以及作为函数参数和返回值的应用。在专栏C教程的第七篇中,我们将深入了解C中的命名空间(nam…...

Elasticsearch:Runtime fields - 运行时字段(一)
目录 使用运行时字段带来的好处 激励 折衷 映射运行时字段 定义运行时字段而不使用脚本 忽略运行时字段上的脚本错误 更新和删除运行时字段 在搜索请求中定义运行时字段 创建使用其他运行时字段的运行时字段 运行时字段(runtime fields)是在查询…...

03:C语言运算符
C语言运算符 1、常见运算符2、赋值运算符3、判断运算符4、与- - 1、常见运算符 数学运算符号。常见数学运算符号,跟数学中理解相同 加号 - 减号 * 乘号 / 除号,相除以后的商 % 取余符号,相除以后余数是几 ()括号括起来优先级最高࿰…...

JAVA每日作业day7.4
ok了家人们今天学习了Date类和simpleDateformat类,话不多说我们一起看看吧 一.Date类 类 java.util.Date 表示特定的瞬间 ( 日期和时间 ) ,精确到毫秒。 1.2 Date类的构造方法 public Date(): 用来创建当前系统时间对应的日期对象。 public Date(long …...

WordPress网站违法关键词字过滤插件下载text-filter
插件下载地址:https://www.wpadmin.cn/2025.html 插件介绍 WordPress网站违法关键词字过滤插件text-filter由本站原创开发,支持中英文关键字自动替换成**号,可以通过自定义保存修改按钮增加“预设关键字”,也可以导入定义好的txt文本形式的关…...

ros1仿真导航机器人 navigation
仅为学习记录和一些自己的思考,不具有参考意义。 1navigation导航框架 2导航设置过程 (1)启动仿真环境 roslaunch why_simulation why_robocup.launch (2)启动move_base导航、amcl定位 roslaunch why_simulation nav…...

Python制作动态颜色变换:颜色渐变动效
文章目录 引言准备工作前置条件 代码实现与解析导入必要的库初始化Pygame颜色变换函数主循环 完整代码 引言 颜色渐变动画是一种视觉上非常吸引人的效果,常用于网页设计和图形应用中。在这篇博客中,我们将使用Python创建一个动态颜色变换的动画效果。通…...

Python 异步编程介绍与代码示例
Python 异步编程介绍与代码示例 一、异步编程概述 异步编程是一种编程范式,它旨在处理那些需要等待I/O操作完成或执行耗时任务的情况。在传统的同步编程中,代码会按照顺序逐行执行,直到遇到一个耗时操作,它会阻塞程序的执行直到…...

堆叠的作用
一、为什么要堆叠 传统的园区网络采用设备和链路冗余来保证高可靠性,但其链路利用率低、网络维护成本高,堆叠技术将多台交换机虚拟成一台交换机,达到简化网络部署和降低网络维护工作量的目的。 二、堆叠优势 1、提高可靠性 堆叠系统多台成…...

ubuntu 如何查看某一个网卡的ip地址
在Ubuntu中,你可以使用多种方法来查看某一个网卡的IP地址。以下是一些常用的方法: 使用ip命令: ip命令是现代Linux系统中用于显示和操作路由、网络设备、策略路由和隧道的工具。要查看所有网络接口的IP地址,你可以使用:…...

跨界客户服务:拓展服务边界,创造更多价值
在当今这个日新月异的商业时代,跨界合作已不再是新鲜词汇,它如同一股强劲的东风,吹散了行业间的壁垒,为企业服务创新开辟了前所未有的广阔天地。特别是在客户服务领域,跨界合作正以前所未有的深度和广度,拓…...

linux驱动编程 - kfifo先进先出队列
简介: kfifo是Linux Kernel里面的一个 FIFO(先进先出)数据结构,它采用环形循环队列的数据结构来实现,提供一个无边界的字节流服务,并且使用并行无锁编程技术,即当它用于只有一个入队线程和一个出…...

JS 四舍五入使用整理
一、Number.toFixed() 把数字转换为字符串,结果的小数点后有指定位数的数字,重点返回的数据类型为字符串 toFixed() 方法将一个浮点数转换为指定小数位数的字符串表示,如果小数位数高于数字,则使用 0 来填充。 toFixed() 方法可把 Number 四舍五入为指定小数位数的数字。…...

上万组风电,光伏,用户负荷数据分享
上万组风电,光伏,用户负荷数据分享 可用于风光负荷预测等研究 获取链接🔗 https://pan.baidu.com/s/1izpymx6R3Y8JsFdx42rL0A 提取码:381i 获取链接🔗 https://pan.baidu.com/s/1izpymx6R3Y8JsFdx42rL0A 提取…...

在物联网快速发展的趋势下,Java 怎样优化对低功耗、资源受限的边缘设备的支持,保障物联网应用的稳定运行?
在物联网快速发展的趋势下,Java可以通过以下方式优化对低功耗、资源受限的边缘设备的支持,以保障物联网应用的稳定运行: 精简Java运行环境:针对边缘设备的资源限制,可以使用精简型的Java运行环境,避免不必要…...

java-HashSet 源码分析 1
## 深入分析 Java 中的 HashSet 源码 HashSet 是 Java 集合框架中的一个重要类,它基于哈希表实现,用于存储不重复的元素。HashSet 允许 null 元素,并且不保证元素的顺序。本文将详细分析 HashSet 的源码,包括其数据结构、构造方法…...

K8S 部署 EFK
安装说明 系统版本为 Centos7.9 内核版本为 6.3.5-1.el7 K8S版本为 v1.26.14 ES官网 开始安装 本次安装使用官方ECK方式部署 EFK,部署的是当前的最新版本。 在 Kubernetes 集群中部署 ECK 安装自定义资源 如果能打开这个网址的话直接用这个命令安装,打不开的话…...

AI Earth应用—— 在线使用sentinel数据VV和VH波段进行水体提取分析(昆明抚仙湖、滇池为例)
AI Earth 本文的主要目的就是对水体进行提取,这里,具体的操作步骤很简单基本上是通过,首页的数据检索,选择需要研究的区域,然后选择工具箱种的水体提取分析即可,剩下的就交给阿里云去处理,结果如下: 这是我所选取的一景影像: 详情 卫星: Sentinel-1 级别: 1 …...