Android App 启动时的操作之 ClassLoader 和 Application 初始化

Android App 启动时的操作之 ClassLoader 和 Application 初始化

公共部分

  1. ActivityManagerService.startProcessLocked()

    app 启动时, ActivityManagerService.startProcessLocked()app 启动时启动进程的地方。

  2. ZygoteInit.java 的过程

    startSystemServer() 方法 -> Zygote.forkSystemServer()

  3. RuntimeInit.invokeStaticMain() 反射调回到 ActivityThread.main(...)

    //反射调用 ActivityThread.main(...) 
    m = cl.getMethod("main", new Class[] { String[].class });
    

上面做了一个简述。

Zygotefork 分析,在这里没有细说。需要的可以参考链接:
理解Android进程创建流程

Application 和 ClassLoader 部分

1. 补充部分-有关 ClassLoader

app 里,系统类加载器有三种: BootClassLoader, PathClassLoader, DexClassLoader

  1. BootClassLoader 是用来加载系统 framework 层级的类加载器, 同时它也是 app 中所有 ClassLoader 的最顶层的 parent

    Android系统启动时会使用 BootClassLoader 来预加载常用类

    是个单例。是 ClassLoader 的一个私有内部类

  2. PathClassLoader 是用来加载应用程序的类, 通常是加载已经安装好的 apk 文件

  3. DexClassLoader 可以加载 dex 文件以及包含 dex 的 apk 文件(安装好的程序和未安装的 dex 文件)

    实际上 DexClassLoader 的加载范围比 PathClassLoader 的加载范围要大, 它可以加载在 SD 上面的 .jar.apk 文件。

    在一些动态修复,补丁包上面,是利用 DexClassLoader 去实现的。

现在引出一个问题,Application 的 类加载器 是哪个??

代码检验一下:

ClassLoader classLoader = baseContext.getClassLoader();
if (classLoader != null) {

    Log.i(TAG, "classLoader is " + classLoader.toString() + " --->from Log");

    while (classLoader.getParent() != null) {
          classLoader = classLoader.getParent();
          Log.i(TAG, "classLoader is " + classLoader.toString() + " --->from Log  in while");
    }
}

打印结果为:

ThemeLayoutContainer: classLoader is dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.chenzhao.thememaintest-1/base.apk"],nativeLibraryDirectories=[/data/app/com.example.chenzhao.thememaintest-1/lib/arm, /data/app/com.example.chenzhao.thememaintest-1/base.apk!/lib/armeabi-v7a, /vendor/lib, /system/lib]]] --->from Log
ThemeLayoutContainer: classLoader is java.lang.BootClassLoader@f42ce93 --->from Log  in while

从上面可以看到, 加载 application 的类加载器为 dalvik.system.PathClassLoader, 并且它与包名 com.example.chenzhao.thememaintest-1/base.apk 是相关的,也就是一对一对应的。


为什么会有 parent 这个属性?

ClassLoaderloadclass() 采用的是 (双)父亲委托模型, 在 BootClassLoaderPathClassLoader 的父亲,同时它也是最顶层的一个 classLoader.(脑补树的图)

loadclass() 的步骤:加载类 ATest.class

  1. 会先查询当前 ClassLoader(AClassLoader) 是否加载过 ATest,加载过就返回;

    注: 注意是加载过!!

  2. 如果没有,查询 它 (AClassLoader) 的 parent 是否已经加载过ATest,如果有,就直接返回 parent 加载过的类, 如果没有,依次向上去寻找它的 parent

  3. 如果继承路线上的 ClassLoader 都没有加载,则会用它 (AClassLoader) 去加载该类 ATest;

  4. 当一个类被位于树根 的 ClassLoader 加载过,那么, 在以后整个系统的生命周期内,这个类永远不会被重新加载

    树根的 ClassLoaderAndroid 系统中是 BootClassLoader


2. 正式开始, 那么,PathClassLoader 是什么时候初始化的呢?

一切都需要从 ActivityThread.main(...) 说起

