startService启动流程---Service在非App进程且未启动

2023-10-11 21:48

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

在 Service启动流程(startService)的最后,分析了在调用startService时可能存在的三种情况,本文分析第三种情况—Service不在App进程且未启动。

Service启动流程(startService)最后已经说明,在这种情况下,系统会执行startProcessLocked函数。startProcessLocked函数是不是很熟悉,在前面分析Activity从Launched中启动的时候,为了启动App,需要调用startProcessLocked函数创建新的进程。

private final void startProcessLocked(ProcessRecord app,String hostingType, String hostingNameStr) {......int debugFlags = 0;if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;// Also turn on CheckJNI for debuggable apps. It's quite// awkward to turn on otherwise.debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;}// Run the app in safe mode if its manifest requests so or the// system is booted in safe mode.if ((app.info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0 ||Zygote.systemInSafeMode == true) {debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;}if ("1".equals(SystemProperties.get("debug.checkjni"))) {debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;}if ("1".equals(SystemProperties.get("debug.jni.logging"))) {debugFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;}if ("1".equals(SystemProperties.get("debug.assert"))) {debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;}// Start the process.  It will either succeed and return a result containing// the PID of the new process, or else throw a RuntimeException.Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",app.processName, uid, uid, gids, debugFlags,app.info.targetSdkVersion, null);......
}  

函数中略去了不影响Process启动的代码,Process.start方法的执行,回去调用ActivityThreadmain方法。

public static void main(String[] args) {......ActivityThread thread = new ActivityThread();thread.attach(false);......
}  

继续看下ActivityThreadattach函数

