Tinker接入及源码分析(二)

来源:互联网 发布:js中array长度 编辑:程序博客网 时间:2024/06/01 11:47

转载地址:http://qlm.pw/2017/01/07/tinker%E6%8E%A5%E5%85%A5%E5%8F%8A%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%EF%BC%88%E4%BA%8C%EF%BC%89/

该系列文章分析基于 Tinker1.7.6 版本

Tinker项目地址:https://github.com/Tencent/tinker

Tinker接入及源码分析(一):简单介绍以及如何接入

Tinker接入及源码分析(二):加载补丁源码分析

Tinker接入及源码分析(三):合成补丁源码分析

再次推荐大家阅读官方wiki:https://github.com/Tencent/tinker/wiki

上篇文章也提及了Tinker的热修复原理,这里再重复一遍:

先简单的说一下Tinker框架热修复的原理,主要是dex文件的修复,不再涉及资源文件以及so文件的修复,通过对比原dex文件(存在bug)与现dex文件(bug已修复)生成差异包,生成的差异包作为补丁包下发给客户端,客户端做一系列校验之后,将下发的差异包与本应用的dex文件合并成成全量的dex文件,并进行opt优化,在应用重启的时候,会在TinkerApplication中加载优化过的全量dex文件,加载过程与QQ空间热修复方案类似,将dex文件插入到DexPathList 中 dexElements的前面。

下面先从简单的入手,分析补丁文件的加载过程,加载之前我们需要明确目前补丁文件已经push到手机,并且通过校验,使用dexDiff算法合成全量补丁并保存到应用data目录下 /data/data/package_name/tinker/

还记得TinkerApplication 和 DefaultApplicationLike 吗?我们就从应用的入口开始分析:

