Android四大组件之Activity(八)——新创建的进程:Activity组件和新创建的进程相关联

2023-10-19 22:10

本文主要是介绍Android四大组件之Activity(八)——新创建的进程:Activity组件和新创建的进程相关联,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1、使用的是Android 7.1.2的源代码:

https://pan.baidu.com/s/1XcVD68cC_2wKtm8jJkdNQA
przv

2、感谢IT先森的系列博客:

Android应用进程创建流程大揭秘
Android四大组件之bindService源码实现详解
Android四大组件之Activity启动流程源码实现详解概要
Activity启动流程(一)发起端进程请求启动目标Activity
Activity启动流程(二)system_server进程处理启动Activity请求
Activity启动流程(三)-Activity Task调度算法复盘分析
Activity启动流程(四)-Pause前台显示Activity,Resume目标Activity
Activity启动流程(五)请求并创建目标Activity进程
Activity启动流程(六)注册目标Activity进程到system_server进程以及创建目标Activity进程Application
Activity启动流程(七)初始化目标Activity并执行相关生命周期流程

当Zygote创建完一个应用进程之后,得到的仅仅是一个可以运行的载体,但是,Android的四大组件还没有进入到这个新创建的进程之中。

当Zygote进程创建目标Activity进程之后,还需要创建一个运行环境Context,然后创建Application,然后再装载Provider等组件信息。

目标Activity所属的应用进程已经创建好了,接下来,包括如下流程:

  • 注册目标Activity所属进程到system_server
  • 为目标Activity所属进程创建Application实例对象

伪代码:

ATP.bindApplication()---> 
BinderProxy.transact() --->
BpBinder.transact()--->binder驱动传输--->JavaBBinder.onTransact()--->
ATN.onTransact()--->
ATN.bindApplication(...) --->
ApplicationThread.bindApplication(...) --->
ActivityThread.H.sendMessage(H.BIND_APPLICATION,...) --->
ActivityThread.handleBindApplication(...) --->data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);//创建LoadedApkfinal ContextImpl appContext = ContextImpl.createAppContext(this, data.info);//构建ContextImplfinal ClassLoader cl = instrContext.getClassLoader();mInstrumentation = (Instrumentation)cl.loadClass(data.instrumentationName.getClassName()).newInstance();//构建InstrumentationinstallContentProviders(app, data.providers);//安装ContentProvidermInstrumentation.callApplicationOnCreate(app);//调用Application的onCreate方法

一、注册(attach)目标Activity进程到system_server

问题:目标Activity进程是由AMS创建的,为什么还需要将其注册到system_server进程?
答:通过attach的Binder远程调用传递匿名Binder类IApplicationThread给AMS,然后AMS就可以通过上述的匿名Binder来继续对目标Activity进程的相关组件进行调度。

1、ActivityThread.main

  • 调用attach,向system_server发起一个绑定操作,告诉AMS进程启动完毕,可以进行其他事情了
  • 初始化应用进程的主线程的Looper,并开启loop消息循环
