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

相关文章

C#提取PDF表单数据的实现流程

《C#提取PDF表单数据的实现流程》PDF表单是一种常见的数据收集工具,广泛应用于调查问卷、业务合同等场景,凭借出色的跨平台兼容性和标准化特点,PDF表单在各行各业中得到了广泛应用,本文将探讨如何使用... 目录引言使用工具C# 提取多个PDF表单域的数据C# 提取特定PDF表单域的数据引言PDF表单是一

PyCharm接入DeepSeek实现AI编程的操作流程

《PyCharm接入DeepSeek实现AI编程的操作流程》DeepSeek是一家专注于人工智能技术研发的公司,致力于开发高性能、低成本的AI模型,接下来,我们把DeepSeek接入到PyCharm中... 目录引言效果演示创建API key在PyCharm中下载Continue插件配置Continue引言

使用MongoDB进行数据存储的操作流程

《使用MongoDB进行数据存储的操作流程》在现代应用开发中,数据存储是一个至关重要的部分,随着数据量的增大和复杂性的增加,传统的关系型数据库有时难以应对高并发和大数据量的处理需求,MongoDB作为... 目录什么是MongoDB?MongoDB的优势使用MongoDB进行数据存储1. 安装MongoDB

解决systemctl reload nginx重启Nginx服务报错:Job for nginx.service invalid问题

《解决systemctlreloadnginx重启Nginx服务报错:Jobfornginx.serviceinvalid问题》文章描述了通过`systemctlstatusnginx.se... 目录systemctl reload nginx重启Nginx服务报错:Job for nginx.javas

SpringBoot项目启动后自动加载系统配置的多种实现方式

《SpringBoot项目启动后自动加载系统配置的多种实现方式》:本文主要介绍SpringBoot项目启动后自动加载系统配置的多种实现方式,并通过代码示例讲解的非常详细,对大家的学习或工作有一定的... 目录1. 使用 CommandLineRunner实现方式:2. 使用 ApplicationRunne

Python实现NLP的完整流程介绍

《Python实现NLP的完整流程介绍》这篇文章主要为大家详细介绍了Python实现NLP的完整流程,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 编程安装和导入必要的库2. 文本数据准备3. 文本预处理3.1 小写化3.2 分词(Tokenizatio

macOS怎么轻松更换App图标? Mac电脑图标更换指南

《macOS怎么轻松更换App图标?Mac电脑图标更换指南》想要给你的Mac电脑按照自己的喜好来更换App图标?其实非常简单,只需要两步就能搞定,下面我来详细讲解一下... 虽然 MACOS 的个性化定制选项已经「缩水」,不如早期版本那么丰富,www.chinasem.cn但我们仍然可以按照自己的喜好来更换

python多进程实现数据共享的示例代码

《python多进程实现数据共享的示例代码》本文介绍了Python中多进程实现数据共享的方法,包括使用multiprocessing模块和manager模块这两种方法,具有一定的参考价值,感兴趣的可以... 目录背景进程、进程创建进程间通信 进程间共享数据共享list实践背景 安卓ui自动化框架,使用的是

React实现原生APP切换效果

《React实现原生APP切换效果》最近需要使用Hybrid的方式开发一个APP,交互和原生APP相似并且需要IM通信,本文给大家介绍了使用React实现原生APP切换效果,文中通过代码示例讲解的非常... 目录背景需求概览技术栈实现步骤根据 react-router-dom 文档配置好路由添加过渡动画使用

SpringBoot使用minio进行文件管理的流程步骤

《SpringBoot使用minio进行文件管理的流程步骤》MinIO是一个高性能的对象存储系统,兼容AmazonS3API,该软件设计用于处理非结构化数据,如图片、视频、日志文件以及备份数据等,本文... 目录一、拉取minio镜像二、创建配置文件和上传文件的目录三、启动容器四、浏览器登录 minio五、