private void attach(boolean system) {sThreadLocal.set(this);mSystemThread = system;if (!system) {......IActivityManager mgr = ActivityManagerNative.getDefault();try {mgr.attachApplication(mAppThread);} catch (RemoteException ex) {// Ignore}} else {......}......
}  

由于Service进程不是系统进程,故attach会执行以上逻辑,attachApplication函数是跨进程调用,可以在ActivityManagerService中找到这个方法

private final boolean attachApplicationLocked(IApplicationThread thread,int pid) {......try {......thread.bindApplication(processName, appInfo, providers,app.instrumentationClass, profileFile, profileFd, profileAutoStop,app.instrumentationArguments, app.instrumentationWatcher, testMode,enableOpenGlTrace, isRestrictedBackupMode || !normalMode, app.persistent,new Configuration(mConfiguration), app.compat, getCommonServicesLocked(),mCoreSettingsObserver.getCoreSettingsLocked());......} catch (Exception e) {// todo: Yikes!  What should we do?  For now we will try to// start another process, but that could easily get us in// an infinite loop of restarting processes...Slog.w(TAG, "Exception thrown during bind!", e);app.resetPackageList();app.unlinkDeathRecipient();startProcessLocked(app, "bind fail", processName);return false;}......//新建完进程后启动Serviceif (!badApp && mPendingServices.size() > 0) {ServiceRecord sr = null;try {for (int i=0; i<mPendingServices.size(); i++) {sr = mPendingServices.get(i);if (app != sr.isolatedProc && (app.uid != sr.appInfo.uid|| !processName.equals(sr.processName))) {continue;}mPendingServices.remove(i);i--;realStartServiceLocked(sr, app);didSomething = true;}} catch (Exception e) {Slog.w(TAG, "Exception in new application when starting service "+ sr.shortName, e);badApp = true;}}return true;
}  

这部分的代码有启动Service的逻辑,会在执行完新建进程后分析。直接定位到最关键的代码bindApplication,又是一个Binder跨进程调用,最终在ActivityThread中调用。

public final void bindApplication(String processName,ApplicationInfo appInfo, List<ProviderInfo> providers,ComponentName instrumentationName, String profileFile,ParcelFileDescriptor profileFd, boolean autoStopProfiler,Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,int debugMode, boolean enableOpenGlTrace, boolean isRestrictedBackupMode,boolean persistent, Configuration config, CompatibilityInfo compatInfo,Map<String, IBinder> services, Bundle coreSettings) {if (services != null) {// Setup the service cache in the ServiceManagerServiceManager.initServiceCache(services);}setCoreSettings(coreSettings);AppBindData data = new AppBindData();data.processName = processName;data.appInfo = appInfo;data.providers = providers;data.instrumentationName = instrumentationName;data.instrumentationArgs = instrumentationArgs;data.instrumentationWatcher = instrumentationWatcher;data.debugMode = debugMode;data.enableOpenGlTrace = enableOpenGlTrace;data.restrictedBackupMode = isRestrictedBackupMode;data.persistent = persistent;data.config = config;data.compatInfo = compatInfo;data.initProfileFile = profileFile;data.initProfileFd = profileFd;data.initAutoStopProfiler = false;queueOrSendMessage(H.BIND_APPLICATION, data);}  

又是Handler对消息的处理,可以直接定位到最终的执行函数handleBindApplication

private void handleBindApplication(AppBindData data) {mBoundApplication = data;......if (data.instrumentationName != null) {InstrumentationInfo ii = null;try {ii = appContext.getPackageManager().getInstrumentationInfo(data.instrumentationName, 0);} catch (PackageManager.NameNotFoundException e) {}if (ii == null) {throw new RuntimeException("Unable to find instrumentation info for: "+ data.instrumentationName);}mInstrumentationAppDir = ii.sourceDir;mInstrumentationAppLibraryDir = ii.nativeLibraryDir;mInstrumentationAppPackage = ii.packageName;mInstrumentedAppDir = data.info.getAppDir();mInstrumentedAppLibraryDir = data.info.getLibDir();ApplicationInfo instrApp = new ApplicationInfo();instrApp.packageName = ii.packageName;instrApp.sourceDir = ii.sourceDir;instrApp.publicSourceDir = ii.publicSourceDir;instrApp.dataDir = ii.dataDir;instrApp.nativeLibraryDir = ii.nativeLibraryDir;LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,appContext.getClassLoader(), false, true);ContextImpl instrContext = new ContextImpl();instrContext.init(pi, null, this);try {java.lang.ClassLoader cl = instrContext.getClassLoader();mInstrumentation = (Instrumentation)cl.loadClass(data.instrumentationName.getClassName()).newInstance();} catch (Exception e) {throw new RuntimeException("Unable to instantiate instrumentation "+ data.instrumentationName + ": " + e.toString(), e);}mInstrumentation.init(this, instrContext, appContext,new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher);if (mProfiler.profileFile != null && !ii.handleProfiling&& mProfiler.profileFd == null) {mProfiler.handlingProfiling = true;File file = new File(mProfiler.profileFile);file.getParentFile().mkdirs();Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);}} else {mInstrumentation = new Instrumentation();}if ((data.appInfo.flags&ApplicationInfo.FLAG_LARGE_HEAP) != 0) {dalvik.system.VMRuntime.getRuntime().clearGrowthLimit();}// Allow disk access during application and provider setup. This could// block processing ordered broadcasts, but later processing would// probably end up doing the same disk access.final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();try {// If the app is being launched for full backup or restore, bring it up in// a restricted environment with the base application class.Application app = data.info.makeApplication(data.restrictedBackupMode, null);mInitialApplication = app;......try {mInstrumentation.onCreate(data.instrumentationArgs);}catch (Exception e) {throw new RuntimeException("Exception thrown in onCreate() of "+ data.instrumentationName + ": " + e.toString(), e);}try {//执行Application的onCreate方法mInstrumentation.callApplicationOnCreate(app);} catch (Exception e) {if (!mInstrumentation.onException(app, e)) {throw new RuntimeException("Unable to create application " + app.getClass().getName()+ ": " + e.toString(), e);}}} finally {StrictMode.setThreadPolicy(savedPolicy);}
}  

这段代码略去了不影响流程的代码,剩余的代码完成了新建Instrumentation对象mInstrumentation,然后调用了mInstrumentation.callApplicationOnCreate(app)方法,也就是执行了Application的onCreate方法。

这部分的逻辑也提醒我们,如果我们在应用中声明的Service不在应用的进程中,在启动Service的时候,应用的Application会执行。

好的,上面的代码执行完成之后,Service所在的进程已经创建完毕,接下来就要真正走执行Service自己的方法流程了。在本文attachApplicationLocked方法的最后,会有一个启动Service的逻辑:

if (!badApp && mPendingServices.size() > 0) {ServiceRecord sr = null;try {for (int i=0; i<mPendingServices.size(); i++) {sr = mPendingServices.get(i);if (app != sr.isolatedProc && (app.uid != sr.appInfo.uid|| !processName.equals(sr.processName))) {continue;}mPendingServices.remove(i);i--;realStartServiceLocked(sr, app);didSomething = true;}} catch (Exception e) {Slog.w(TAG, "Exception in new application when starting service "+ sr.shortName, e);badApp = true;}}  

badAppp = false,在Service启动流程(startService)的最后,会将即将启动的ServiceRecord添加到ActivityManagerService的mPendingServices队列中,因此mPendingServices.size()>0,故会执行接下来的逻辑。接下来的逻辑中,会循环遍历mPendingServices,由于此时是在单独进程中启动Service,故会对新建的进程app进行判断,,并且会对app的uid和processName进行判断,条件满足说明不需要对Service执行启动操作,执行continue,否则执行realStartServiceLocked去启动Service,realStartServiceLocked函数的逻辑可以参考startService启动流程—Service在App进程但未启动的内容,这里就不在进行分析了

这篇关于startService启动流程---Service在非App进程且未启动的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Security OAuth2 单点登录流程

单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一注销(single sign-off)就是指

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

MySQL数据库宕机,启动不起来,教你一招搞定!

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG、Mongodb数据库运维(如安装迁移,性能优化、故障应急处理等)公众号:老苏畅谈运维欢迎关注本人公众号,更多精彩与您分享。 MySQL数据库宕机,数据页损坏问题,启动不起来,该如何排查和解决,本文将为你说明具体的排查过程。 查看MySQL error日志 查看 MySQL error日志,排查哪个表(表空间

springboot3打包成war包,用tomcat8启动

1、在pom中,将打包类型改为war <packaging>war</packaging> 2、pom中排除SpringBoot内置的Tomcat容器并添加Tomcat依赖,用于编译和测试,         *依赖时一定设置 scope 为 provided (相当于 tomcat 依赖只在本地运行和测试的时候有效,         打包的时候会排除这个依赖)<scope>provided

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

用命令行的方式启动.netcore webapi

用命令行的方式启动.netcore web项目 进入指定的项目文件夹,比如我发布后的代码放在下面文件夹中 在此地址栏中输入“cmd”,打开命令提示符,进入到发布代码目录 命令行启动.netcore项目的命令为:  dotnet 项目启动文件.dll --urls="http://*:对外端口" --ip="本机ip" --port=项目内部端口 例: dotnet Imagine.M

Linux服务器Java启动脚本

Linux服务器Java启动脚本 1、初版2、优化版本3、常用脚本仓库 本文章介绍了如何在Linux服务器上执行Java并启动jar包, 通常我们会使用nohup直接启动,但是还是需要手动停止然后再次启动, 那如何更优雅的在服务器上启动jar包呢,让我们一起探讨一下吧。 1、初版 第一个版本是常用的做法,直接使用nohup后台启动jar包, 并将日志输出到当前文件夹n

[Linux]:进程(下)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:Linux学习 贝蒂的主页:Betty’s blog 1. 进程终止 1.1 进程退出的场景 进程退出只有以下三种情况: 代码运行完毕,结果正确。代码运行完毕,结果不正确。代码异常终止(进程崩溃)。 1.2 进程退出码 在编程中,我们通常认为main函数是代码的入口,但实际上它只是用户级

衡石分析平台使用手册-单机安装及启动

单机安装及启动​ 本文讲述如何在单机环境下进行 HENGSHI SENSE 安装的操作过程。 在安装前请确认网络环境,如果是隔离环境,无法连接互联网时,请先按照 离线环境安装依赖的指导进行依赖包的安装,然后按照本文的指导继续操作。如果网络环境可以连接互联网,请直接按照本文的指导进行安装。 准备工作​ 请参考安装环境文档准备安装环境。 配置用户与安装目录。 在操作前请检查您是否有 sud

SpringBoot项目是如何启动

启动步骤 概念 运行main方法,初始化SpringApplication 从spring.factories读取listener ApplicationContentInitializer运行run方法读取环境变量,配置信息创建SpringApplication上下文预初始化上下文,将启动类作为配置类进行读取调用 refresh 加载 IOC容器,加载所有的自动配置类,创建容器在这个过程