Service启动流程(startService)

2023-10-11 21:48

本文主要是介绍Service启动流程(startService),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前面分析了Activity的启动流程,下面开始分析Service的启动,本文分析的是startService的流程,android的版本为4.1.2

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

这就是startService的入口,mUser就是代表当前的用户,在后面启动Serivce的流程中,会通过此实例获取用户的uid。

private ComponentName startServiceCommon(Intent service, UserHandle user) {try {......ComponentName cn = ActivityManagerNative.getDefault().startService(mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(getContentResolver()), getOpPackageName(), 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());}}return cn;} catch (RemoteException e) {throw e.rethrowFromSystemServer();}

接下来通过binder通信,Service启动流程会进入ActivityManagerService的startService函数中。

public ComponentName startService(IApplicationThread caller, Intent service,String resolvedType) {......synchronized(this) {final int callingPid = Binder.getCallingPid();final int callingUid = Binder.getCallingUid();final long origId = Binder.clearCallingIdentity();ComponentName res = startServiceLocked(caller, service,resolvedType, callingPid, callingUid);Binder.restoreCallingIdentity(origId);return res;}
}

接下来会调用startServiceLockedstartServiceLocked的逻辑稍微显得复杂一些,主要的难点是获取Service信息。

ComponentName startServiceLocked(IApplicationThread caller,Intent service, String resolvedType,int callingPid, int callingUid) {synchronized(this) {......ServiceLookupResult res =retrieveServiceLocked(service, resolvedType,callingPid, callingUid);if (res == null) {return null;}if (res.record == null) {return new ComponentName("!", res.permission != null? res.permission : "private to package");}ServiceRecord r = res.record;//检测当前用户是否有权限访问Intent中的Data和ClipDataint targetPermissionUid = checkGrantUriPermissionFromIntentLocked(callingUid, r.packageName, service);//将即将启动的Service从等待启动列表中删除if (unscheduleServiceRestartLocked(r)) {if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: " + r);}......if (!bringUpServiceLocked(r, service.getFlags(), false)) {return new ComponentName("!", "Service process is bad");}return r.name;}
}  

系统首先调用retriveServiceLocked 函数获取即将要启动的Service信息,并封装成ServiceLookupResult对象,ServiceLoopupResult对象内部包括了一个ServiceRecord对象和记录Service权限的字符串,retrivveServiceLocked函数的执行过程会在后面分析。获得ServiceLookupResult结果res后会进行判断,如果未找到即将启动的Service的ServiceLookupResult对象,则直接返回;接下来会将即将启动的Service从等待启动列表中删除,最终会调用会进入bringupServiceLockedbringupServiceLocked函数中包含了ActivityManagerService对Service进行不同配置以及系统处于不同状态的不同启动方式。

private final boolean bringUpServiceLocked(ServiceRecord r,int intentFlags, boolean whileRestarting) {//Slog.i(TAG, "Bring up service:");//r.dump("  ");//Service已经运行,则执行Service的onStartCommand函数if (r.app != null && r.app.thread != null) {sendServiceArgsLocked(r, false);return true;}//如果正在等待重启,则直接返回if (!whileRestarting && r.restartDelay > 0) {// If waiting for a restart, then do nothing.return true;}//从正在重启的服务列表中删除即将启动的服务mRestartingServices.remove(r);//服务正在启动,当前应用不能被停止try {AppGlobals.getPackageManager().setPackageStoppedState(r.packageName, false, r.userId);} catch (RemoteException e) {} catch (IllegalArgumentException e) {Slog.w(TAG, "Failed trying to unstop package "+ r.packageName + ": " + e);}final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;final String appName = r.processName;ProcessRecord app;//如果Service不需要运行在单独的进程if (!isolated) {//尝试获取Service的进程app = getProcessRecordLocked(appName, r.appInfo.uid);if (DEBUG_MU)Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid + " app=" + app);//如果Service所在进程已经启动,但是Service还未启动,则去执行realStartServiceLocked,在进程内执行Service的启动流程if (app != null && app.thread != null) {try {app.addPackage(r.appInfo.packageName);realStartServiceLocked(r, app);return true;} catch (RemoteException e) {Slog.w(TAG, "Exception when starting service " + r.shortName, e);}// If a dead object exception was thrown -- fall through to// restart the application.}} else {// If this service runs in an isolated process, then each time// we call startProcessLocked() we will get a new isolated// process, starting another process if we are currently waiting// for a previous process to come up.  To deal with this, we store// in the service any current isolated process it is running in or// waiting to have come up.app = r.isolatedProc;}// Not running -- get it started, and enqueue this service record// to be executed when the app comes up.//Service所在的进程未启动,则启动进程if (app == null) {//如果Service所在进程并没有被启动,则根据Service信息去启动一个新的进程,新进程启动完成后则去启动Serviceif ((app=startProcessLocked(appName, r.appInfo, true, intentFlags,"service", r.name, false, isolated)) == null) {Slog.w(TAG, "Unable to launch app "+ r.appInfo.packageName + "/"+ r.appInfo.uid + " for service "+ r.intent.getIntent() + ": process is bad");bringDownServiceLocked(r, true);return false;}if (isolated) {r.isolatedProc = app;}}//将即将启动的Service添加到pending队列中if (!mPendingServices.contains(r)) {mPendingServices.add(r);}return true;
}

这部分的代码描述了调用startService时可能遇到的三种情况:

  • 目标Service已经启动,则去执行onStartCommand
  • 目标Service未启动,但是Service所在的进程已经启动
  • 目标Service未启动且Service所在进程未启动,需要新建进程

本篇的分析到此结束,接下来我会分3篇文章分别取分析这三种不同的启动方式。

这篇关于Service启动流程(startService)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/191036

相关文章

redis-sentinel基础概念及部署流程

《redis-sentinel基础概念及部署流程》RedisSentinel是Redis的高可用解决方案,通过监控主从节点、自动故障转移、通知机制及配置提供,实现集群故障恢复与服务持续可用,核心组件包... 目录一. 引言二. 核心功能三. 核心组件四. 故障转移流程五. 服务部署六. sentinel部署

SpringBoot集成XXL-JOB实现任务管理全流程

《SpringBoot集成XXL-JOB实现任务管理全流程》XXL-JOB是一款轻量级分布式任务调度平台,功能丰富、界面简洁、易于扩展,本文介绍如何通过SpringBoot项目,使用RestTempl... 目录一、前言二、项目结构简述三、Maven 依赖四、Controller 代码详解五、Service

SpringBoot通过main方法启动web项目实践

《SpringBoot通过main方法启动web项目实践》SpringBoot通过SpringApplication.run()启动Web项目,自动推断应用类型,加载初始化器与监听器,配置Spring... 目录1. 启动入口:SpringApplication.run()2. SpringApplicat

解决Nginx启动报错Job for nginx.service failed because the control process exited with error code问题

《解决Nginx启动报错Jobfornginx.servicefailedbecausethecontrolprocessexitedwitherrorcode问题》Nginx启... 目录一、报错如下二、解决原因三、解决方式总结一、报错如下Job for nginx.service failed bec

MySQL 临时表与复制表操作全流程案例

《MySQL临时表与复制表操作全流程案例》本文介绍MySQL临时表与复制表的区别与使用,涵盖生命周期、存储机制、操作限制、创建方法及常见问题,本文结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随小... 目录一、mysql 临时表(一)核心特性拓展(二)操作全流程案例1. 复杂查询中的临时表应用2. 临时

Spring Boot项目如何使用外部application.yml配置文件启动JAR包

《SpringBoot项目如何使用外部application.yml配置文件启动JAR包》文章介绍了SpringBoot项目通过指定外部application.yml配置文件启动JAR包的方法,包括... 目录Spring Boot项目中使用外部application.yml配置文件启动JAR包一、基本原理

解决若依微服务框架启动报错的问题

《解决若依微服务框架启动报错的问题》Invalidboundstatement错误通常由MyBatis映射文件未正确加载或Nacos配置未读取导致,需检查XML的namespace与方法ID是否匹配,... 目录ruoyi-system模块报错报错详情nacos文件目录总结ruoyi-systnGLNYpe

MySQL 升级到8.4版本的完整流程及操作方法

《MySQL升级到8.4版本的完整流程及操作方法》本文详细说明了MySQL升级至8.4的完整流程,涵盖升级前准备(备份、兼容性检查)、支持路径(原地、逻辑导出、复制)、关键变更(空间索引、保留关键字... 目录一、升级前准备 (3.1 Before You Begin)二、升级路径 (3.2 Upgrade

解决hive启动时java.net.ConnectException:拒绝连接的问题

《解决hive启动时java.net.ConnectException:拒绝连接的问题》Hadoop集群连接被拒,需检查集群是否启动、关闭防火墙/SELinux、确认安全模式退出,若问题仍存,查看日志... 目录错误发生原因解决方式1.关闭防火墙2.关闭selinux3.启动集群4.检查集群是否正常启动5.

Springboot项目启动失败提示找不到dao类的解决

《Springboot项目启动失败提示找不到dao类的解决》SpringBoot启动失败,因ProductServiceImpl未正确注入ProductDao,原因:Dao未注册为Bean,解决:在启... 目录错误描述原因解决方法总结***************************APPLICA编