美文网首页
Android 插件化原理解析

Android 插件化原理解析

作者: 搬砖写Bug | 来源:发表于2018-08-01 10:39 被阅读33次

    <未完成,待续>

    前期学习计划:

    ClassLoad 加载机制

    BaseClassLoader:系统启动时创建,加载 Framework 层级和应用使用的系统类
    PathClassLoader:应用启动时创建,加载/data
    目录下应用自己的类,只能加载已经安装过的内部存储上的类
    DexClassLoader:可以加载 jar/apk/dex,也能从SD卡中加载未安装的apk

    ClassLoader双亲代理模型
    loadClass方法在加载一个类的实例的时候:
    1.先查询当前ClassLoader实例是否加载过此类,有就返回;
    2.如果没有,查询Parent是否已经加载过此类,如果已经加载过,就直接返回Parent加载的类;
    3.如果继承路线上的ClassLoader都没有加载,才由Child执行类的加载工作;
    这样做有个明显的特点,如果一个类被位于树根的ClassLoader加载过,那么在以后整个系统的生命周期内,这个类永远不会被重新加载。

    作用:
    首先是共享功能,一些Framework层级的类一旦被顶层的ClassLoader加载过就缓存在内存里面,以后任何地方用到都不需要重新加载。
    除此之外还有隔离功能,不同继承路线上的ClassLoader加载的类肯定不是同一个类,这样的限制避免了用户自己的代码冒充核心类库的类访问核心类库包可见成员的情况。这也好理解,一些系统层级的类会在系统初始化的时候被加载,比如java.lang.String,如果在一个应用里面能够简单地用自定义的String类把这个系统的String类给替换掉,那将会有严重的安全问题。

    使用ClassLoader一些需要注意的问题
    如果希望通过动态加载的方式,加载一个新版本的dex文件,使用里面的新类替换原有的旧类,从而修复原有类的BUG,那么你必须保证在加载新类的时候,旧类还没有被加载,因为如果已经加载过旧类,那么ClassLoader会一直优先使用旧类。
    如果旧类总是优先于新类被加载,我们也可以使用一个与加载旧类的ClassLoader没有树的继承关系的另一个ClassLoader来加载新类,因为ClassLoader只会检查其Parent有没有加载过当前要加载的类,如果两个ClassLoader没有继承关系,那么旧类和新类都能被加载。
    不过这样一来又有另一个问题了,在Java中,只有当两个实例的类名、包名以及加载其的ClassLoader都相同,才会被认为是同一种类型。上面分别加载的新类和旧类,虽然包名和类名都完全一样,但是由于加载的ClassLoader不同,所以并不是同一种类型,在实际使用中可能会出现类型不符异常。
    同一个Class = 相同的 ClassName + PackageName + ClassLoader

    Activity 启动流程

    //在 ApplicationThread 的scheduleLaunchActivity() 方法中创建了 ActivityClientRecord 对象

    public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                    ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
                    CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
                    int procState, Bundle state, PersistableBundle persistentState,
                    List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
                    boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
    
                ActivityClientRecord r = new ActivityClientRecord();
    
                r.token = token;
                r.ident = ident;
                r.intent = intent;
                ......
    
                sendMessage(H.LAUNCH_ACTIVITY, r);
     }
    
     private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    
        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }
    
        ComponentName component = r.intent.getComponent();
        if (component == null) {
            component = r.intent.resolveActivity(
                mInitialApplication.getPackageManager());
            r.intent.setComponent(component);
        }
    
        if (r.activityInfo.targetActivity != null) {
            component = new ComponentName(r.activityInfo.packageName,
                    r.activityInfo.targetActivity);
        }
    
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            // 创建 activity 实例
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
     
        } catch (Exception e) {
        ......
        }
    
        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            if (activity != null) {
                Context appContext = createBaseContextForActivity(r, activity);
                ......
                //执行 Activity 的 attach() 方法,此方法中会把AmS 的 token 传递过去
                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);
                ......
                
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }
                ......
                // 把创建的 activity 赋值到 ActivityClientRecord 对象中,之后就能通过 token 来找到对应的 Activity
                r.activity = activity;
                ......
            }
            ......
            mActivities.put(r.token, r);
        } 
        return activity;
    }
    

    Activity 和 AmS 之间如何对应识别的?
    ActivityThread.java 中有一个ArrayMap 类型的成员变量:

         final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
    

    它是在 performLaunchActivity() 中添加应用进程中所有的 Activity 对象 :

         mActivities.put(r.token, r);
    

    之后 AmS 调用 Activity 生命周期方法时都会传入 token 参数来告诉应用进程当前应该操作哪一个 Activity,例如回调 onResume() 方法:

    public final void scheduleResumeActivity(IBinder token, int processState,
           boolean isForward, Bundle resumeArgs) {
       updateProcessState(processState, false);
       sendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0);
    }
           
    public final ActivityClientRecord performResumeActivity(IBinder token,
           boolean clearHide) {
       ActivityClientRecord r = mActivities.get(token);
       ......
    }
    

    参考:
    Android插件化开发之动态加载基础之ClassLoader工作机制
    Android 插件化原理解析——插件加载机制

    相关文章

      网友评论

          本文标题:Android 插件化原理解析

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