当一个进程被创建成功后,会走到 ActivityThread.main(...),

    //ActivityThread.java
    public static void main(String[] args) {
        
        ...
        
        Looper.prepareMainLooper();
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        ...
        
        Looper.loop();
        
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
    

上述代码,主要的操作在 thread.attach(false);

调用次序:

Android App 启动时的操作之 ClassLoader 和 Application 初始化_第1张图片
调用时序图
  1. thread.attach(false);

    这是第一步,如图上所示 1. thread.attach(false): 大致代码如下:

    final IActivityManager mgr = ActivityManager.getService();
    //在这里 mgr 是 AMS(ActivityManagerService)
    try {
       mgr.attachApplication(mAppThread);
    } catch (RemoteException ex) {
       throw ex.rethrowFromSystemServer();
    }
    

    关键代码:调用了 mgr.attachApplication(...);

  2. mgr.attachApplication(mAppThread);

    mgrIActivityManager ,在这里是 ActivityManagerService, 即 ActivityManagerService.attachApplication(mAppThread)

    那么需要去 AMS 里面寻找对应相关代码.

  3. ActivityManagerService.attachApplicationLocked(thread, callingPid);

    在它的里面,会调用 thread.bindApplication()

    thread 为传过来的参数,是在 ActivityThread 里面的一个成员变量, 它的赋值是在初始化时完成的,代码如下:

    // ActivityThread 里面
    final ApplicationThread mAppThread = new ApplicationThread();
    

    ApplicationThread 的一个对象,它是 ActivityThread 的内部类,再次回到 ActivityThread, 去找 thread.bindApplication() 这个方法,进入第 4 步;

  4. ApplicationThread.bindApplication()

    在这方法里面,最主要的代码就是 sendMessage(H.BIND_APPLICATION, data);

    发了一条 message(是主线程的 handler发送的消息), 在 handleMessage(Message) 里面处理了该消息。

    AppBindData data = (AppBindData)msg.obj;
    handleBindApplication(data);
    

    注:在 handleMessage(Message) 这个方法里面可以看到,是主线程的处理位置,里面有很多管理 activity 生命周期的方法和参数, 它便是主线程处理消息的地方。

  1. ActivityThread.handleBindApplication(data)

    这个方法太长了,太长了!主要代码看下面的部分:

    Application app = data.info.makeApplication(data.restrictedBackupMode, null);
    mInitialApplication = app;
    ...
    //其实这一步是调用的 application  的 onCreate() 方法. 如时序图中的
    mInstrumentation.callApplicationOnCreate(app);
    

    data.info 是什么? 在这里是 LoadedApk 这个对象, 这个类是一个比较重要的类,可以多关注下.

    注:这里稍微注意一下 mInitialApplication 赋值的操作

  2. LoadedApk.makeApplication(..., null);

    LoadedApk.makeApplication() 里面的代码会去获取 ClassLoader, 并且创建 appContext, 再通过 ClassLoaderappContext 去创建 application 对象;

    try {
            java.lang.ClassLoader cl = getClassLoader();
            if (!mPackageName.equals("android")) {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                        "initializeJavaContextClassLoader");
                initializeJavaContextClassLoader();
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            appContext.setOuterContext(app);
        } catch (Exception e) {
        ...
    

    终于看到了有关 ClassLoader 的部分。下面具体看 ClassLoader 是如何创建的。 如图上右上角。

  3. LoadedApk.getClassLoader()

    首先假设这个时候 mClassLoader 是空的,然后去看一下 ClassLoader 的创建:(其实这个时候已经被创建出来了)

     public ClassLoader getClassLoader() {
        synchronized (this) {
            if (mClassLoader == null) {
                createOrUpdateClassLoaderLocked(null /*addedPaths*/);
            }
            
            return mClassLoader;
        }
     }
    

    注:实际上 LoadedApk.getClassLoader() 在这里并不为 null, ClassLoader 真正创建是在程序的前面,这里暂不做分析.

  4. LoadedApk.createOrUpdateClassLoaderLocked(null)

    在这个方法里面,LoadedApkmIncludeCode = true;

    会走到:

    if (mClassLoader == null) {
            // Temporarily disable logging of disk reads on the Looper thread
            // as this is early and necessary.
            StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
    
            mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip,
                    mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
                    libraryPermittedPath, mBaseClassLoader);
    
            StrictMode.setThreadPolicy(oldPolicy);
            // Setup the class loader paths for profiling.
            needToSetupJitProfiles = true;
        }
        
    

    那么接下来,看一下 ApplicationLoaders.getDefault().getClassLoader(...)

    注: mIncludeCode 是一个标志位,去加载 APK 时是否需要加载它的源码。mIncludeCode 在一些比较 hack 的方式去获取其他 apk 的 context 时, truefalse 是有区别的。

  5. ApplicationLoaders 这个类

    对于 ApplicationLoaders 这个类,是个单例,它里面维护了一个 mLoaders, 它是一个 mapkeystring (可以看做是 包名),valueClassLoader (类加载器),

    看一下 getClassLoader() 的实现:

    public ClassLoader getClassLoader(String zip, ... ) {
        
        ...
        // 首先 检查 mLoaders map 里 是否有该 loader, 有即返回,
        //没有则创建一个新的 pathClassLoader, 并把新建的 loader 加入 mLoaders
        
        ClassLoader loader = mLoaders.get(zip);
       if (loader != null) {
           return loader;
       }
       
       PathClassLoader pathClassloader = PathClassLoaderFactory.createClassLoader(...);
       ...
       mLoaders.put(zip, pathClassloader);
       
       return pathClassloader;
        
        ... 
    }
    

    到这里为止,一个 app 对应的进程的 ClassLoader 才被创建成功.

    注:这里应该注意到 ApplicationLoaders 里面是可以对应保存多个 APK 的 ClassLoader

上述 1~9 是 app 启动时的一系列动作,7~9 分析的是 ClassLoader 的创建过程。下面我们看一下 Application 的创建过程。

从第 6 条开始:

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

如图上右边 Application 创建时序图

  1. Instrumentation 是什么?

    Instrumentationandroid 系统里面的一套控制方法或者“钩子”,我理解的是,它可以在正常的生命周期(正常是有系统控制的)之外控制 android 的运行, 拿到一些流程控制的时机,然后我们就可以在这些时机里面写一些处理我们想要的东西。它提供了各种流程控制方法,例如下面的方法:

    callActivityOnCreate()-----> 对应着系统的 onCreate();
        
    callActivityOnStart()-----> 对应着系统的 onStart();
        
    callActivityOnDestroy()-----> 对应着系统的 onDestroy();
    

    所以 当我们去新建一个类, 继承与 Instrumentation, 重写一些方法,在这些方法里面我们就可以自由的做一些控制的事情,例如,添加自动测试模块等。

    Instrumentation.newApplication(cl, appClass, appContext) 的三个参数:

    • cl 根据上面可知道,它是 ClassLoader

    • appClass 当前 app application 的名字:

      appClass = mApplicationInfo.className; 
      
    • appContext: 是当前这个 app 对应的 context

      作为参数去创建 application 时, 在 application.attach(context), 把该 context 作为 application 的 context.

  2. Instrumentation.newApplication(...)

    代码:

    return newApplication(cl.loadClass(className), context);
    

    本质上是通过 ClassLoader 根据路径名去 loadClass(className)

    结合 ClassLoader 的 loadClass() 的逻辑.

    VMClassLoader

  3. Instrumentation.newApplication(clazz, context);

    代码:

    Application app = (Application)clazz.newInstance();
    app.attach(context);
    return app;
    

    实例话 application 对象,然后赋值给 app, return.

    上述实际上已经完成了 application 对象的建立

  1. Application 的 app.attach(context) 是做什么?

    跳转到 Application 中,进入到代码里可以看到:

    /* package */ final void attach(Context context) {
        attachBaseContext(context);
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
    }
    

    手动调用了 attachBaseContext(context) 这个方法。

    注:手动跳转到 ContextWrapper.attachBaseContext()

    在 Application 的回调方法里面,我们通常会用到的两个方法:

    • attachBaseContext(context)

    • onCreate()

    当一个 Application 建立起时, 会首先调用 attachBaseContext() 这个方法。那么什么时候,才会调用 onCreate() 呢?

    接着去寻找调用 onCreate() 的时机。

  2. Application 调用 onCreate()

    回到上面的第 5 步: ActivityThread.handleBindApplication(data)

    在其方法里面:

    Application app = data.info.makeApplication(data.restrictedBackupMode, null);
    
    mInitialApplication = app;
    ...
    
     try {
         mInstrumentation.callApplicationOnCreate(app);
     } catch (Exception e) {
     ...
    

    这个时候,调用了 mInstrumentation.callApplicationOnCreate(app);, 调用位置标注为 C 处

    注:makeApplication(...) 调用时,传入的第二个参数为 instrumentation = null, 在 makeApplication(...) 下有关于 instrumentation 的判断

  3. Instrumentation.callApplicationOnCreate(app)

    这个方法里面比较简单:

    // app 为 application 对象
    app.onCreate();
    

    所以在 Application 里面 attachBaseContext() 是早于 onCreate() 的调用的。

    如图所示:调用 application.attachBaseContext() 的位置是 A, 调用 application.onCreate() 的位置是 C.

Application 中 getApplicationContext() 与上述两个方法的关系

在代码调用中,

  • attachBaseContext() 里调用 getApplicationContext() 返回的为 null;

  • onCreate() 里调用 getApplicationContext() 返回的不为 null;

为什么呢? 图示如左下角:

看一下 getApplicationContext() 调用栈:

  1. 会首先在 ContextWrapper.java 里面 getApplicationContext():

    return mBase.getApplicationContext();
    
  2. mBase 是什么?

    是一个 Context 对象,在 Application.attachBaseContext() 时被赋值的。

    ContextImpl.java 里面去看。

  3. ContextImpl.getApplicationContext()

    代码如下:

    return (mPackageInfo != null) ?
                mPackageInfo.getApplication() : mMainThread.getApplication();
    

    梳理一下逻辑:

    1. mPackageInfo 什么时候赋值的?是否为 null

      mPackageInfoLoadedApk 的对象

      mBase 是在 LoadedApk 通过 ContextImpl.createAppContext(mActivityThread, this) 里创建的,
      mPackageInfo = packageInfo; 出现在 ContextImpl 的构造函数里面,不为 null, 为 this( 为 LoadedApk)

    2. mPackageInfo.getApplication()

      返回的是 return mApplication;

      mApplication 是什么时候赋值的呢?

  4. 在 mPackageInfo.getApplication() 里 mApplication 的赋值

    在 LoadedApk.getApplication() 里面 对 mApplication 的赋值只有一处, 出现在 LoadedApk.makeApplication() 里面

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

    我们已经知道,在 mActivityThread.mInstrumentation.newApplication(...) 的时候, Application 已经调用了 attachBaseContext() 所以当在 attachBaseContext() 里面调用 getApplicationContext() 时,返回的值为 null.

    而 mApplication = app; 之后,才执行了 Application.onCreate().

总结:

从上述步骤,总结一下,可以看到:

  1. ClassLoader 的创建要早于这个 Apk 里所有的 Class;

  2. PathClassLoader 是和 APK 相互对应的, PathClassLoaderDexPathList 是与 APK 的安装路径一一对应的;

  3. 一个进程是可以对应多个 ClassLoader 的。 在 ApplicationLoader 里面有一个 ArrayMap mLoaders;

  4. Application 的创建是利用 ClassLoaderloadclass() 实现的

  5. ApplicationattachBaseContext() 是早于 onCreate() 的调用的

  6. getApplicationContext() 不为 null 是晚于 attachBaseContext(), 早于 onCreate() 方法的

坑边闲话

关于这部分的内容,有些枯燥,查看源码的过程比较枯燥,这里面的源码具体 api 是 26 还是多少,暂时不定,因为后面你如果自己看源码可能会发现 api 已经修改了,但大体是这样一个流程。

希望能在查看这个流程中得到意外的收获~

当然文中不可避免可能会出现错误,我的一些理解可能不足够准确,希望不会对看到文章的同学造成困扰。

参考链接:

  1. 以 ClassLoader 为视角看 Android 应用的启动过程

  2. ClassLoader的来源

  3. 理解Android进程创建流程

  4. Android 源码, Zygote.java, ActivityManagerServer.java, Process, ZygoteInit.java, ActivityThread.java, LoadedApk.java, ApplicationLoaders.java

你可能感兴趣的:(Android App 启动时的操作之 ClassLoader 和 Application 初始化)