展开说说: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操作完成或执行耗时任务的情况。在传统的同步编程中,代码会按照顺序逐行执行,直到遇到一个耗时操作,它会阻塞程序的执行直到…...
Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...
无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...
令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...
网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