public static void main(String[] args) {··· ···Environment.initForCurrentUser();··· ···Process.setArgV0("<pre-initialized>");//创建主线程LooperLooper.prepareMainLooper();//创建一个新的ActivityThreadActivityThread thread = new ActivityThread();//attach到系统进程thread.attach(false);if (sMainThreadHandler == null) {sMainThreadHandler = thread.getHandler();}//主线程进入循环状态Looper.loop();throw new RuntimeException("Main thread loop unexpectedly exited");}

注意:
    ActivityThread就是应用程序的主线程,这种说法是笼统的,因为ActivityThread并非真正意义上的线程,它不是Thread的子类。在ActivityThread对象构建时,会创建一个Handler对象,这个Handler对象所绑定的消息队列就是主线程的消息队列,后面主线程所做的任何事情都是往Handler中发送消息来完成的,所以说Android系统是基于消息驱动的。主线程实现Looper,就是在new ActivityThread()时实现的,即在ActivityThread.main()中构造ActivityThread对象时初始化的Looper。

public final class ActivityThread {final Looper mLooper = Looper.myLooper();final H mH = new H();private class H extends Handler {public static final int LAUNCH_ACTIVITY         = 100;public static final int PAUSE_ACTIVITY          = 101;···}··· ···ActivityThread() {mResourcesManager = ResourcesManager.getInstance();}··· ···
}

在构造函数初始化时,其上面的变量也都会初始化:

public class One {private class H{H(){System.out.println("One::H():::");}}H mH = new H();int a = 3;public One() {System.out.println("One::One():::" + a);}
}
public class Two {public static void main(String args[]) {One mOne = new One();	}
}

输出结果为:

One::H():::
One::One():::3


2、ActivityThread.attach

final ApplicationThread mAppThread = new ApplicationThread();//system参数的作用是区别是system_server进程发起的绑定还是普通应用进程发起的绑定
//我们现在是子进程发起的绑定private void attach(boolean system) {sCurrentActivityThread = this;mSystemThread = system;if (!system) {//设置进程名。android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",UserHandle.myUserId());RuntimeInit.setApplicationObject(mAppThread.asBinder());final IActivityManager mgr = ActivityManagerNative.getDefault();try {mgr.attachApplication(mAppThread);  //这里的mAppThread是一个匿名Binder实例} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}··· ···} else {//系统进程走这里}}

上述流程中,通过AMP借助Binder调用AMS的方法attachApplication,注意这里传递的mAppThread是一个匿名Binder实例,因此可以作为跨进程传递的参数。这里的mAppThread对象存在于应用进程,但会被传递到系统进程,在系统进程看来,此时的mAppThread就是操作应用进程的一个通信工具。后续,系统进程system_server如果想要向应用进程发起跨进程调用,也都需要通过mAppThread这个对象来完成相关的调度。

AMP.attachApplication的调用逻辑:
在这里插入图片描述

3、AMS.attachApplication

 // [AMS]@Overridepublic final void attachApplication(IApplicationThread thread) {synchronized (this) {//获取调用进程端pidint callingPid = Binder.getCallingPid();final long origId = Binder.clearCallingIdentity();attachApplicationLocked(thread, callingPid);Binder.restoreCallingIdentity(origId);}}

调用attachApplicationLocked进行下一步的处理,此时多了个参数pid。

注意,在Android源码中带Locked的方法,一般都需要进行加锁操作

在attachApplicationLocked中,会根据pid映射应用进程的ProcessRecord对象,在Android四大组件之Activity(五)——AMS:针对新启动的Activity创建一个新的进程 中的AMS.startProcessLocked中会将通过Process.start生成的pid给put到mPidsSelfLocked中。

 // [AMS]private final boolean attachApplicationLocked(IApplicationThread thread,int pid) {ProcessRecord app;long startTime = SystemClock.uptimeMillis();/*1、根据PID映射应用进程的ProcessRecord对象那么此处的ProcessRecord是什么时候创建并加入到mPidsSelfLocked中的呢,这个在该系列博客的五中有详细描述*/if (pid != MY_PID && pid >= 0) {synchronized (mPidsSelfLocked) {app = mPidsSelfLocked.get(pid);}} else {app = null;}if (app == null) {//当获取目标Activity进程的ProcessRecord失败,则会执行清理逻辑,并强制退出// 获取ProcessRecord对象失败,则做一些清理操作后退出if (pid > 0 && pid != MY_PID) {Process.killProcessQuiet(pid);} else {try {thread.scheduleExit();} catch (Exception e) {}}return false;}/*当下需要将ProcessRecord绑定到一个新的进程所以需要将之前ProcessRecord所绑定的进程信息清除这个地方有点难理解,我们可以认为对此处的ProcessRecord进行复用,在复用之前需要做一些清理*/if (app.thread != null) {handleAppDiedLocked(app, true, true);}final String processName = app.processName;try {/*2、注册应用进程的DeathRecipient,当应用进程崩溃时,系统进程可以收到通知为啥要整这一套呢,主要是因为AMS服务监听到应用进程奔溃以后需要做一些资源包回收和数据结构的调整关于AppDeathRecipient可以参见大神gityuan的博客http://gityuan.com/2016/10/03/binder_linktodeath/*/AppDeathRecipient adr = new AppDeathRecipient(app, pid, thread);thread.asBinder().linkToDeath(adr, 0);app.deathRecipient = adr;} catch (RemoteException e) {app.resetPackageList(mProcessStats);startProcessLocked(app, "link fail", processName);return false;}//3、激活ProcessRecord对象        //将目标Activity进程的IApplicationThread匿名Binder代理端绑定到ProcessRecord对象app.makeActive(thread, mProcessStats);/****************************************************///这里为了演示方便,直接给出源码//[ProcessRecord.java]final class ProcessRecord {IApplicationThread thread;public void makeActive(IApplicationThread _thread, ProcessStatsService tracker) {if (thread == null) {...}thread = _thread;}    }     /****************************************************///继续进行其它的对ProcessRecord的赋值app.curAdj = app.setAdj = app.verifiedAdj = ProcessList.INVALID_ADJ;app.curSchedGroup = app.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;app.forcingToForeground = null;updateProcessForegroundLocked(app, false, false);app.hasShownUi = false;app.debugging = false;app.cached = false;app.killedByAm = false;app.unlocked = StorageManager.isUserKeyUnlocked(app.userId);//移除进程启动超时消息,就是前面ANR的埋雷机制mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);// 4、获取应用进程的所有ProviderList<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;//这又是一个ANR埋雷,后续在ContentProvider发布的时候会进行解除if (providers != null && checkAppInLaunchingProvidersLocked(app)) {Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);msg.obj = app;mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);}...try {...//省略debug和性能优化等相关配置的代码,这里就不赘述了//5、这里又是老熟人了,一路分析过来见过很多次了,此处发起跨进程调用,将一堆的信息传递给目标Activity应用进程thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,app.instrumentationUiAutomationConnection, testMode,mBinderTransactionTrackingEnabled, enableTrackAllocation,isRestrictedBackupMode || !normalMode, app.persistent,new Configuration(mConfiguration), app.compat,getCommonServicesLocked(app.isolated),mCoreSettingsObserver.getCoreSettingsLocked());updateLruProcessLocked(app, false, null);...} catch (Exception e) {//异常处理...}mPersistentStartingProcesses.remove(app);mProcessesOnHold.remove(app);//将该ProcessRecord从ProcessesOnHold列表中移除boolean badApp = false;boolean didSomething = false;//此处表示目标Activity进程是正常启动模式,此时肯定是if (normalMode) {try {//6-1、此处是关键啊,检查有没有待启动的activity,假如目标Activity是被冷启动则会在此处流程执行if (mStackSupervisor.attachApplicationLocked(app)) {didSomething = true;}} catch (Exception e) {badApp = true;}}if (!badApp) {try {//6-2、检查有没有待启动的service,这个我们再binderService有涉及到过didSomething |= mServices.attachApplicationLocked(app, processName);} catch (Exception e) {badApp = true;}}if (!badApp && isPendingBroadcastProcessLocked(pid)) {try {//6-3、查有没有待启动的receiverdidSomething |= sendPendingBroadcastsLocked(app);} catch (Exception e) {badApp = true;}}       .../*假如启动目标Activity,Service,或者BroadCast任何一个执行失败,都会kill掉目标Activity进程,并且执行handleAppDiedLocked的处理*/if (badApp) {app.kill("error during init", true);handleAppDiedLocked(app, false, true);return false;}if (!didSomething) {updateOomAdjLocked();}return true;}

上述代码的主要逻辑:

  1. 获取新创建的进程(即Activity目标进程)在启动阶段由 AMS 向 Zygote进程发起请求时创建的ProcessRecord数据结构。ProcessRecord在AMS中是以 key-value 键值对存储的,其key是pid。如果ProcessRecord对象获取失败,则表示应用进程已经被杀掉,需要清除应用进程的数据;如果ProcessRecord之前绑定的进程信息还在,则需要清除这些信息。
  2. 为应用进程注册死亡通知AppDeathRecipient,它是存在于系统进程的对象。当目标Activity进程被杀的时候,AMS会通过AppDeathRecipient来进行清理工作。
  3. 激活ProcessRecord对象。通过 app.makeActive(thread, mProcessStats); 将ProcessRecord绑定到了一个具体的应用进程,绑定的标识就是将应用进程的ApplicationThread对象赋值给 ProcessRecord.thread变量,注意此时的ApplicationThread是Binder的代理端,它的实体端在目标Activity进程端。
  4. 获取目标Activity应用进程中所有注册的Provider,这个需要通过PackageManager来扫描进程所关联的包名,所有静态的 Provider信息,即ProviderInfo对象,都会保存到ProcessRecord.putProviders变量中,通常上述操作在应用安装或终端启动的时候会执行。

系统启动时,也经历过这个过程,系统进程对应的包名是“android”,扫描的是framework-res.apk的这个应用的信息。

  1. 在进行一些调试与性能相关的变量设置之后,通过IApplicationThread.bindApplication() 向目标Activity进程发起跨进程 Binder调用,这样一来,诸如进程名、ApplicationInfo等相关信息就传递给应用进程了。(ProcessRecord.info是ApplicationInfo类型的数据,记录的是 AndroidManifest.xml中定义的Application信息,其信息是通过AMS.newProcessRecordLocked中new ProcessRecord传递过去的,是从ActivityRecord中得到的)
  2. 将信息传递给目标Activity应用程序之后,检查有没有四大组件等待在该进程中运行,如果有,继续执行四大组件。不过,这里只有Activity、Service和BroadCastReceiver三种组件,ContentProvider的发布会在bindApplication的时候进行。这里通过badApp/didSomething两个标识来记录调度的情况,其中badApp标识是否调度失败,默认为false,在依次调度Activity/Service/Broadcast的过程中,根据实际的情况,可能将其调整为true,表示调度失败了。一旦调度失败,则需要杀掉应用进程。而didSomething表示确实有调度发生。

二、创建Activity应用进程Application和并初始化应用进程运行环境

开始应用进程的自我成长。

此时目标Activity应用进程有了自己的进程名,并且在接下来会构建出Android的运行环境,真正有了Android应用程序的概念,即Application。

1、ApplicationThread.bindApplication

新创建的进程处理bindApplication的RPC请求:

//[ActivityThread.java]private class ApplicationThread extends ApplicationThreadNative {...public final void bindApplication(String processName, ApplicationInfo appInfo,List<ProviderInfo> providers, ComponentName instrumentationName,ProfilerInfo profilerInfo, Bundle instrumentationArgs,IInstrumentationWatcher instrumentationWatcher,IUiAutomationConnection instrumentationUiConnection, int debugMode,boolean enableBinderTracking, boolean trackAllocation,boolean isRestrictedBackupMode, boolean persistent, Configuration config,CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings) {//此处缓存的是通过bindApplication传递过来的PKMS,WMS,ALARM_SERVICE代理端if (services != null) {ServiceManager.initServiceCache(services);}setCoreSettings(coreSettings);//将AMS传递过来的参数封装到AppBindData 数据结构中AppBindData data = new AppBindData();data.processName = processName;...//巧用ActivityThread的主线程的Handler发送消息sendMessage(H.BIND_APPLICATION, data);}...}        

在上述代码中,将system_server进程传递过来的与应用进程相关的信息打包到了AppBindData 类型的变量 data 中,接下来会将上述的相关信息一起组装成Android应用程序运行所必须的构成元素。

//[ActivityThread.java]
private class H extends Handler {··· ···public static final int BIND_APPLICATION = 110;public void handleMessage(Message msg) {··· ···case BIND_APPLICATION:Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");AppBindData data = (AppBindData)msg.obj;handleBindApplication(data);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);break;}
}

2、ActivityThread.handleBindApplication

//[ActivityThread.java]private void handleBindApplication(AppBindData data) {...//省略一些相关的参数设置//虽然说目标Actiivity进程在之间已经fork出来了,但是系统直到此时才是它的开端Process.setStartTimes(SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());// 虽然应用进程早就已经创建,但直到这时,才知道进程名是什么Process.setArgV0(data.processName);android.ddm.DdmHandleAppName.setAppName(data.processName, UserHandle.myUserId());... // 省略应用进程运行信息其它的一些设置代码,譬如语言,时区//2.1、创建LoadedApk对象,此处是关键,后续会专门分析data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);//Android应用默认dpi相关设置if ((data.appInfo.flags&ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES)== 0) {mDensityCompatMode = true;Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEFAULT);}updateDefaultDensity();//设置时间格式final boolean is24Hr = "24".equals(mCoreSettings.getString(Settings.System.TIME_12_24));DateFormat.set24HourTimePref(is24Hr);...//StrictMode相关的设置StrictMode.enableDeathOnNetwork();//应用进程相关的初始化代码,包含时区、StrictMode、调试模式等相关的设置,感兴趣的小伙们就自行研究吗...final InstrumentationInfo ii;··· ···//2.2、创建ContextImpl对象;final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);if (ii != null) {final ApplicationInfo instrApp = new ApplicationInfo();ii.copyTo(instrApp);instrApp.initForUser(UserHandle.myUserId());final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,appContext.getClassLoader(), false, true, false);final ContextImpl instrContext = ContextImpl.createAppContext(this, pi);/*2.3、创建Instrumentation它也是我们的老熟人了,在前面分析的四大组件相关的生命周期调度中多次看到它了这里通过ClassLoader直接加载进行构建,在整个Android应用进程中它是唯一的网上很多博客将Instrumentation说为Android系统里面的一套控制方法或者”钩子“。这些钩子可以在正常的生命周期(正常是由操作系统控制的)之外控制Android控件的运行*/try {final ClassLoader cl = instrContext.getClassLoader();mInstrumentation = (Instrumentation)cl.loadClass(data.instrumentationName.getClassName()).newInstance();} catch (Exception e) {··· ···}final ComponentName component = new ComponentName(ii.packageName, ii.name); mInstrumentation.init(this, instrContext, appContext, component,data.instrumentationWatcher, data.instrumentationUiAutomationConnection);...}} else {mInstrumentation = new Instrumentation();}//2.4、创建Application对象Application app = data.info.makeApplication(data.restrictedBackupMode, null);mInitialApplication = app;//2.5、安装providers,看来providers的安装都前于其它三大组件啊List<ProviderInfo> providers = data.providers;installContentProviders(app, providers);//2.6、这不Instrumentation刚被创建就被排上了用场执行Application.Create回调mInstrumentation.callApplicationOnCreate(app);}

在上述代码中:

  • 各种零碎资源的初始化。设定显示相关的configuration、初始化packageInfo、设置默认的Dpi、设置时间格式、设置strictMode,设置字体资源等等,为以后四大组件的运行提供运行环境。
  • 创建对象LoadedApk
  • 创建Android运行环境ContextImpl
  • 创建Instrumentation对象
  • 创建Application对象。通过LoadedApk.makeApplication()函数,就能创建一个Application对象
  • 装载Providers。有了一个静态的ProviderInfo列表,但应用进程的ContentProvider还不能真正工作,因为ContentProvider对象还未创建。ActivityThread.installContentProviders()函数就是用来创建ContentProvider对象的。由此可见,在Application.onCreate()函数调用之前,进程的ContentProvider都已经创建完毕了,是不是有点惊讶ContentProvider的创建竟然早于Application.onCreate()方法
  • 调用Application.onCreate()方法。这个方法就是我们通常在Android应用开发者系统回调的方法,并且通常应用开发者会在此方法中做一些应用的全局设置
2.1 创建LoadedApk对象

在这里插入图片描述

2.1.1 ActivityThread.getPackageInfoNoCheck(…)
//[ActivityThread.java]public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,CompatibilityInfo compatInfo) {return getPackageInfo(ai, compatInfo, null, false, true, false);}

2.1.2 ActivityThread.getPackageInfo(…)

//[ActivityThread.java]final ArrayMap<String, WeakReference<LoadedApk>> mPackages= new ArrayMap<String, WeakReference<LoadedApk>>();private LoadedApk getPackageInfo(ApplicationInfo aInfo, //这个是AMS通过bindApplication传递过来的Activity应用进程AndroidManifest中Application的的相关数据结构CompatibilityInfo compatInfo,//对ApplicationInfo数据结构的又一层封装ClassLoader baseLoader, //类加载器,此时为nullboolean securityViolation, //表示隐私,此时为falseboolean includeCode,//功能不详,此时为trueboolean registerPackage) //功能不详,此时为false{//多用户情况的判断final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid));synchronized (mResourcesManager) {WeakReference<LoadedApk> ref;if (differentUser) {ref = null;} else if (includeCode) {//进入此分支,从mPackages查询,由于前面没有创建并且存储过,所以此处获取的为nullref = mPackages.get(aInfo.packageName);} else {ref = mResourcePackages.get(aInfo.packageName);}LoadedApk packageInfo = ref != null ? ref.get() : null;if (packageInfo == null || (packageInfo.mResources != null&& !packageInfo.mResources.getAssets().isUpToDate())) {//直接new出一个packageInfo =new LoadedApk(this, aInfo, compatInfo, baseLoader,securityViolation, includeCode &&(aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);//详见2.3.2//system_server进程会进入此分支,这里不会进入此分支if (mSystemThread && "android".equals(aInfo.packageName)) {packageInfo.installSystemApplicationInfo(aInfo,getSystemContext().mPackageInfo.getClassLoader());}if (differentUser) {} else if (includeCode) {//将前面创建的LoadedApk放入mPackages列表mPackages.put(aInfo.packageName,new WeakReference<LoadedApk>(packageInfo));} else {mResourcePackages.put(aInfo.packageName,new WeakReference<LoadedApk>(packageInfo));}}return packageInfo;}}

该方法的作用:

  • 判断mPackages中能否通过包名找到合适的LoadedApk实例对象,如果没有则直接创建,然后将创建的LoadedApk对象放入到mPackages中。mPackages的数据类型为ArrayMap<String, WeakReference>,记录着每一个包名所对应的LoadedApk对象的弱引用,所以对于应用进程来说LoadedApk通常是唯一的!
2.1.2 new LoadedApk(…)
//[LoadedApk.java]public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,CompatibilityInfo compatInfo, ClassLoader baseLoader,boolean securityViolation, boolean includeCode, boolean registerPackage) {mActivityThread = activityThread;setApplicationInfo(aInfo);/*****************************************************************************///这里为了演示方便,直接将源码搬出来!private void setApplicationInfo(ApplicationInfo aInfo) {final int myUid = Process.myUid();aInfo = adjustNativeLibraryPaths(aInfo);mApplicationInfo = aInfo;mAppDir = aInfo.sourceDir;mResDir = aInfo.uid == myUid ? aInfo.sourceDir : aInfo.publicSourceDir;mSplitAppDirs = aInfo.splitSourceDirs;mSplitResDirs = aInfo.uid == myUid ? aInfo.splitSourceDirs : aInfo.splitPublicSourceDirs;mOverlayDirs = aInfo.resourceDirs;mSharedLibraries = aInfo.sharedLibraryFiles;mDataDir = aInfo.dataDir;mLibDir = aInfo.nativeLibraryDir;mDataDirFile = FileUtils.newFileOrNull(aInfo.dataDir);mDeviceProtectedDataDirFile = FileUtils.newFileOrNull(aInfo.deviceProtectedDataDir);mCredentialProtectedDataDirFile = FileUtils.newFileOrNull(aInfo.credentialProtectedDataDir);}        /*****************************************************************************/mPackageName = aInfo.packageName;mBaseClassLoader = baseLoader;mSecurityViolation = securityViolation;mIncludeCode = includeCode;mRegisterPackage = registerPackage;mDisplayAdjustments.setCompatibilityInfo(compatInfo);}

在构造方法中记录了Activity运行所在的ActivityThread、Activity所在的应用程序信息、Activity所在应用进程的包名、Activity所在应用进程的库路径、Activity所在应用进程的数据存储路径、类加载器和应用程序所使用的资源等信息。

2.2 创建ContextImpl

最终相关Context上下文的操作都是由ContextImpl来执行的。

2.2.1 ContextImpl.createAppContext
//[ContextImpl.java]static ContextImpl createAppContext(ActivityThread mainThread, //这里传入的是ActivityThread的引用LoadedApk packageInfo) //前面构建的LoadedApk实例对象引用{if (packageInfo == null) throw new IllegalArgumentException("packageInfo");return new ContextImpl(null, mainThread,packageInfo, null, null, 0, null, null, Display.INVALID_DISPLAY);}
2.2.1 new ContextImpl(…)
//[ContextImpl.java]
class ContextImpl extends Context {final ActivityThread mMainThread;final LoadedApk mPackageInfo;private final IBinder mActivityToken;private final String mBasePackageName;private Context mOuterContext;//缓存Binder服务final Object[] mServiceCache = SystemServiceRegistry.createServiceCache();private ContextImpl(ContextImpl container, ActivityThread mainThread, LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted, Display display, Configuration overrideConfiguration, int createDisplayWithId) {mOuterContext = this; //ContextImpl对象mMainThread = mainThread; // ActivityThread赋值mPackageInfo = packageInfo; // LoadedApk赋值mActivityToken = activityToken;//这个通常是Activity在构建其ContextImpl时候传入的mBasePackageName = packageInfo.mPackageName; //mBasePackageName通常等于应用进程包名...}
}

创建ContextImpl的方式有多种, 不同的组件初始化调用不同的方法:
1)Activity: 调用createBaseContextForActivity初始化;
2)Service/Application: 调用createAppContext初始化; -->本节调用的是createAppContext
3)Provider: 调用createPackageContext初始化;
4)BroadcastReceiver: 直接从Application.getBaseContext()来获取ContextImpl对象;

