美文网首页android组件化 参照大神1-Android开发知识
Android插件化框架之动态加载Activity(一)

Android插件化框架之动态加载Activity(一)

作者: jjlanbupt | 来源:发表于2017-04-04 23:15 被阅读954次

    上一篇文章Android插件化框架系列之类加载器分析了android类加载器并成功加载了一个插件apk中的类。接下来将实现启动插件中的activity,包括如何加载资源等内容。分为以下几方面内容。
    1. context继承结构
    2. activity的启动流程分析
    3. 动态加载activity原理分析
    4. 动手实现activity的动态加载
    本篇文章主要分析一下context的相关知识点。
    想要动态加载一个activity,有必要先弄清楚context的继承关系图及继承链中类之间的关系。

    context继承结构图
    context的继承结构整体来看采用的是装饰者的设计模式。context类是顶层的抽象组件,内部定义了大量的抽象方法。而这些方法真正的实现是由ContextImpl来实现的,ContextImpl继承自Context抽象类,并实现了Context中的抽象方法。ContextWrapper用来承担装饰者的身份,内部有一个名为mBase的Context引用,这个引用指向的就是ContextImpl。下面提两个问题。一个应用中,到底有多少个context对象?ContextImpl是何时被创建的,它的个数和Context相等吗?

    首先看第一个问题,从继承关系可以看出,一个应用的context的总数等于Activity,service,application的数量之和。下面我们重点来看一下ContextImpl是何时被创建的,从源码中翻一下。首先要搞清楚,应用的启动是从ActivityThread类的main方法开始的,启动的过程肯定存在创建application和activity的过程,所以我们的入手点应该是ActivityThread类。找到performLaunchActivity方法,activity和application的创建都是由这个方法进入的。为了方便阅读,将源码中的部分代码删掉了,留下了关键代码,可见该方法通过Instrumentation的newActivity创建了activity类,接着完成了application的创建(没有创建Application的情况下),接着通过createBaseContextForActivity方法为该activity创建context,再调用attach方法进行绑定。

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        //省略代码
        Activity activity = null;
        try {
          java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
          activity = mInstrumentation.newActivity(
                  cl, component.getClassName(), r.intent);
          StrictMode.incrementExpectedActivityCount(activity.getClass());
          r.intent.setExtrasClassLoader(cl);
          r.intent.prepareToEnterProcess();
          //省略代码
        try {
          Application app = r.packageInfo.makeApplication(false, mInstrumentation);
          //省略代码
          if (activity != null) {
            Context appContext = createBaseContextForActivity(r, activity);
            CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
            Configuration config = new Configuration(mCompatConfiguration);
            if (r.overrideConfig != null) {
              config.updateFrom(r.overrideConfig);
            }
            //省略代码
            activity.attach(appContext, this, getInstrumentation(), r.token,
                    r.ident, app, r.intent, r.activityInfo, title, r.parent,
                    r.embeddedID, r.lastNonConfigurationInstances, config,
                    r.referrer, r.voiceInteractor, window);
            if (r.isPersistable()) {
              mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
            } else {
              mInstrumentation.callActivityOnCreate(activity, r.state);
            }
            //省略代码
        return activity;
      }
    

    跟进去createBaseContextForActivity方法

     private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
            int displayId = Display.DEFAULT_DISPLAY;
            try {
                displayId = ActivityManagerNative.getDefault().getActivityDisplayId(r.token);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
    
            ContextImpl appContext = ContextImpl.createActivityContext(
                    this, r.packageInfo, r.token, displayId, r.overrideConfig);
            appContext.setOuterContext(activity);
            Context baseContext = appContext;
           //省略代码
            return baseContext;
        }
    
    

    找到啦!activity的ContextImpl是通过 ContextImpl.createActivityContext方法完成的。现在已经新建了activity类,创建了ContextImpl,那么二者是如何联系起来的呢,从performLaunchActivity方法的流程上看,应该在activity的attach方法上完成的,跟进去看一下,果然,在Activity的attach方法中调用了attachBaseContext方法,将创建好的ContextImpl赋值给了mBase变量,接下来就是通过Instrumentation类进行activity生命周期的回调,所以attachBaseContext方法是在onCreate方法之前调用的。

      final void attach(Context context, ActivityThread aThread,
                Instrumentation instr, IBinder token, int ident,
                Application application, Intent intent, ActivityInfo info,
                CharSequence title, Activity parent, String id,
                NonConfigurationInstances lastNonConfigurationInstances,
                Configuration config, String referrer, IVoiceInteractor voiceInteractor,
                Window window) {
            attachBaseContext(context);
    //省略代码
    }
    

    我们再来看看Application的创建和mBase的赋值过程。在performLaunchActivity方法中,在创建activity的ContextImpl的方法前,调用了这一条语句,跟进去看一下。

    Application app = r.packageInfo.makeApplication(false, mInstrumentation);
    
      public Application makeApplication(boolean forceDefaultAppClass,
                Instrumentation instrumentation) {
            if (mApplication != null) {
                return mApplication;
            }
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");
            Application app = null;
             //省略代码
                ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
                app = mActivityThread.mInstrumentation.newApplication(
                        cl, appClass, appContext);
                appContext.setOuterContext(app);
            } catch (Exception e) {
                if (!mActivityThread.mInstrumentation.onException(app, e)) {
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    throw new RuntimeException(
                        "Unable to instantiate application " + appClass
                        + ": " + e.toString(), e);
                }
            }
            mActivityThread.mAllApplications.add(app);
            mApplication = app;
            if (instrumentation != null) {
                try {
                    instrumentation.callApplicationOnCreate(app);
                } catch (Exception e) {
    
            return app;
        }
    

    可见,在makeApplication方法中首先通过调用ContextImpl的createAppContext方法创建了Application的ContextImpl,然后通过调用
    Intrumentation的newApplication方法完成Application类的创建,与创建activity不同,在newApplication方法中,直接调用了Application的attach方法,在attach方法中调用了attachBaseContext方法,完成了mBase变量的赋值。service的mBase的赋值过程与之类似,此处就不分析了。可以得出ContextImpl的个数与Context的个数是相等的。

        static public Application newApplication(Class<?> clazz, Context context)
                throws InstantiationException, IllegalAccessException, 
                ClassNotFoundException {
            Application app = (Application)clazz.newInstance();
            app.attach(context);
            return app;
        }
    

    本篇文章,主要分析了Context类的继承关系,并从源码中分析了mBase变量是如何与application和activity完成绑定的,加深理解,之所以分析mBase的赋值过程,是因为后面动态加载需要对其进行一定的替换处理,具体实现,下一篇文章会介绍。
    此外,通过分析,也能彻底的清楚为什么application的attachBaseContext方法会先于onCreate方法调用,以及Application的onCreate方法先于activity的onCreate。时间仓促,有问题欢迎交流指正。

    相关文章

      网友评论

      本文标题:Android插件化框架之动态加载Activity(一)

      本文链接:https://www.haomeiwen.com/subject/fuyaottx.html