Context完全解析(二)Application的Context创建过程

     Android应用由四大组件组成,组件的运行要有一个完整的Android工程环境,在这个环境下,Activity、Service等系统组件 才能够正常工作。如果我们手动通过new的方式来创建这些组件,那么这些组件是不能正常工作的,因为他们需要一个运行环境 Context,而这个Context的构造过程是由系统帮我们完成的。
     应用程序在以下几种情况下会创建一个Context实例:(1)创建Application对象时, 每一个应用程序进程只有一个Application对象(2) 创建Activity对象时,(3)创建Service对象时。
    我们知道Android应用程序进程的入口类 ActivityThread ,在这个类中将完成四大组件的创建工作,当然也包括其中的Context的创建,而真正实现了 Context 类的是 Contextlmpl 类, 所以本节就来研究这个 “ 真正的 Context" ( Contextlmpl) 是如何创建的

 Application的Context创建过程

    我们都知道,Android应用程序进程的入口函数是 android.app.ActivityThread类的静态成员函数main,在Android中想要启动一个进程,必须通过四大组件来启动。当启动一个组件时,如果发现该组件对应的进程还没有启动,于是会请求系统来创建这个进程, 一个新的应用程序进程在创建完成之后,随即进入ActivityThread类的静态成员函数main。
public final class ActivityThread {
    ......
    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");
    }

    ......
}
    新的应用程序进程在启动时, 主要做了两件事情, 一是:在进程中创建 一 个ActivityThread对象, 并且调用它的成员函数attach向ActivityManagerService 发送 一 个进程启动完成的通知。 二是:调用Looper类的静态成员函数prepareMainLooper创建建 一 个消息循环, 并且在向 ActivityManagerService发送启动完成通知之后, 使得当前进程进入到这个消息循环中。这里我们主要关心的Application的创建过程,也就是通知完AMS进程启动完成后,AMS做了什么事,至于消息机制会在下一章讲解。

public final class ActivityThread {
    ...... 
    private void attach(boolean system) {
        sCurrentActivityThread = this;
        mSystemThread = system;
        if (!system) {
            ......
            final IActivityManager mgr = ActivityManager.getService();
            try {
                mgr.attachApplication(mAppThread);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
            ......
        } else {
            ......
            try {
                mInstrumentation = new Instrumentation();
                ContextImpl context = ContextImpl.createAppContext(
                        this, getSystemContext().mPackageInfo);
                mInitialApplication = context.mPackageInfo.makeApplication(true, null);
                mInitialApplication.onCreate();
            } catch (Exception e) {
                throw new RuntimeException(
                        "Unable to instantiate Application():" + e.toString(), e);
            }
        }

       ......
    }
  attach方法中入参system表示是否为系统进程,这个只有在 Framework 进程启动时,会调用ActivityThread的 静态方法systemMain(),该方法里会调用 attach方法并传入true,此处我们只需要分析为false的情况。attachApplication方法会向AMS发送一个进程间通讯消息,当AMS接收到消息后,会通过远程调用到ApplicationThread的bindApplication()方法,该方法的参数中 有 一个 是Applicationlnfo类型的 ,它是实现了Parcelable接口的数据类,它是由AmS创建的,通过IPC 传递到ActivityThread中。 在bindApplication()方法中, 会用以上两种参数构造 一 个本地AppBindData数据类, 然后再去调用
handleBindApplication()方法,并且将AppBindData作为参数传入
public final class ActivityThread {
    ...... 
    private void handleBindApplication(AppBindData data) {
        ......
        data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
        ......
        app = data.info.makeApplication(data.restrictedBackupMode, null);
        mInitialApplication = app;   
        ......
    }
    ......   
}
   在调用 handleBindApplication()的时候,AppBindData 对象中的 info变量还是空值, 然后会使用
data.info  = getPackageInfoNoCheck()方法为 info变量赋值  。接着在 该方法中会调用LoadedApk类的 makeApplication方法来创建一个Application对象,在LoadedApk类中会把创建的Application对象保存在mApplication变量中,返回这个变量又会赋值给ActivityThread类中的mInitialApplication变量,所以通常这两个变量的值是一样的
public final class ActivityThread {
    ......  
    @GuardedBy("mResourcesManager")    
    final ArrayMap<String, WeakReference<LoadedApk>> mPackages = new ArrayMap<>();
    @GuardedBy("mResourcesManager")
    final ArrayMap<String, WeakReference<LoadedApk>> mResourcePackages = new ArrayMap<>();
    ......
    public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
            CompatibilityInfo compatInfo) {
        return getPackageInfo(ai, compatInfo, null, false, true, false);
     }
    ......
    private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
            ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
            boolean registerPackage) {
        final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid));
        synchronized (mResourcesManager) {
            WeakReference<LoadedApk> ref;
            if (differentUser) {
                // Caching not supported across users
                ref = null;
            } else if (includeCode) {
                ref = 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())) {
                  
                packageInfo =
                    new LoadedApk(this, aInfo, compatInfo, baseLoader,
                            securityViolation, includeCode &&
                            (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);

                ......

                if (differentUser) {
                    // Caching not supported across users
                } else if (includeCode) {
                    mPackages.put(aInfo.packageName,
                            new WeakReference<LoadedApk>(packageInfo));
                } else {
                    mResourcePackages.put(aInfo.packageName,
                            new WeakReference<LoadedApk>(packageInfo));
                }
            }
            return packageInfo;
        }
    }
 getPackageInfo方法的内部实际上会根据AppBindData 中的Applicationlnfo 中的packageName创建 一个 LoadedApk 对象,并把将这个对象设置为弱引用。从LoadedApk的构造方法中可以看出,LoadedApk中将会保存一个ActivityThread类实例对象,一个ApplicationInfo类实例对象,以及后面设置的Application实例对象。 然后将这个弱引用对象保存在以 packageName为键,弱引用对象为值得的ActivityThread 类中的实例变量Map集合中。 显然,一个应用程序中的所有Activity或者Application对象对应的packageName 都是 一 样的, 所以ActivityThread 中会产生 一 个全局LoadedApk对象 。但是,LoadedApk对象是一个弱引用,有可能会被回收,因此如果我们在其他使用的地方再次调用了getPackageInfo方法时,有可能是新new出来的对象,此时,LoadedApk类中的mApplication有可能就为null,后面会在创建Activity,Service的Context的时候还会再调用该方法来获取一个LoadedApk类对象