2.3 构建Instrumentation 并初始化它
2.3.1 反射调用new Instrumentation()
//[Instrumentation.java]//构造方法啥也没有干,看来是通过其它public Instrumentation() {}final void init(ActivityThread thread,//持有对ActivityThread实例对象的引用Context instrContext, Context appContext, ComponentName component, IInstrumentationWatcher watcher, IUiAutomationConnection uiAutomationConnection) {mThread = thread;mMessageQueue = mThread.getLooper().myQueue();mInstrContext = instrContext;mAppContext = appContext;mComponent = component;mWatcher = watcher;mUiAutomationConnection = uiAutomationConnection;}

在一个应用进程中,Instrumentation实例对象都是唯一的。

2.4 创建Application

从2.1中看到,data.info是LoadedApk对象

2.4.1 LoadedApk.makeApplication(…)
//[LoadedApk.java]public Application makeApplication(boolean forceDefaultAppClass,//该值是从AMS中传递过来的Instrumentation instrumentation) //此时的instrumentation已经被指定为null{/*1、保证一个LoadedApk对象只创建一个对应的Application对象实例,和我们章节开始所说的每个应用进程只拥有一个Application相对应*/if (mApplication != null) {return mApplication;}...Application app = null;/*获取Application名称,即我们在AndroidManifest中Application的名称如果我们没有实现自己的Application,通常appClass就是null了*/String appClass = mApplicationInfo.className;//强制给Application一个名称if (forceDefaultAppClass || (appClass == null)) {appClass = "android.app.Application";}try {//2、获取当前应用的ClassLoader对象java.lang.ClassLoader cl = getClassLoader();//system_server进程除外if (!mPackageName.equals("android")) {//设置当前线程的Context ClassLoaderinitializeJavaContextClassLoader();}//3、和2.2.1中一样,根据当前 ActivityThread 对象来创建相应的 ContextImpl 对象//这里的appContext变量为方法内部变量ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);//4、创建Application对象, 并将appContext和创建的Application关联起来app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);appContext.setOuterContext(app); //-->ContextImpl.setOuterContext(...)} catch (Exception e) {,,,}	//这个地方不是很明白,ActivityThread通常不是一个应用进程只有一个,而且Application也是唯一的吗,为啥这里要整个//存放Application的列表进行存放呢,纳尼mActivityThread.mAllApplications.add(app);mApplication = app;//将刚创建的app赋值给mApplicationif (instrumentation != null) {//此时的instrumentation已经被强制位null,所以此分支肯定不会走...}...return app;}
//[ContextImpl]final void setOuterContext(Context context) {mOuterContext = context;}