public class SampleApplication extends TinkerApplication {    public SampleApplication() {      super(        //tinkerFlags, tinker支持的类型,dex,library,还是全部都支持!        ShareConstants.TINKER_ENABLE_ALL,        //ApplicationLike的实现类,只能传递字符串         "tinker.sample.android.app.SampleApplicationLike",        //Tinker的加载器,一般来说用默认的即可        "com.tencent.tinker.loader.TinkerLoader",        //tinkerLoadVerifyFlag, 运行加载时是否校验dex与,ib与res的Md5        false);    }  }

TinkerApplication.:

/**     * Hook for sub-classes to run logic after the {@link Application#attachBaseContext} has been     * called but before the delegate is created. Implementors should be very careful what they do     * here since {@link android.app.Application#onCreate} will not have yet been called.     */    private void onBaseContextAttached(Context base) {        applicationStartElapsedTime = SystemClock.elapsedRealtime();        applicationStartMillisTime = System.currentTimeMillis();        loadTinker();        ensureDelegate();        try {            Method method = ShareReflectUtil.findMethod(delegate, "onBaseContextAttached", Context.class);            method.invoke(delegate, base);        } catch (Throwable t) {            throw new TinkerRuntimeException("onBaseContextAttached method not found", t);        }        //reset save mode        if (useSafeMode) {            String processName = ShareTinkerInternals.getProcessName(this);            String preferName = ShareConstants.TINKER_OWN_PREFERENCE_CONFIG + processName;            SharedPreferences sp = getSharedPreferences(preferName, Context.MODE_PRIVATE);            sp.edit().putInt(ShareConstants.TINKER_SAFE_MODE_COUNT, 0).commit();        }    }

其中loadTinker()方法是通过反射初始化我们之前传过来的com.tencent.tinker.loader.TinkerLoader,并且调用它的tryLoad方法,该方法也是加载补丁包的关键所在,我们先放一放,继续往下看。

ensureDelegate() 方法最终会调用createDelegate(),createDelegate()是通过反射初始化化我们传过来的tinker.sample.android.app.SampleApplicationLike,最后会将初始化好的对象赋值给delegate。

private Object createDelegate() {        try {            // Use reflection to create the delegate so it doesn't need to go into the primary dex.            // And we can also patch it            Class<?> delegateClass = Class.forName(delegateClassName, false, getClassLoader());            Constructor<?> constructor = delegateClass.getConstructor(Application.class, int.class, boolean.class, long.class, long.class,                Intent.class, Resources[].class, ClassLoader[].class, AssetManager[].class);            return constructor.newInstance(this, tinkerFlags, tinkerLoadVerifyFlag,                applicationStartElapsedTime, applicationStartMillisTime,                tinkerResultIntent, resources, classLoader, assetManager);        } catch (Throwable e) {            throw new TinkerRuntimeException("createDelegate failed", e);        }    }

接下来便是在TinkerApplication各个生命周期方法中通过反射调用代理的ApplicationLike中对应的生命周期方法。比如onBaseContextAttached中的:

try {    Method method = ShareReflectUtil.findMethod(delegate, "onBaseContextAttached", Context.class);    method.invoke(delegate, base);} catch (Throwable t) {    throw new TinkerRuntimeException("onBaseContextAttached method not found", t);}

以及其他方法:

 @Override    public void onCreate() {        super.onCreate();        ensureDelegate();        delegateMethod("onCreate");    }    @Override    public void onTerminate() {        super.onTerminate();        delegateMethod("onTerminate");    }    @Override    public void onLowMemory() {        super.onLowMemory();        delegateMethod("onLowMemory");    }

下面主要来看一下加载补丁的方法loadTinker():

private void loadTinker() {        //disable tinker, not need to install        if (tinkerFlags == TINKER_DISABLE) {            return;        }        tinkerResultIntent = new Intent();        try {            //reflect tinker loader, because loaderClass may be define by user!            Class<?> tinkerLoadClass = Class.forName(loaderClassName, false, getClassLoader());            Method loadMethod = tinkerLoadClass.getMethod(TINKER_LOADER_METHOD, TinkerApplication.class, int.class, boolean.class);            Constructor<?> constructor = tinkerLoadClass.getConstructor();            tinkerResultIntent = (Intent) loadMethod.invoke(constructor.newInstance(), this, tinkerFlags, tinkerLoadVerifyFlag);        } catch (Throwable e) {            //has exception, put exception error code            ShareIntentUtil.setIntentReturnCode(tinkerResultIntent, ShareConstants.ERROR_LOAD_PATCH_UNKNOWN_EXCEPTION);            tinkerResultIntent.putExtra(INTENT_PATCH_EXCEPTION, e);        }    }

其中loaderClassName是我们传过来的:com.tencent.tinker.loader.TinkerLoader,最终会调用该类的tryLoad方法,下面我们转到TinkerLoader,看tryLoad方法:

/**     * only main process can handle patch version change or incomplete     */    @Override    public Intent tryLoad(TinkerApplication app, int tinkerFlag, boolean tinkerLoadVerifyFlag) {        Intent resultIntent = new Intent();        long begin = SystemClock.elapsedRealtime();        tryLoadPatchFilesInternal(app, tinkerFlag, tinkerLoadVerifyFlag, resultIntent);        long cost = SystemClock.elapsedRealtime() - begin;        ShareIntentUtil.setIntentPatchCostTime(resultIntent, cost);        return resultIntent;    }

调用tryLoadPatchFilesInternal,并计算耗时:

private void tryLoadPatchFilesInternal(TinkerApplication app, int tinkerFlag, boolean tinkerLoadVerifyFlag, Intent resultIntent) {        if (!ShareTinkerInternals.isTinkerEnabled(tinkerFlag)) {            ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_DISABLE);            return;        }        //tinker        File patchDirectoryFile = SharePatchFileUtil.getPatchDirectory(app);        if (patchDirectoryFile == null) {            Log.w(TAG, "tryLoadPatchFiles:getPatchDirectory == null");            //treat as not exist            ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_DIRECTORY_NOT_EXIST);            return;        }        String patchDirectoryPath = patchDirectoryFile.getAbsolutePath();        //check patch directory whether exist        if (!patchDirectoryFile.exists()) {           //...        }        //tinker/patch.info        File patchInfoFile = SharePatchFileUtil.getPatchInfoFile(patchDirectoryPath);        //check patch info file whether exist        if (!patchInfoFile.exists()) {            //...        }        //old = 641e634c5b8f1649c75caf73794acbdf        //new = 2c150d8560334966952678930ba67fa8        File patchInfoLockFile = SharePatchFileUtil.getPatchInfoLockFile(patchDirectoryPath);        patchInfo = SharePatchInfo.readAndCheckPropertyWithLock(patchInfoFile, patchInfoLockFile);        if (patchInfo == null) {            ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_INFO_CORRUPTED);            return;        }        String oldVersion = patchInfo.oldVersion;        String newVersion = patchInfo.newVersion;        if (oldVersion == null || newVersion == null) {            //it is nice to clean patch            Log.w(TAG, "tryLoadPatchFiles:onPatchInfoCorrupted");            ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_INFO_CORRUPTED);            return;        }        resultIntent.putExtra(ShareIntentUtil.INTENT_PATCH_OLD_VERSION, oldVersion);        resultIntent.putExtra(ShareIntentUtil.INTENT_PATCH_NEW_VERSION, newVersion);        boolean mainProcess = ShareTinkerInternals.isInMainProcess(app);        boolean versionChanged = !(oldVersion.equals(newVersion));        String version = oldVersion;        if (versionChanged && mainProcess) {            version = newVersion;        }        if (ShareTinkerInternals.isNullOrNil(version)) {            Log.w(TAG, "tryLoadPatchFiles:version is blank, wait main process to restart");            ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_INFO_BLANK);            return;        }        //patch-641e634c        String patchName = SharePatchFileUtil.getPatchVersionDirectory(version);        if (patchName == null) {            Log.w(TAG, "tryLoadPatchFiles:patchName is null");            //we may delete patch info file            ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_VERSION_DIRECTORY_NOT_EXIST);            return;        }        //tinker/patch.info/patch-641e634c        String patchVersionDirectory = patchDirectoryPath + "/" + patchName;        File patchVersionDirectoryFile = new File(patchVersionDirectory);        if (!patchVersionDirectoryFile.exists()) {            Log.w(TAG, "tryLoadPatchFiles:onPatchVersionDirectoryNotFound");            //we may delete patch info file            ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_VERSION_DIRECTORY_NOT_EXIST);            return;        }        //tinker/patch.info/patch-641e634c/patch-641e634c.apk        File patchVersionFile = new File(patchVersionDirectoryFile.getAbsolutePath(), SharePatchFileUtil.getPatchVersionFile(version));        if (!patchVersionFile.exists()) {            Log.w(TAG, "tryLoadPatchFiles:onPatchVersionFileNotFound");            //we may delete patch info file            ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_VERSION_FILE_NOT_EXIST);            return;        }        ShareSecurityCheck securityCheck = new ShareSecurityCheck(app);        int returnCode = ShareTinkerInternals.checkTinkerPackage(app, tinkerFlag, patchVersionFile, securityCheck);        if (returnCode != ShareConstants.ERROR_PACKAGE_CHECK_OK) {            Log.w(TAG, "tryLoadPatchFiles:checkTinkerPackage");            resultIntent.putExtra(ShareIntentUtil.INTENT_PATCH_PACKAGE_PATCH_CHECK, returnCode);            ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_PACKAGE_CHECK_FAIL);            return;        }        resultIntent.putExtra(ShareIntentUtil.INTENT_PATCH_PACKAGE_CONFIG, securityCheck.getPackagePropertiesIfPresent());        final boolean isEnabledForDex = ShareTinkerInternals.isTinkerEnabledForDex(tinkerFlag);        if (isEnabledForDex) {            //tinker/patch.info/patch-641e634c/dex            boolean dexCheck = TinkerDexLoader.checkComplete(patchVersionDirectory, securityCheck, resultIntent);            if (!dexCheck) {                //file not found, do not load patch                Log.w(TAG, "tryLoadPatchFiles:dex check fail");                return;            }        }        final boolean isEnabledForNativeLib = ShareTinkerInternals.isTinkerEnabledForNativeLib(tinkerFlag);        if (isEnabledForNativeLib) {            //tinker/patch.info/patch-641e634c/lib            //...        }        //check resource        final boolean isEnabledForResource = ShareTinkerInternals.isTinkerEnabledForResource(tinkerFlag);        Log.w(TAG, "tryLoadPatchFiles:isEnabledForResource:" + isEnabledForResource);        if (isEnabledForResource) {            //...        }        //only work for art platform oat        boolean isSystemOTA = ShareTinkerInternals.isVmArt() && ShareTinkerInternals.isSystemOTA(patchInfo.fingerPrint);        //we should first try rewrite patch info file, if there is a error, we can't load jar        if (isSystemOTA            || (mainProcess && versionChanged)) {            //...        }        if (!checkSafeModeCount(app)) {            //...            return;        }        //now we can load patch jar        if (isEnabledForDex) {            boolean loadTinkerJars = TinkerDexLoader.loadTinkerJars(app, tinkerLoadVerifyFlag, patchVersionDirectory, resultIntent, isSystemOTA);            if (!loadTinkerJars) {                Log.w(TAG, "tryLoadPatchFiles:onPatchLoadDexesFail");                return;            }        }        //now we can load patch resource        if (isEnabledForResource) {            //...        }        //all is ok!        ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_OK);        Log.i(TAG, "tryLoadPatchFiles: load end, ok!");        return;    }

以上代码省略了部分判断补丁文件是否存在,是否有效,以及加载补丁资源文件的方法,主要检查补丁信息中的数据是否有效,校验补丁签名以及tinkerId与基准包是否一致。在校验签名时,为了加速校验速度,Tinker只校验 *_meta.txt文件,然后再根据meta文件中的md5校验其他文件。最后调用

TinkerDexLoader.loadTinkerJars(app, tinkerLoadVerifyFlag, patchVersionDirectory, resultIntent, isSystemOTA);

开始加载补丁文件:

/**     * Load tinker JARs and add them to     * the Application ClassLoader.     *     * @param application The application.     */    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)    public static boolean loadTinkerJars(Application application, boolean tinkerLoadVerifyFlag, String directory, Intent intentResult, boolean isSystemOTA) {        if (dexList.isEmpty()) {            Log.w(TAG, "there is no dex to load");            return true;        }        PathClassLoader classLoader = (PathClassLoader) TinkerDexLoader.class.getClassLoader();        if (classLoader != null) {            Log.i(TAG, "classloader: " + classLoader.toString());        } else {            Log.e(TAG, "classloader is null");            ShareIntentUtil.setIntentReturnCode(intentResult, ShareConstants.ERROR_LOAD_PATCH_VERSION_DEX_CLASSLOADER_NULL);            return false;        }        String dexPath = directory + "/" + DEX_PATH + "/";        File optimizeDir = new File(directory + "/" + DEX_OPTIMIZE_PATH);//        Log.i(TAG, "loadTinkerJars: dex path: " + dexPath);//        Log.i(TAG, "loadTinkerJars: opt path: " + optimizeDir.getAbsolutePath());        ArrayList<File> legalFiles = new ArrayList<>();        final boolean isArtPlatForm = ShareTinkerInternals.isVmArt();        for (ShareDexDiffPatchInfo info : dexList) {            //for dalvik, ignore art support dex            if (isJustArtSupportDex(info)) {                continue;            }            String path = dexPath + info.realName;            File file = new File(path);            if (tinkerLoadVerifyFlag) {                //...校验            }            legalFiles.add(file);        }        if (isSystemOTA) {            parallelOTAResult = true;            parallelOTAThrowable = null;            Log.w(TAG, "systemOTA, try parallel oat dexes!!!!!");            TinkerParallelDexOptimizer.optimizeAll(                legalFiles, optimizeDir,                new TinkerParallelDexOptimizer.ResultCallback() {                    long start;                    @Override                    public void onStart(File dexFile, File optimizedDir) {                        start = System.currentTimeMillis();                        Log.i(TAG, "start to optimize dex:" + dexFile.getPath());                    }                    @Override                    public void onSuccess(File dexFile, File optimizedDir) {                        // Do nothing.                        Log.i(TAG, "success to optimize dex " + dexFile.getPath() + "use time " + (System.currentTimeMillis() - start));                    }                    @Override                    public void onFailed(File dexFile, File optimizedDir, Throwable thr) {                        parallelOTAResult = false;                        parallelOTAThrowable = thr;                        Log.i(TAG, "fail to optimize dex " + dexFile.getPath() + "use time " + (System.currentTimeMillis() - start));                    }                }            );            if (!parallelOTAResult) {                Log.e(TAG, "parallel oat dexes failed");                intentResult.putExtra(ShareIntentUtil.INTENT_PATCH_EXCEPTION, parallelOTAThrowable);                ShareIntentUtil.setIntentReturnCode(intentResult, ShareConstants.ERROR_LOAD_PATCH_VERSION_PARALLEL_DEX_OPT_EXCEPTION);                return false;            }        }        try {            SystemClassLoaderAdder.installDexes(application, classLoader, optimizeDir, legalFiles);        } catch (Throwable e) {            Log.e(TAG, "install dexes failed");//            e.printStackTrace();            intentResult.putExtra(ShareIntentUtil.INTENT_PATCH_EXCEPTION, e);            ShareIntentUtil.setIntentReturnCode(intentResult, ShareConstants.ERROR_LOAD_PATCH_VERSION_DEX_LOAD_EXCEPTION);            return false;        }        return true;    }

以上代码根据传过来的tinkerLoadVerifyFlag选项控制是否每次加载都要验证dex的md5值,一般来说不需要,默认也是false,会节省加载时间。

然后根据传过来的isSystemOTA来决定是否OTA((Ahead-Of—Time 提前编译)优化

最后调用

SystemClassLoaderAdder.installDexes(application, classLoader, optimizeDir, legalFiles)

加载dex文件

@SuppressLint("NewApi")    public static void installDexes(Application application, PathClassLoader loader, File dexOptDir, List<File> files)        throws Throwable {        if (!files.isEmpty()) {            ClassLoader classLoader = loader;            if (Build.VERSION.SDK_INT >= 24) {                classLoader = AndroidNClassLoader.inject(loader, application);            }            //because in dalvik, if inner class is not the same classloader with it wrapper class.            //it won't fail at dex2opt            if (Build.VERSION.SDK_INT >= 23) {                V23.install(classLoader, files, dexOptDir);            } else if (Build.VERSION.SDK_INT >= 19) {                V19.install(classLoader, files, dexOptDir);            } else if (Build.VERSION.SDK_INT >= 14) {                V14.install(classLoader, files, dexOptDir);            } else {                V4.install(classLoader, files, dexOptDir);            }            //install done            sPatchDexCount = files.size();            Log.i(TAG, "after loaded classloader: " + classLoader + ", dex size:" + sPatchDexCount);            if (!checkDexInstall(classLoader)) {                //reset patch dex                SystemClassLoaderAdder.uninstallPatchDex(classLoader);                throw new TinkerRuntimeException(ShareConstants.CHECK_DEX_INSTALL_FAIL);            }        }    }

这里就是根据不同的系统版本把dex插到dexElements的前面,其中比较特殊的是安卓7.0,在Dalvik虚拟机中是通过JIT,在运行时将热代码编译成机器码,可以提高下次运行到这段代码的速度,ART虚拟机改变了这种方式,改为OTA,一开始安装的时候就会将字节码编译成机器码,提高运行效率,但带来了2个问题,一个是安装时间过长,占用体积过大;

所以在Android N上改变了这种激进的编译方式,改为混合编译,混合编译运行主要指AOT编译,解释执行与JIT编译。简单来说,在应用运行时分析运行过的代码以及“热代码”,并将配置存储下来。在设备空闲与充电时,ART仅仅编译这份配置中的“热代码”。

无论是使用插入pathlist还是parent classloader的方式,若补丁修改的class已经存在与app image,它们都是无法通过热补丁更新的。它们在启动app时已经加入到PathClassloader的ClassTable中,系统在查找类时会直接使用base.apk中的class

假设base.art文件在补丁前已经存在,这里存在三种情况:

  1. 补丁修改的类都不app image中;这种情况是最理想的,此时补丁机制依然有效;
  2. 补丁修改的类部分在app image中;这种情况我们只能更新一部分的类,此时是最危险的。一部分类是新的,一部分类是旧的,app可能会出现地址错乱而出现crash。
  3. 补丁修改的类全部在app image中;这种情况只是造成补丁不生效,app并不会因此造成crash。

Tinker的解决方案是,完全废弃掉PathClassloader,而采用一个新建Classloader来加载后续的所有类,即可达到将cache无用化的效果。基本原理我们清楚了,让我们来看下代码吧。

if (Build.VERSION.SDK_INT >= 24) {    classLoader = AndroidNClassLoader.inject(loader, application);}

以上关于AndroidN的内容可以参考:Android N混合编译与对热补丁影响解析

继续往下看具体的加载过程,这里就看一个V14的实现吧:

/**     * Installer for platform versions 14, 15, 16, 17 and 18.     */    private static final class V14 {        private static void install(ClassLoader loader, List<File> additionalClassPathEntries,                                    File optimizedDirectory)            throws IllegalArgumentException, IllegalAccessException,            NoSuchFieldException, InvocationTargetException, NoSuchMethodException {            /* The patched class loader is expected to be a descendant of             * dalvik.system.BaseDexClassLoader. We modify its             * dalvik.system.DexPathList pathList field to append additional DEX             * file entries.             */            Field pathListField = ShareReflectUtil.findField(loader, "pathList");            Object dexPathList = pathListField.get(loader);            ShareReflectUtil.expandFieldArray(dexPathList, "dexElements", makeDexElements(dexPathList,                new ArrayList<File>(additionalClassPathEntries), optimizedDirectory));        }        /**         * A wrapper around         * {@code private static final dalvik.system.DexPathList#makeDexElements}.         */        private static Object[] makeDexElements(            Object dexPathList, ArrayList<File> files, File optimizedDirectory)            throws IllegalAccessException, InvocationTargetException,            NoSuchMethodException {            Method makeDexElements =                ShareReflectUtil.findMethod(dexPathList, "makeDexElements", ArrayList.class, File.class);            return (Object[]) makeDexElements.invoke(dexPathList, files, optimizedDirectory);        }    }

通过反射拿到BaseDexClassLoader的pathList,然后通过反射调用PathList的makeDexElements传进去的参数分别是补丁dexList和优化过的opt目录,在Tinker中是dex补丁目录的同级目录odex/。

看一下下面这个方法,是将生成的dexElements插入到原先dexElements的前面。

public static void expandFieldArray(Object instance, String fieldName, Object[] extraElements)        throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {        Field jlrField = findField(instance, fieldName);        Object[] original = (Object[]) jlrField.get(instance);        Object[] combined = (Object[]) Array.newInstance(original.getClass().getComponentType(), original.length + extraElements.length);        // NOTE: changed to copy extraElements first, for patch load first        System.arraycopy(extraElements, 0, combined, 0, extraElements.length);        System.arraycopy(original, 0, combined, extraElements.length, original.length);        jlrField.set(instance, combined);    }

首先会拿到原始的数组:

Object[] original = (Object[]) jlrField.get(instance);

再生成一个长度为original.length + extraElements.length的新数组,

Object[] combined = (Object[]) Array.newInstance(original.getClass().getComponentType(), original.length + extraElements.length);

然后先把传进来的extraElements拷贝到新数组中,再把原始的数组拷贝进来,这里的位置很重要,必须得得将加载的dex文件列表拷贝到数组前面,再拷贝原先的数组放在数组的后面;否则补丁将不会生效。

System.arraycopy(extraElements, 0, combined, 0, extraElements.length);System.arraycopy(original, 0, combined, extraElements.length, original.length);

最后将新数组设置进instance中。

jlrField.set(instance, combined);

到此为止加载补丁dex文件的过程就结束了。

至于为什么新的补丁文件加载的类会生效,那么需要看一下BaseDexClassLoader,该类是PathClassLoader和DexClassLoader的基类,

public BaseDexClassLoader(String dexPath, File optimizedDirectory,            String libraryPath, ClassLoader parent) {        super(parent);        this.originalPath = dexPath;        this.pathList =            new DexPathList(this, dexPath, libraryPath, optimizedDirectory);    }    @Override    protected Class<?> findClass(String name) throws ClassNotFoundException {        Class clazz = pathList.findClass(name);        if (clazz == null) {            throw new ClassNotFoundException(name);        }        return clazz;    }

其中findClass会调用DexPathList的findClass:

//DexPathList    public Class findClass(String name) {        for (Element element : dexElements) {            DexFile dex = element.dexFile;            if (dex != null) {                Class clazz = dex.loadClassBinaryName(name, definingContext);                if (clazz != null) {                    return clazz;                }            }        }        return null;    }

从这里便可以看出dexElements中Element元素顺序的作用了,前面的会先被读取到,如果读取到了对应的类循环就会结束。

下篇文章会介绍补丁文件的合成过程,敬请期待…

0 0
原创粉丝点击