public final class LoadedApk {
    ......
    public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
        (1)检查Application是否已经创建
        if (mApplication != null) {
            return mApplication;
        }

        ......

        Application app = null;
        (2)设置Application的类名
        String appClass = mApplicationInfo.className;
        if (forceDefaultAppClass || (appClass == null)) {
            appClass = "android.app.Application";
        }

        try {
            (3)创建Appication及其Context
            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) {
            ......
        }
        (4)保存创建的Appication
        mActivityThread.mAllApplications.add(app);
        mApplication = app;
        
        (5)调用Application的onCreate方法
        if (instrumentation != null) {
            try {
                instrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                ......
            }
        }

        ......

        return app;
    }
    ......   
}
    变量 mlnstrumentation是 一 个Instrumentation类型对象,因此,前面调用它 的 new Application方法就会进入Instrumentation类中 。
public class Instrumentation {
    ......
    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);
        return app;
    }
    ......
}

    (1)检查Application是否已经创建:一个应用程序进程中只能创建一个 Application对象,如果之前已经创建过Application对象,则之后在同一个进程中启动其他组件时,就不会再创建Application对象,Application对象在整个进程中是一个单例。
     (2)设置Application的类名: Application默认的类名是"android.app.Application",如果在AndroidManifest.xml文件中,没有对Application节点中的name属性指定自定义的 Application类名,则系统就会默认创建一个 android.app.Application的实例。
    (3)创建Appication及其Context:如果之前没有创建过Application,则调用ContextImpl类的静态方法createAppContext来
创建一个 Application的 ContextImpl实例,之后会调用newApplication方法,传入 ContextImpl实例,以及 之前设置的appClass,来创建该类名对应的一个 Application实例。在 newApplication方法内部,创建完 Application实例后,会调用其attach方法并传入ContextImpl实例,在attach方法中又会调用其父类ContextWrapper中的attachBaseContext()方法,将 ContextImpl对象设置给内部实例变量mBase, 这样 Application内部就持有了Context的实现类ContextImpl实例 。最后调用ContextImpl的实例方法setOuterContext,传入这个 Application实例,这样 ContextImpl实例也会持有Context的包装类ContextWrapper 实例。
    (4)保存创建的Appication:创建完一个Application对象后,将其保存在mApplication实例变量中,由于一个应用程序进程中的LoadedApk对象也是一个单例,因此一个进程中只能创建一个Application对象。
    (5)回调Application的onCreate方法:当创建完一个Application对象后,就会回调其onCreate方法,在该方法中可以做一些全局的初始化工作。另外需要注意的是,在Application的构造方法中,ContextImpl实例还没有设置进去,因此还不能使用Context中的方法,否则报空指针异常,因此初始化工作要在onCreate方法中完成。
     至此,Application Context的创建过程就介绍完毕,在应用程序开发中,可以通过ContextWrapper类的getApplicationContext()实例方 获创建的Application对象 ,这个对象就是保存LoadedApk类中的mApplication变量,如果LoadedApk类的实例为null,则会返回ActivityThread类中的mInitialApplication变量,因为这两个值是同一个对象。因此,我们可以在Activity,Service中都可以获取这个对象。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值