在上述代码中,appContext是makeApplication()方法的内部变量,调用 appContext.setOuterContext(app); 将新创建的 Application 对象 app 保存到 ContextImpl 的成员变量 mOuterContext 的话,方法运行结束变量 appContext 不就销毁了吗,那么作用是什么?

答:因为 Application 对象 app 创建的时候,即

app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);

持有对 appContext 的引用,而且可以通过getApplication获取唯一的Application实例对象,然后通过这个Application对象可以获取到这里的ContextImpl实例对象,即可以获取到其保存的mOuterContext。

2.4.2 Instrumentation.newApplication(…)
//[Instrumentation.java]public Application newApplication(ClassLoader cl, String className, Context context)throws InstantiationException, IllegalAccessException, ClassNotFoundException {return newApplication(cl.loadClass(className), context);}static public Application newApplication(Class<?> clazz, Context context)throws InstantiationException, IllegalAccessException, ClassNotFoundException {Application app = (Application)clazz.newInstance();app.attach(context);//执行attach操作return app;}
//[Application]
final void attach(Context context) {attachBaseContext(context); //Application的mBasemLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}

attach方法的作用:

  • 将新创建的ContextImpl对象保存到Application的父类成员变量mBase;
  • 将所在的LoadedApk对象保存到Application的父类成员变量mLoadedApk;
2.5 安装providers

在Android原生应用中可以看到许多的Provider:

XXX:/system/priv-app # ls | grep Provider
ContactsProvider
DownloadProvider
ExternalStorageProvider
MediaProvider
MtpDocumentsProvider
SettingsProvider
TelephonyProvider

2.5.1 ActivityThread.installContentProviders(…)
//[ActivityThread.java]private void installContentProviders(Context context, List<ProviderInfo> providers) //注意这里的参数,是AMS传递过来的在AndroidManifest中的注册的Provider信息{final ArrayList<IActivityManager.ContentProviderHolder> results =new ArrayList<IActivityManager.ContentProviderHolder>();for (ProviderInfo cpi : providers) {if (DEBUG_PROVIDER) {StringBuilder buf = new StringBuilder(128);buf.append("Pub ");buf.append(cpi.authority);buf.append(": ");buf.append(cpi.name);Log.i(TAG, buf.toString());}IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);if (cph != null) {cph.noReleaseNeeded = true;results.add(cph);}}try {//这个就不分析了,总之会注册到AMS中去ActivityManagerNative.getDefault().publishContentProviders(getApplicationThread(), results);} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}}
2.5.2 ActivityThread.installProvider(…)
// [ActivityThread]
private IActivityManager.ContentProviderHolder installProvider(Context context, IActivityManager.ContentProviderHolder holder, ProviderInfo info, boolean noisy, boolean noReleaseNeeded, boolean stable) {ContentProvider localProvider = null;IContentProvider provider;if (holder == null || holder.provider == null) {Context c = null;ApplicationInfo ai = info.applicationInfo;if (context.getPackageName().equals(ai.packageName)) {c = context;} else if (mInitialApplication != null &&mInitialApplication.getPackageName().equals(ai.packageName)) {c = mInitialApplication;} else {//step 1 && 2: 创建LoadedApk和ContextImpl对象c = context.createPackageContext(ai.packageName,Context.CONTEXT_INCLUDE_CODE);}final java.lang.ClassLoader cl = c.getClassLoader();//step 3: 创建ContentProvider对象localProvider = (ContentProvider)cl.loadClass(info.name).newInstance();provider = localProvider.getIContentProvider();//step 4: ContextImpl都attach到ContentProvider对象//step 5: 并执行回调onCreatelocalProvider.attachInfo(c, info);} else {...}...return retHolder;
}
  • 创建对象LoadedApk(此处并不会真的去创建了,前面以前有创建过了)
  • 创建对象ContextImpl;
  • 创建对象ContentProvider;
  • ContextImpl都attach到ContentProvider对象;
  • 执行ContentProvider的onCreate回调;
2.6 执行目标应用进程Application的onCreate方法
//[Instrumentation.java]public void callApplicationOnCreate(Application app) {app.onCreate();}

在这里插入图片描述

本来待启动的应用程序进程为一个赤裸裸的进程,但是

  • 通过attachApplication方法跳转到在system_server进程中,将AMS保存的关于该进程的ProcessRecord与该进程的匿名Binder,IApplicationThread类型的变量thread,关联起来。即设置变量app(ProcessRecord类型)的变量thread为该thread。
  • 由system_server进程调用bindApplication()方法,在其参数中携带了关于该进程的一系列相关变量,包括:processName、ApplicationInfo、providers、instrumentationName等信息。
  • 在待启动的应用程序进程中,创建LoadedApk、ContextImpl、Instrumentation、Application以及安装providers等操作,即将目标Activity将要运行的应用环境构建好,然后执行mInstrumentation.callApplicationOnCreate(Application app);

AMS.attachApplicationLocked 执行完 thread.bindApplication 之后,接着执行6

这篇关于Android四大组件之Activity(八)——新创建的进程:Activity组件和新创建的进程相关联的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

JS常用组件收集

收集了一些平时遇到的前端比较优秀的组件,方便以后开发的时候查找!!! 函数工具: Lodash 页面固定: stickUp、jQuery.Pin 轮播: unslider、swiper 开关: switch 复选框: icheck 气泡: grumble 隐藏元素: Headroom

如何在页面调用utility bar并传递参数至lwc组件

1.在app的utility item中添加lwc组件: 2.调用utility bar api的方式有两种: 方法一,通过lwc调用: import {LightningElement,api ,wire } from 'lwc';import { publish, MessageContext } from 'lightning/messageService';import Ca

高效录音转文字:2024年四大工具精选!

在快节奏的工作生活中,能够快速将录音转换成文字是一项非常实用的能力。特别是在需要记录会议纪要、讲座内容或者是采访素材的时候,一款优秀的在线录音转文字工具能派上大用场。以下推荐几个好用的录音转文字工具! 365在线转文字 直达链接:https://www.pdf365.cn/ 365在线转文字是一款提供在线录音转文字服务的工具,它以其高效、便捷的特点受到用户的青睐。用户无需下载安装任何软件,只

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

[Linux]:进程(下)

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

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

Android 10.0 mtk平板camera2横屏预览旋转90度横屏拍照图片旋转90度功能实现

1.前言 在10.0的系统rom定制化开发中,在进行一些平板等默认横屏的设备开发的过程中,需要在进入camera2的 时候,默认预览图像也是需要横屏显示的,在上一篇已经实现了横屏预览功能,然后发现横屏预览后,拍照保存的图片 依然是竖屏的,所以说同样需要将图片也保存为横屏图标了,所以就需要看下mtk的camera2的相关横屏保存图片功能, 如何实现实现横屏保存图片功能 如图所示: 2.mtk

android应用中res目录说明

Android应用的res目录是一个特殊的项目,该项目里存放了Android应用所用的全部资源,包括图片、字符串、颜色、尺寸、样式等,类似于web开发中的public目录,js、css、image、style。。。。 Android按照约定,将不同的资源放在不同的文件夹中,这样可以方便的让AAPT(即Android Asset Packaging Tool , 在SDK的build-tools目