Android 热修复Tinker源码分析(三)补丁的加载

3,618 阅读15分钟

前言

对于重要类的分析已经全部加上了详细注释,代码在github.com/ColorfulHor…

上一篇文章中我们大致分析了补丁包的合成过程,本文将逐步分析补丁的加载过程。补丁加载主要是dex和资源文件的加载,对于dex文件来说,加载实际上就是将补丁dex转换为Element插入到app的PathClassLoader.pathList.dexElements数组最前面,对于dex加载过程不了解的读者可以先去看看相关知识或者源码;对于资源加载,主要是替换掉原本的AssetManager,相对没那么复杂;另外对于新增activity的处理涉及到hook相关内容,需要对activity的启动过程有一些了解。

Application隔离

在看补丁加载过程之前,我们要先了解一下tinker中的application隔离,Tinker要求将Application类和其他逻辑类隔离开,为此特意用了AnnotationProcessor插件生成Application类,然后将生命周期代理到ApplicationLike类,Application对于开发者不可见,所有逻辑代码都放入ApplicationLike,避免了Application对于其他类的引用。对于Tinker如何生成Application不做具体分析,因为生成的Application并没有起到什么实际上的作用,有兴趣可以自行翻看相关源码。

进行Application隔离的原因

由于Android N之后由于混合编译会出现加载补丁以后地址错乱崩溃问题,所以Tinker在Application中使用自定义ClassLoader替换掉了PathClassLoader来加载后续类;而Application类本身必定先被PathClassLoader加载,所以如果Application类中引用了其他逻辑类,这个类也会先被原PathClassLoader加载,如果后面这个类又被替换后的自定义ClassLoader加载,使用时就会出现ClassCastException问题。

补丁加载的时机和大致流程

集成Tinker后,对于开发者来说应用入口变成了ApplicationLike类,但是实际上的应用入口还是Application类,Application类中先进行补丁的加载,然后再启动ApplicationLike,所以补丁加载的逻辑在TinkerApplication类中。TinkerApplication主要做了几件事,简单列一下:

  1. attachBaseContext时调用loadTinker方法加载补丁,这一步会替换掉PathClassLoader
  2. createInlineFence创建TinkerApplicationInlineFence实例,此类用于阻止Art下内联带来的影响
  3. 通过TinkerApplicationInlineFence回调ApplicationLike各个生命周期方法
public abstract class TinkerApplication extends Application {
    protected void onBaseContextAttached(Context base, long applicationStartElapsedTime, long applicationStartMillisTime) {
        try {
            // 反射调用loader类的tryLoad方法
            // 因为开发者可以自定义拓展loader,所以根据ApplicationLike中配置的loader类名反射调用
            // 加载补丁,下面单独分析
            loadTinker();
            // loadTinker已经将app PathClassLoader替换,这里是替换后的cl
            mCurrentClassLoader = base.getClassLoader();
            mInlineFence = createInlineFence(this, tinkerFlags, delegateClassName,
                    tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime,
                    tinkerResultIntent);
            // 回调ApplicationLike onBaseContextAttached
            TinkerInlineFenceAction.callOnBaseContextAttached(mInlineFence, base);
            //reset save mode
            if (useSafeMode) {
                ShareTinkerInternals.setSafeModeCount(this, 0);
            }
        } catch (TinkerRuntimeException e) {
            throw e;
        } catch (Throwable thr) {
            throw new TinkerRuntimeException(thr.getMessage(), thr);
        }
    }
}

TinkerApplicationInlineFence阻止内联

方法内联可以简单理解为,由于被调用的方法过于简单,其中的代码直接被拷贝到调用处,避免压入方法栈这一优化行为。

如果在Application类中直接调用ApplicationLike中方法,ApplicationLike方法编译时被内联到Application,而执行时由于替换了原PathClassLoader,导致加载Application和ApplicationLike的ClassLoader是不同的,此时在Android P开始就会出现被内联的代码和内联到的类的classloader不一致的校验错误,Android P以下,>=N的系统又会出现ClassCastException。

为了阻止对于ApplicationLike的内联,将TinkerApplicationInlineFence作为调用中间层,通过特殊命名加上try代码块阻止内联。

private Handler createInlineFence(Application app,
                                      int tinkerFlags,
                                      String delegateClassName,
                                      boolean tinkerLoadVerifyFlag,
                                      long applicationStartElapsedTime,
                                      long applicationStartMillisTime,
                                      Intent resultIntent) {
    try {
        // 使用替换后的classLoader反射ApplicationLike类
        final Class<?> delegateClass = Class.forName(delegateClassName, false, mCurrentClassLoader);
        final Constructor<?> constructor = delegateClass.getConstructor(Application.class, int.class, boolean.class,
                long.class, long.class, Intent.class);
        // 创建ApplicationLike实例
        final Object appLike = constructor.newInstance(app, tinkerFlags, tinkerLoadVerifyFlag,
                applicationStartElapsedTime, applicationStartMillisTime, resultIntent);
        // 反射创建TinkerApplicationInlineFence
        final Class<?> inlineFenceClass = Class.forName(
                "com.tencent.tinker.entry.TinkerApplicationInlineFence", false, mCurrentClassLoader);
        final Class<?> appLikeClass = Class.forName(
                "com.tencent.tinker.entry.ApplicationLike", false, mCurrentClassLoader);
        final Constructor<?> inlineFenceCtor = inlineFenceClass.getConstructor(appLikeClass);
        inlineFenceCtor.setAccessible(true);
        return (Handler) inlineFenceCtor.newInstance(appLike);
    } catch (Throwable thr) {
        throw new TinkerRuntimeException("createInlineFence failed", thr);
    }
}
public final class TinkerApplicationInlineFence extends Handler {
    private final ApplicationLike mAppLike;

    public TinkerApplicationInlineFence(ApplicationLike appLike) {
        mAppLike = appLike;
    }

    @Override
    public void handleMessage(Message msg) {
        handleMessage_$noinline$(msg);
    }
    // 特殊命名方法
    private void handleMessage_$noinline$(Message msg) {
        // try块阻止内联
        try {
            dummyThrowExceptionMethod();
        } finally {
            handleMessageImpl(msg);
        }
    }
    ......
    private static void dummyThrowExceptionMethod() {
        if (TinkerApplicationInlineFence.class.isPrimitive()) {
            throw new RuntimeException();
        }
    }
}

加载补丁预操作

首先在TinkerApplication.loadTinker反射调用TinkerLoader.tryLoad,最后调用到TinkerLoader.tryLoadPatchFilesInternal,此方法主要是进行加载前一系列的校验。

private void loadTinker() {
    try {
        // 因为loader类可以被开发者自定义,所以反射创建tinker loader实例,默认为TinkerLoader类
        Class<?> tinkerLoadClass = Class.forName(loaderClassName, false, TinkerApplication.class.getClassLoader());
        // 调用TinkerLoader tryLoad
        Method loadMethod = tinkerLoadClass.getMethod(TINKER_LOADER_METHOD, TinkerApplication.class);
        Constructor<?> constructor = tinkerLoadClass.getConstructor();
        tinkerResultIntent = (Intent) loadMethod.invoke(constructor.newInstance(), this);
    } catch (Throwable e) {
        //has exception, put exception error code
        tinkerResultIntent = new Intent();
        ShareIntentUtil.setIntentReturnCode(tinkerResultIntent, ShareConstants.ERROR_LOAD_PATCH_UNKNOWN_EXCEPTION);
        tinkerResultIntent.putExtra(INTENT_PATCH_EXCEPTION, e);
    }
}
public class TinkerLoader extends AbstractTinkerLoader {
    public Intent tryLoad(TinkerApplication app) {
        ShareTinkerLog.d(TAG, "tryLoad test test");
        // intent记录加载补丁的信息
        Intent resultIntent = new Intent();
        long begin = SystemClock.elapsedRealtime();
        tryLoadPatchFilesInternal(app, resultIntent);
        long cost = SystemClock.elapsedRealtime() - begin;
        ShareIntentUtil.setIntentPatchCostTime(resultIntent, cost);
        return resultIntent;
    }
}

TinkerLoader.tryLoadPatchFilesInternal中先对补丁进行一系列的校验,然后校验合成后的dex、so库、资源文件的完整性合法性,最后依次调用TinkerDexLoader、TinkerSoLoader、TinkerResourceLoader加载补丁。

另外方法内包含对于系统OTA升级后解释模式运行的相关逻辑,这里先单独拿出来说,以免看加载补丁逻辑时感到混乱。

系统OTA后加载补丁ANR的相关处理

android 8.0之前动态加载的dex会被以speed模式全量编译,系统OTA更新后导致旧的补丁dex的oat文件失效,此时运行app就会对它重新执行dex2oat,可能耗时比较久造成ANR。所以TinkerLoader中加载补丁前先判断是否OTA后首次运行,然后对补丁dex先做quiken/interpret-only模式的dex2oat操作,以解释模式运行,之后再进行后台的全量编,大致流程如下。

  1. TinkerLoader加载补丁时判断此次加载是否系统OTA升级后首次运行,是的话调用TinkerDexOptimizer.optimizeAll以解释模式对合成后dex做dexopt,保存在data/data/包名/tinker/patch-xxx/interpret文件夹。加载odex,然后将patch.info和resultIntent中oatDir设为interpet
  2. 补丁加载完成后,Tinker.install时调用TinkerLoadResult.parseTinkerResult解析intentResult,判断oatDir为interpet则回调DefaultPatchListener.onLoadInterpret,之后再次调用到UpgradePatch.tryPatch重走一遍补丁合成流程重新触发后台dex2oat,同时将patch.info中oatDir设为changing,以便下次加载补丁时不再以解释模式加载
  3. 下次加载补丁时TinkerLoader中判断oatDir为changing,代表oat文件已经生成,不再需要以解释模式加载补丁,此时标记patch.info中isRemoveInterpretOATDir为true,下次加载删除data/data/包名/tinker/patch-xxx/interpret文件夹

下面我们从代码入手,大致看一下加载补丁前的准备工作,之后进一步分析真正的加载过程

public class TinkerLoader extends AbstractTinkerLoader {
    private void tryLoadPatchFilesInternal(TinkerApplication app, Intent resultIntent) {
       .....补丁目录以及patch.info文件是否存在等校验
        // 读取patch.info文件中记录的信息
        patchInfo = SharePatchInfo.readAndCheckPropertyWithLock(patchInfoFile, patchInfoLockFile);
        if (patchInfo == null) {
            ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_INFO_CORRUPTED);
            return;
        }

        final boolean isProtectedApp = patchInfo.isProtectedApp;
        resultIntent.putExtra(ShareIntentUtil.INTENT_IS_PROTECTED_APP, isProtectedApp);
        // 上一次加载的补丁版本
        String oldVersion = patchInfo.oldVersion;
        // 当前需要加载的补丁版本
        String newVersion = patchInfo.newVersion;
        String oatDex = patchInfo.oatDir;
        // 没有补丁
        if (oldVersion == null || newVersion == null || oatDex == null) {
            ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_INFO_CORRUPTED);
            return;
        }
        // 是否主进程(app运行的进程)
        boolean mainProcess = ShareTinkerInternals.isInMainProcess(app);
        boolean isRemoveNewVersion = patchInfo.isRemoveNewVersion;

        if (mainProcess) {
            final String patchName = SharePatchFileUtil.getPatchVersionDirectory(newVersion);
            // 清除补丁操作不是及时生效,而是等到下一次加载时生效
            // 是否清除所有补丁,若补丁被加载过需要重置patch.info并杀死主进程以外所有进程
            if (isRemoveNewVersion) {
                if (patchName != null) {
                    // 新旧版本号相同说明该补丁之前被加载过
                    final boolean isNewVersionLoadedBefore = oldVersion.equals(newVersion);
                    // 重置补丁信息
                    if (isNewVersionLoadedBefore) {
                        oldVersion = "";
                    }
                    newVersion = oldVersion;
                    patchInfo.oldVersion = oldVersion;
                    patchInfo.newVersion = newVersion;
                    patchInfo.isRemoveNewVersion = false;
                    SharePatchInfo.rewritePatchInfoFileWithLock(patchInfoFile, patchInfo, patchInfoLockFile);
                    // 删除补丁文件
                    String patchVersionDirFullPath = patchDirectoryPath + "/" + patchName;
                    SharePatchFileUtil.deleteDir(patchVersionDirFullPath);

                    if (isNewVersionLoadedBefore) {
                        // 如果已经加载过补丁,则杀死其他进程
                        ShareTinkerInternals.killProcessExceptMain(app);
                        ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_DIRECTORY_NOT_EXIST);
                        return;
                    }
                }
            }
            // 是否删除解释编译产生的odex文件
            if (patchInfo.isRemoveInterpretOATDir) {
                // 重写patch.info删除odex标识,然后杀死其他进程并且删除odex文件
                patchInfo.isRemoveInterpretOATDir = false;
                SharePatchInfo.rewritePatchInfoFileWithLock(patchInfoFile, patchInfo, patchInfoLockFile);
                ShareTinkerInternals.killProcessExceptMain(app);
                String patchVersionDirFullPath = patchDirectoryPath + "/" + patchName;
                // data/data/包名/tinker/patch-xxx/interpet
                SharePatchFileUtil.deleteDir(patchVersionDirFullPath + "/" + ShareConstants.INTERPRET_DEX_OPTIMIZE_PATH);
            }
        }

        resultIntent.putExtra(ShareIntentUtil.INTENT_PATCH_OLD_VERSION, oldVersion);
        resultIntent.putExtra(ShareIntentUtil.INTENT_PATCH_NEW_VERSION, newVersion);
        // oldVersion和newVersion不同说明合成了新补丁,但是还没有加载过
        boolean versionChanged = !(oldVersion.equals(newVersion));
        // changing代表后台dex2oat已经完成,切换到非解释模式
        // 此时使用dex2oat生成的odex文件,删除解释模式的odex文件
        boolean oatModeChanged = oatDex.equals(ShareConstants.CHANING_DEX_OPTIMIZE_PATH);
        oatDex = ShareTinkerInternals.getCurrentOatMode(app, oatDex);
        resultIntent.putExtra(ShareIntentUtil.INTENT_PATCH_OAT_DIR, oatDex);

        String version = oldVersion;
        if (versionChanged && mainProcess) {
            version = newVersion;
        }
        if (ShareTinkerInternals.isNullOrNil(version)) {
            ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_INFO_BLANK);
            return;
        }
        // 待加载的补丁名:patch-641e634c
        String patchName = SharePatchFileUtil.getPatchVersionDirectory(version);
        // 待加载的补丁路径 data/data/包名/tinker/patch-641e634c
        String patchVersionDirectory = patchDirectoryPath + "/" + patchName;

        File patchVersionDirectoryFile = new File(patchVersionDirectory);

        final String patchVersionFileRelPath = SharePatchFileUtil.getPatchVersionFile(version);
        // 补丁文件:data/data/包名/tinker/patch-md5/patch-md5.apk
        File patchVersionFile = (patchVersionFileRelPath != null ? new File(patchVersionDirectoryFile.getAbsolutePath(), patchVersionFileRelPath) : null);

        // 此类用于读取补丁包中的meta文件进行校验
        ShareSecurityCheck securityCheck = new ShareSecurityCheck(app);
        // 校验补丁的TinkerId、签名、MD5、meta文件等的合法性
        int returnCode = ShareTinkerInternals.checkTinkerPackage(app, tinkerFlag, patchVersionFile, securityCheck);
        
        resultIntent.putExtra(ShareIntentUtil.INTENT_PATCH_PACKAGE_CONFIG, securityCheck.getPackagePropertiesIfPresent());

        // 是否合成dex
        final boolean isEnabledForDex = ShareTinkerInternals.isTinkerEnabledForDex(tinkerFlag);
        // 是否方舟编译器
        final boolean isArkHotRuning = ShareTinkerInternals.isArkHotRuning();

        if (!isArkHotRuning && isEnabledForDex) {
            // 解析dex_meta,校验要加载的dex以及对应的的odex是否存在
            boolean dexCheck = TinkerDexLoader.checkComplete(patchVersionDirectory, securityCheck, oatDex, resultIntent);
            if (!dexCheck) {
                return;
            }
        }

        final boolean isEnabledForNativeLib = ShareTinkerInternals.isTinkerEnabledForNativeLib(tinkerFlag);

        if (isEnabledForNativeLib) {
            // 校验so库合法性
            boolean libCheck = TinkerSoLoader.checkComplete(patchVersionDirectory, securityCheck, resultIntent);
            if (!libCheck) {
                return;
            }
        }

        final boolean isEnabledForResource = ShareTinkerInternals.isTinkerEnabledForResource(tinkerFlag);
        ShareTinkerLog.w(TAG, "tryLoadPatchFiles:isEnabledForResource:" + isEnabledForResource);
        if (isEnabledForResource) {
            // 校验补丁资源正确性
            boolean resourceCheck = TinkerResourceLoader.checkComplete(app, patchVersionDirectory, securityCheck, resultIntent);
            if (!resourceCheck) {
                return;
            }
        }
        // 判断系统是否进行了OTA升级,系统OTA后首次启动需要先以解释模式运行
        // 8.0以后不需要做此操作
        boolean isSystemOTA = ShareTinkerInternals.isVmArt()
            && ShareTinkerInternals.isSystemOTA(patchInfo.fingerPrint)
            && Build.VERSION.SDK_INT >= 21 && !ShareTinkerInternals.isAfterAndroidO();

        resultIntent.putExtra(ShareIntentUtil.INTENT_PATCH_SYSTEM_OTA, isSystemOTA);

        if (mainProcess) {
            if (versionChanged) {
                patchInfo.oldVersion = version;
            }

            if (oatModeChanged) {
                patchInfo.oatDir = oatDex;
                patchInfo.isRemoveInterpretOATDir = true;
            }
        }

        // 判断是否安全模式,也就是加载补丁是否失败了超过三次,超过三次之后直接删除对应补丁回退
        if (!checkSafeModeCount(app)) {
            if (mainProcess) {
                // 主进程杀死其他进程,然后直接删除
                patchInfo.oldVersion = "";
                patchInfo.newVersion = "";
                patchInfo.isRemoveNewVersion = false;
                SharePatchInfo.rewritePatchInfoFileWithLock(patchInfoFile, patchInfo, patchInfoLockFile);
                ShareTinkerInternals.killProcessExceptMain(app);

                String patchVersionDirFullPath = patchDirectoryPath + "/" + patchName;
                SharePatchFileUtil.deleteDir(patchVersionDirFullPath);

                resultIntent.putExtra(ShareIntentUtil.INTENT_PATCH_EXCEPTION, new TinkerRuntimeException("checkSafeModeCount fail"));
                return;
            } else {
                // 非主进程将 patchInfo isRemoveNewVersion设为true,下次在主进程删除
                ShareTinkerInternals.cleanPatch(app);
            }
        }

        if (!isArkHotRuning && isEnabledForDex) {
            // 加载dex,isSystemOTA = true以解释模式加载
            boolean loadTinkerJars = TinkerDexLoader.loadTinkerJars(app, patchVersionDirectory, oatDex, resultIntent, isSystemOTA, isProtectedApp);

            if (isSystemOTA) {
                // update fingerprint after load success
                patchInfo.fingerPrint = Build.FINGERPRINT;
                // ota后第一次启动加载补丁成功,oatDir = interpet
                patchInfo.oatDir = loadTinkerJars ? ShareConstants.INTERPRET_DEX_OPTIMIZE_PATH : ShareConstants.DEFAULT_DEX_OPTIMIZE_PATH;
                oatModeChanged = false;

                if (!SharePatchInfo.rewritePatchInfoFileWithLock(patchInfoFile, patchInfo, patchInfoLockFile)) {
                    return;
                }
                // intent中设置oatDir为interpret
                resultIntent.putExtra(ShareIntentUtil.INTENT_PATCH_OAT_DIR, patchInfo.oatDir);
            }
            if (!loadTinkerJars) {
                return;
            }
        }

        if (isEnabledForResource) {
            // 加载资源文件
            boolean loadTinkerResources = TinkerResourceLoader.loadTinkerResources(app, patchVersionDirectory, resultIntent);
            if (!loadTinkerResources) {
                return;
            }
        }

        // TODO
        if ((isEnabledForDex || isEnabledForArkHot) && isEnabledForResource) {
            ComponentHotplug.install(app, securityCheck);
        }

        if (!AppInfoChangedBlocker.tryStart(app)) {
            ShareTinkerLog.w(TAG, "tryLoadPatchFiles:AppInfoChangedBlocker install fail.");
            ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_BAIL_HACK_FAILURE);
            return;
        }

        // Before successfully exit, we should update stored version info and kill other process
        // to make them load latest patch when we first applied newer one.
        if (mainProcess && (versionChanged || oatModeChanged)) {
            //update old version to new
            if (!SharePatchInfo.rewritePatchInfoFileWithLock(patchInfoFile, patchInfo, patchInfoLockFile)) {
                ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_REWRITE_PATCH_INFO_FAIL);
                ShareTinkerLog.w(TAG, "tryLoadPatchFiles:onReWritePatchInfoCorrupted");
                return;
            }

            ShareTinkerInternals.killProcessExceptMain(app);
        }

        //all is ok!
        ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_OK);
        ShareTinkerLog.i(TAG, "tryLoadPatchFiles: load end, ok!");
    }
}

加载补丁

加载补丁部分重点分析dex加载、资源文件加载以及新增Activity相关处理。

dex加载

dex加载主要逻辑在TinkerDexLoader.loadTinkerJars方法中,此方法中先进行一些校验,然后调用到SystemClassLoaderAdder.installDexes方法进行加载,如果是系统OTA后首次启动则需要删除旧的oat文件,然后解释模式执行dex2oat再加载。

public class TinkerDexLoader {
    // dalvik下需要加载的dex
    private static final ArrayList<ShareDexDiffPatchInfo> LOAD_DEX_LIST = new ArrayList<>();

    // art下需要加载的dex
    private static HashSet<ShareDexDiffPatchInfo> classNDexInfo = new HashSet<>();

    public static boolean loadTinkerJars(final TinkerApplication application, String directory, 
        String oatDir, Intent intentResult, boolean isSystemOTA, boolean isProtectedApp) {
        if (LOAD_DEX_LIST.isEmpty() && classNDexInfo.isEmpty()) {
            return true;
        }

        ClassLoader classLoader = TinkerDexLoader.class.getClassLoader();
        if (classLoader != null) {
        } else {
            ShareIntentUtil.setIntentReturnCode(intentResult, ShareConstants.ERROR_LOAD_PATCH_VERSION_DEX_CLASSLOADER_NULL);
            return false;
        }
        String dexPath = directory + "/" + DEX_PATH + "/";
        // 需要加载的dex集合
        ArrayList<File> legalFiles = new ArrayList<>();

        for (ShareDexDiffPatchInfo info : LOAD_DEX_LIST) {
            // dalvik下跳过没有改变的非主dex
            if (isJustArtSupportDex(info)) {
                continue;
            }
            String path = dexPath + info.realName;
            File file = new File(path);

            if (application.isTinkerLoadVerifyFlag()) {
                long start = System.currentTimeMillis();
                String checkMd5 = getInfoMd5(info);
                // 校验合成后dex MD5和dex_meta中md5
                if (!SharePatchFileUtil.verifyDexFileMd5(file, checkMd5)) {
                    ShareIntentUtil.setIntentReturnCode(intentResult, ShareConstants.ERROR_LOAD_PATCH_VERSION_DEX_MD5_MISMATCH);
                    intentResult.putExtra(ShareIntentUtil.INTENT_PATCH_MISMATCH_DEX_PATH,
                        file.getAbsolutePath());
                    return false;
                }
            }
            legalFiles.add(file);
        }
        if (isVmArt && !classNDexInfo.isEmpty()) {
            // art下所有dex合成为tinker_classN.apk然后dex2oat生成了tinker_classN.odex
            // 合成后的tinker_classN.apk,art下加载时会自动寻找相应的tinker_classN.odex文件
            File classNFile = new File(dexPath + ShareConstants.CLASS_N_APK_NAME);
            long start = System.currentTimeMillis();

            if (application.isTinkerLoadVerifyFlag()) {
                for (ShareDexDiffPatchInfo info : classNDexInfo) {
                    // 校验合成后dex MD5和dex_meta中md5
                    if (!SharePatchFileUtil.verifyDexFileMd5(classNFile, info.rawName, info.destMd5InArt)) {
                        ShareIntentUtil.setIntentReturnCode(intentResult, ShareConstants.ERROR_LOAD_PATCH_VERSION_DEX_MD5_MISMATCH);
                        intentResult.putExtra(ShareIntentUtil.INTENT_PATCH_MISMATCH_DEX_PATH,
                            classNFile.getAbsolutePath());
                        return false;
                    }
                }
            }
            legalFiles.add(classNFile);
        }
        File optimizeDir = new File(directory + "/" + oatDir);

        if (isSystemOTA) {
            final boolean[] parallelOTAResult = {true};
            final Throwable[] parallelOTAThrowable = new Throwable[1];
            String targetISA;
            try {
                // 获取cpu指令集
                targetISA = ShareTinkerInternals.getCurrentInstructionSet();
            } catch (Throwable throwable) {
                deleteOutOfDateOATFile(directory);
                intentResult.putExtra(ShareIntentUtil.INTENT_PATCH_INTERPRET_EXCEPTION, throwable);
                ShareIntentUtil.setIntentReturnCode(intentResult, ShareConstants.ERROR_LOAD_PATCH_GET_OTA_INSTRUCTION_SET_EXCEPTION);
                return false;
            }
            // 删除失效的oat文件
            deleteOutOfDateOATFile(directory);

            // data/data/包名/tinker/patch-xxx/interpret
            optimizeDir = new File(directory + "/" + INTERPRET_DEX_OPTIMIZE_PATH);
            // 解释模式dex2oat
            TinkerDexOptimizer.optimizeAll(......);
            if (!parallelOTAResult[0]) {
                ShareTinkerLog.e(TAG, "parallel oat dexes failed");
                intentResult.putExtra(ShareIntentUtil.INTENT_PATCH_INTERPRET_EXCEPTION, parallelOTAThrowable[0]);
                ShareIntentUtil.setIntentReturnCode(intentResult, ShareConstants.ERROR_LOAD_PATCH_OTA_INTERPRET_ONLY_EXCEPTION);
                return false;
            }
        }
        try {
            final boolean useDLC = application.isUseDelegateLastClassLoader();
            // inject classloader 开始加载
            SystemClassLoaderAdder.installDexes(application, classLoader, optimizeDir, legalFiles, isProtectedApp, useDLC);
        } catch (Throwable e) {
            intentResult.putExtra(ShareIntentUtil.INTENT_PATCH_EXCEPTION, e);
            ShareIntentUtil.setIntentReturnCode(intentResult, ShareConstants.ERROR_LOAD_PATCH_VERSION_DEX_LOAD_EXCEPTION);
            return false;
        }
        return true;
    }
}

SystemClassLoaderAdder.installDexes方法中根据判断系统版本,在android7.0及以上版本时调用NewClassLoaderInjector.inject创建新PathClassLoader替换app原有PathClassLoader,避免android7.0之后以混合编译与对热补丁影响,之前我们提到过。而对于7.0以下版本,直接调用SystemClassLoaderAdder.injectDexesInternal方法,将补丁dex插入到app PathClassLoader的pathList.dexElements最前面。加载完成后通过TinkerTestDexLoad.isPatch判断补丁是否加载成功,TinkerTestDexLoad.isPatch在loader中为false,加载补丁后被test.dex中类覆盖为true。

public class SystemClassLoaderAdder {
    // 判断补丁是否加载成功的类
    public static final String CHECK_DEX_CLASS = "com.tencent.tinker.loader.TinkerTestDexLoad";
    public static final String CHECK_DEX_FIELD = "isPatch";

    public static void installDexes(Application application, ClassLoader loader, File dexOptDir, List<File> files,
                                    boolean isProtectedApp, boolean useDLC) throws Throwable {

        if (!files.isEmpty()) {
            files = createSortedAdditionalPathEntries(files);
            ClassLoader classLoader = loader;
            if (Build.VERSION.SDK_INT >= 24 && !isProtectedApp) {
                // 7.0之后创建新ClassLoader替换原ClassLoader避免混合编译带来的问题
                classLoader = NewClassLoaderInjector.inject(application, loader, dexOptDir, useDLC, files);
            } else {
                // 加固也不替换ClassLoader
                // android7.0之前直接将补丁dex插入原ClassLoader中pathList中dexElements最前面
                injectDexesInternal(classLoader, files, dexOptDir);
            }
            sPatchDexCount = files.size();
            // 反射TinkerTestDexLoad.isPatch判断是否加载成功
            if (!checkDexInstall(classLoader)) {
                SystemClassLoaderAdder.uninstallPatchDex(classLoader);
                throw new TinkerRuntimeException(ShareConstants.CHECK_DEX_INSTALL_FAIL);
            }
        }
    }
}

补丁dex插入

SystemClassLoaderAdder.injectDexesInternal方法中根据android版本不同采取不同方法插入补丁dex,我们简单看一下V23.install

private static final class V23 {

    private static void install(ClassLoader loader, List<File> additionalClassPathEntries,
                                File optimizedDirectory)
        throws IllegalArgumentException, IllegalAccessException,
        NoSuchFieldException, InvocationTargetException, NoSuchMethodException, IOException {
        // 获取BaseDexClassLoader.pathList
        Field pathListField = ShareReflectUtil.findField(loader, "pathList");
        Object dexPathList = pathListField.get(loader);
        ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
        // makePathElements反射调用DexPathList.makePathElements/makeDexElements得到Element数组
        // 将补丁dex生成的Element数组插入DexPathList.dexElements
        ShareReflectUtil.expandFieldArray(dexPathList, "dexElements", makePathElements(dexPathList,
            new ArrayList<File>(additionalClassPathEntries), optimizedDirectory,
            suppressedExceptions));
        if (suppressedExceptions.size() > 0) {
            for (IOException e : suppressedExceptions) {
                ShareTinkerLog.w(TAG, "Exception in makePathElement", e);
                throw e;
            }

        }
    }
}

ClassLoader替换

在上一篇文章中已经提到过加载时ClassLoader替换,NewClassLoaderInjector.createNewClassLoader根据版本和配置不同创建相应的代理ClassLoader,然后调用NewClassLoaderInjector.doInject替换App中各处classLoader实例为代理ClassLoader,使得运行时先从代理ClassLoader加载补丁中类,后从原始ClassLoader加载。

private static ClassLoader createNewClassLoader(ClassLoader oldClassLoader,
                                                    File dexOptDir,
                                                    boolean useDLC,
                                                    String... patchDexPaths) throws Throwable {
        // 反射获取BaseDexClassLoader的DexPathList字段
        // old oldClassLoader是当前app的PathClassLoader
        final Field pathListField = findField(
                Class.forName("dalvik.system.BaseDexClassLoader", false, oldClassLoader),
                "pathList");
        final Object oldPathList = pathListField.get(oldClassLoader);

        final StringBuilder dexPathBuilder = new StringBuilder();
        final boolean hasPatchDexPaths = patchDexPaths != null && patchDexPaths.length > 0;
        if (hasPatchDexPaths) {
            for (int i = 0; i < patchDexPaths.length; ++i) {
                if (i > 0) {
                    dexPathBuilder.append(File.pathSeparator);
                }
                dexPathBuilder.append(patchDexPaths[i]);
            }
        }
        // 拼接需要dex2oat的dex文件路径
        final String combinedDexPath = dexPathBuilder.toString();

        // 反射DexPathList中nativeLibraryDirectories字段,so库路径
        final Field nativeLibraryDirectoriesField = findField(oldPathList.getClass(), "nativeLibraryDirectories");
        ......
        // 拼接so库路径
        final String combinedLibraryPath = libraryPathBuilder.toString();

        ClassLoader result = null;
        if (useDLC && Build.VERSION.SDK_INT >= 27) {
            // https://developer.android.google.cn/reference/dalvik/system/DelegateLastClassLoader
            // https://www.androidos.net.cn/android/10.0.0_r6/xref/libcore/dalvik/src/main/java/dalvik/system/DelegateLastClassLoader.java
            // DelegateLastClassLoader是android8.1后新增的,继承于PathClassLoader,实行最后查找策略
            result = new DelegateLastClassLoader(combinedDexPath, combinedLibraryPath, ClassLoader.getSystemClassLoader());
            // 将之前的PathClassLoader设为创建的DelegateLastClassLoader的双亲
            final Field parentField = ClassLoader.class.getDeclaredField("parent");
            parentField.setAccessible(true);
            parentField.set(result, oldClassLoader);
        } else {
            // 做的事情和DelegateLastClassLoader差不多
            result = new TinkerClassLoader(combinedDexPath, dexOptDir, combinedLibraryPath, oldClassLoader);
        }

        // Android8.0之前版本替换原本的PathClassLoader中PathList中的classLoader为新创建的
        // Android 8.0之后不支持多个类加载器同时使用同一个DexFile对象来定义类,所以不能替换
        if (Build.VERSION.SDK_INT < 26) {
            findField(oldPathList.getClass(), "definingContext").set(oldPathList, result);
        }

        return result;
    }
private static void doInject(Application app, ClassLoader classLoader) throws Throwable {
        Thread.currentThread().setContextClassLoader(classLoader);
        // ContextWrapper.mBase是该app的ContextImpl实例,LoadedApk.makeApplication中创建
        final Context baseContext = (Context) findField(app.getClass(), "mBase").get(app);
        try {
            // 替换ContextImpl中mClassLoader
            findField(baseContext.getClass(), "mClassLoader").set(baseContext, classLoader);
        } catch (Throwable ignored) {
            // 8.0前ContextImpl中没有mClassLoader
        }
        // app的ContextImpl中mPackageInfo是一个LoadedApk实例
        final Object basePackageInfo = findField(baseContext.getClass(), "mPackageInfo").get(baseContext);
        // 替换LoadedApk的ClassLoader
        findField(basePackageInfo.getClass(), "mClassLoader").set(basePackageInfo, classLoader);

        if (Build.VERSION.SDK_INT < 27) {
            final Resources res = app.getResources();
            try {
                // 替换Resources中ClassLoader
                findField(res.getClass(), "mClassLoader").set(res, classLoader);

                final Object drawableInflater = findField(res.getClass(), "mDrawableInflater").get(res);
                if (drawableInflater != null) {
                    // 替换DrawableInflater中ClassLoader
                    findField(drawableInflater.getClass(), "mClassLoader").set(drawableInflater, classLoader);
                }
            } catch (Throwable ignored) {
                // Ignored.
            }
        }
    }

资源文件加载

加载资源包逻辑并不复杂,主要逻辑在TinkerResourceLoader.loadTinkerResources方法中,此处先校验arsc文件md5,然后调用TinkerResourcePatcher.monkeyPatchExistingResources方法真正开始加载资源包,主要流程如下。

  1. 将原app中LoadedApk实例中mResDir字段设为新资源包路径
  2. 创建新的AssetManager实例,并设置它的资源路径为新资源包路径
  3. 将Resources.mAssets设为新创建的AssetManager,清除原app的Resources中typedArray缓存,刷新资源配置
    有个地方要注意,设置新的AssetManager资源路径时,在7.0后系统并且app使用了共享资源库的情况下还需调用AssetManager.addAssetPathAsSharedLibrary
    ApplicationInfo.sharedLibraryFiles存储app用到的共享资源库,共享资源库就是像so库一样,可以将资源包共享给其他应用使用。
    如果app用到了共享资源库,那么可能会遇到修复热修后 SharedLibrary R 类中的资源 ID 与 AssetManager 中 Package ID 不一致导致的资源找不到问题。
    这里将补丁资源包也添加为共享资源库。
    参考
    #1372
    Android资源管理中的SharedLibrary
public class TinkerResourceLoader {
    public static boolean loadTinkerResources(TinkerApplication application, String directory, Intent intentResult) {
        if (resPatchInfo == null || resPatchInfo.resArscMd5 == null) {
            return true;
        }
        // data/data/包名/tinker/patch-xxx/res/resources.apk
        String resourceString = directory + "/" + RESOURCE_PATH +  "/" + RESOURCE_FILE;
        File resourceFile = new File(resourceString);
        if (application.isTinkerLoadVerifyFlag()) {
            // 校验arsc文件md5
            if (!SharePatchFileUtil.checkResourceArscMd5(resourceFile, resPatchInfo.resArscMd5)) {
                ShareIntentUtil.setIntentReturnCode(intentResult, ShareConstants.ERROR_LOAD_PATCH_VERSION_RESOURCE_MD5_MISMATCH);
                return false;
            }
        }
        try {
           // 加载资源包
           TinkerResourcePatcher.monkeyPatchExistingResources(application, resourceString);
        } catch (Throwable e) {
            // 加载资源失败移除补丁
            try {
                SystemClassLoaderAdder.uninstallPatchDex(application.getClassLoader());
            } catch (Throwable throwable) {
                ShareTinkerLog.e(TAG, "uninstallPatchDex failed", e);
            }
            intentResult.putExtra(ShareIntentUtil.INTENT_PATCH_EXCEPTION, e);
            ShareIntentUtil.setIntentReturnCode(intentResult, ShareConstants.ERROR_LOAD_PATCH_VERSION_RESOURCE_LOAD_EXCEPTION);
            return false;
        }
        return true;
    }
}
class TinkerResourcePatcher {
    private static final String TAG = "Tinker.ResourcePatcher";
    private static final String TEST_ASSETS_VALUE = "only_use_to_test_tinker_resource.txt";

    // ResourcesManager中保存的Resources弱引用集合
    private static Collection<WeakReference<Resources>> references = null;
    private static Object currentActivityThread = null;
    // 新资源包的AssetManager
    private static AssetManager newAssetManager = null;

    // AssetManager.addAssetPath
    private static Method addAssetPathMethod = null;
    // AssetManager.addAssetPathAsSharedLibrary >=android7.0需要调用
    private static Method addAssetPathAsSharedLibraryMethod = null;
    // AssetManager.mStringBlocks字段
    private static Field stringBlocksField = null;
    // AssetManager.ensureStringBlocks
    private static Method ensureStringBlocksMethod = null;

    // Resources.mAssets
    private static Field assetsFiled = null;
    // Resources.mResourcesImpl
    private static Field resourcesImplFiled = null;
    // LoadedApk.mResDir
    private static Field resDir = null;
    // ActivityThread.mPackages
    private static Field packagesFiled = null;
    // ActivityThread.mResourcePackages
    private static Field resourcePackagesFiled = null;
    // ApplicationInfo.publicSourceDir
    private static Field publicSourceDirField = null;
    
    public static void monkeyPatchExistingResources(Context context, String externalResourceFile) throws Throwable {
        // data/data/包名/tinker/patch-xxx/res/resources.apk
        if (externalResourceFile == null) {
            return;
        }

        final ApplicationInfo appInfo = context.getApplicationInfo();

        final Field[] packagesFields;
        if (Build.VERSION.SDK_INT < 27) {
            packagesFields = new Field[]{packagesFiled, resourcePackagesFiled};
        } else {
            packagesFields = new Field[]{packagesFiled};
        }
        // 遍历activityThread中的LoadedApk
        for (Field field : packagesFields) {
            final Object value = field.get(currentActivityThread);

            for (Map.Entry<String, WeakReference<?>> entry
                    : ((Map<String, WeakReference<?>>) value).entrySet()) {
                final Object loadedApk = entry.getValue().get();
                if (loadedApk == null) {
                    continue;
                }
                final String resDirPath = (String) resDir.get(loadedApk);
                // 找到原app的LoadedApk实例,将它的mResDir设为新资源包路径
                if (appInfo.sourceDir.equals(resDirPath)) {
                    resDir.set(loadedApk, externalResourceFile);
                }
            }
        }

        // newAssetManager为新创建的AssetManager,调用AssetManager.addAssetPath设置它的路径为新资源包
        if (((Integer) addAssetPathMethod.invoke(newAssetManager, externalResourceFile)) == 0) {
            throw new IllegalStateException("Could not create new AssetManager");
        }

        // >=android7.0并且使用了共享资源库的情况下还需要调用addAssetPathAsSharedLibrary
        if (shouldAddSharedLibraryAssets(appInfo)) {
            for (String sharedLibrary : appInfo.sharedLibraryFiles) {
                if (!sharedLibrary.endsWith(".apk")) {
                    continue;
                }
                if (((Integer) addAssetPathAsSharedLibraryMethod.invoke(newAssetManager, sharedLibrary)) == 0) {
                    throw new IllegalStateException("AssetManager add SharedLibrary Fail");
                }
            }
        }

        // 重新创建资源字符串索引
        if (stringBlocksField != null && ensureStringBlocksMethod != null) {
            stringBlocksField.set(newAssetManager, null);
            ensureStringBlocksMethod.invoke(newAssetManager);
        }
        // 遍历ResourcesManager中Resources
        for (WeakReference<Resources> wr : references) {
            final Resources resources = wr.get();
            if (resources == null) {
                continue;
            }
            try {
                // 将Resources.mAssets设为新创建的AssetManager
                assetsFiled.set(resources, newAssetManager);
            } catch (Throwable ignore) {
                // android7.0以后该字段为Resources.mResourcesImpl.mAssets
                final Object resourceImpl = resourcesImplFiled.get(resources);
                final Field implAssets = findField(resourceImpl, "mAssets");
                implAssets.set(resourceImpl, newAssetManager);
            }
            // 清除Resources中typedArray缓存
            clearPreloadTypedArrayIssue(resources);
            // 内部调用AssetManager.setConfiguration,刷新资源配置
            resources.updateConfiguration(resources.getConfiguration(), resources.getDisplayMetrics());
        }

        // Handle issues caused by WebView on Android N.
        // Issue: On Android N, if an activity contains a webview, when screen rotates
        // our resource patch may lost effects.
        // for 5.x/6.x, we found Couldn't expand RemoteView for StatusBarNotification Exception
        // android7.0之后,如果activity包含webView,屏幕旋转后补丁资源会失效
        if (Build.VERSION.SDK_INT >= 24) {
            try {
                if (publicSourceDirField != null) {
                    // 重设ApplicationInfo.publicSourceDir
                    publicSourceDirField.set(context.getApplicationInfo(), externalResourceFile);
                }
            } catch (Throwable ignore) {
            }
        }
        // 以类似test.dex的方式检查补丁资源是否加载成功
        if (!checkResUpdate(context)) {
            throw new TinkerRuntimeException(ShareConstants.CHECK_RES_INSTALL_FAIL);
        }
    }
}

添加新增Activity

新增activity由于没有在manifest中注册,需要用hook的方式绕过AMS的检测才能够启动,大致原理:

  1. 在原app中预先注册一些代理activity

  2. 跳转到新增activity时拦截startActivity过程,通过动态代理修改调用参数,将target activity相关参数替换为代理activity的,以通过AMS检查

  3. 拦截AMS对于客户端真正启动activity的调用处,再将代理activity修改回target activity 关于hook相关知识可以通过这篇博客简单了解,原理都差不多,hook点小有区别。
    tinker中hook大致流程如下:

  4. 解析补丁包inc_component_meta.txt,将activity节点解析为ActivityInfo对象并保存

  5. hook ServiceManager,ServiceBinderInterceptor.fetchTarget方法获取AMS客户端代理对象(BpBinder)

  6. ServiceBinderInterceptor.decorate创建该BpBinder的动态代理,通过AMSInterceptHandlerhook对于AMS.startActivity的调用,将target activity替换为tinker loader预先注册的代理activity

  7. ServiceBinderInterceptor.inject将ServiceManager.sCache中AMS代理替换为hook上一步创建的动态代理对象

  8. 同理hook PMS中一些方法,防止Activity校验出错

  9. 8.1以下系统通过hookActivityThread.mH.mCallBack在系统调用handleLaunchActivity之前将target activity替换回来

  10. 8.1及以上系统通过hookActivityThread.mInstrumentation,将它替换为TinkerHackInstrumentation,修改相关方法实现将target activity替换回来。

ComponentHotplug.install开始进行hook

ServiceBinderInterceptor和HandlerMessageInterceptor都是Interceptor的实现类,Interceptor.fetchTarget获取需要hook的实例,Interceptor.decorate创建该对象的动态代理,Interceptor.inject使用动态代理替换原实例。

public final class ComponentHotplug {
     public synchronized static void install(TinkerApplication app, ShareSecurityCheck checker) throws UnsupportedEnvironmentException {
        if (!sInstalled) {
            try {
                // 解析inc_component_meta,将xml activity节点解析为ActivityInfo对象并存
                if (IncrementComponentManager.init(app, checker)) {
                    // ServiceManager.getService获取AMS客户端代理对象,然后创建此代理对象的动态代理对象,hook startActivity等方法
                    sAMSInterceptor = new ServiceBinderInterceptor(app, EnvConsts.ACTIVITY_MANAGER_SRVNAME, new AMSInterceptHandler(app));
                    // 同理hook PMS
                    sPMSInterceptor = new ServiceBinderInterceptor(app, EnvConsts.PACKAGE_MANAGER_SRVNAME, new PMSInterceptHandler());
                    sAMSInterceptor.install();
                    sPMSInterceptor.install();
                    if (Build.VERSION.SDK_INT < 27) {
                        // android 8.1以下
                        // ActivityThread.mH
                        final Handler mH = fetchMHInstance(app);
                        // hook ActivityThread.mH,将H.mCallBack替换为MHMessageHandler
                        sMHMessageInterceptor = new HandlerMessageInterceptor(mH, new MHMessageHandler(app));
                        sMHMessageInterceptor.install();
                    } else {
                        // >=8.1 hook ActivityThread.mInstrumentation,把他替换为TinkerHackInstrumentation
                        sTinkerHackInstrumentation = TinkerHackInstrumentation.create(app);
                        sTinkerHackInstrumentation.install();
                    }
                    sInstalled = true;
                }
            } catch (Throwable thr) {
                uninstall();
                throw new UnsupportedEnvironmentException(thr);
            }
        }
    }
}

AMSInterceptHandler替换activity

拦截所有调用AMS启动activity的方法,将target activity替换为代理

public class AMSInterceptHandler implements BinderInvocationHandler {
    private Object handleStartActivity(Object target, Method method, Object[] args) throws Throwable {
        int intentIdx = -1;
        for (int i = 0; i < args.length; ++i) {
            if (args[i] instanceof Intent) {
                intentIdx = i;
                break;
            }
        }
        if (intentIdx != -1) {
            // target activity intent
            final Intent newIntent = new Intent((Intent) args[intentIdx]);
            // 替换activity
            processActivityIntent(newIntent);
            args[intentIdx] = newIntent;
        }
        return method.invoke(target, args);
    }
    private void processActivityIntent(Intent intent) {
        // 原始activity包名和类名
        String origPackageName = null;
        String origClassName = null;
        if (intent.getComponent() != null) {
            origPackageName = intent.getComponent().getPackageName();
            origClassName = intent.getComponent().getClassName();
        } else {
            ......
        }
        if (IncrementComponentManager.isIncrementActivity(origClassName)) {
            final ActivityInfo origInfo = IncrementComponentManager.queryActivityInfo(origClassName);
            final boolean isTransparent = hasTransparentTheme(origInfo);
            // 根据将要跳转的activity信息得到tinker预先注册的代理activity类名
            // tinker loader manifest预先注册了一些activity,比如ActivityStubs$STDStub_00
            final String stubClassName = ActivityStubManager.assignStub(origClassName, origInfo.launchMode, isTransparent);
            // 用代理activity替换原activity
            storeAndReplaceOriginalComponentName(intent, origPackageName, origClassName, stubClassName);
        }
    }

    private void storeAndReplaceOriginalComponentName(Intent intent, String origPackageName, String origClassName, String stubClassName) {
        final ComponentName origComponentName = new ComponentName(origPackageName, origClassName);
        // 设置用于解析parcel的classLoader
        ShareIntentUtil.fixIntentClassLoader(intent, mContext.getClassLoader());
        // intent bundle中添加原始componentName信息,以便AMS处理完成后再替换回来
        intent.putExtra(EnvConsts.INTENT_EXTRA_OLD_COMPONENT, origComponentName);
        // intent设置componentName为代理activity的
        final ComponentName stubComponentName = new ComponentName(origPackageName, stubClassName);
        intent.setComponent(stubComponentName);
    }
}

MHMessageHandler还原activity

hook ActivityThread.mH.mCallBack,让它先调用到MessageHandler.handleMessage

public class MHMessageHandler implements MessageHandler {
     @Override
    public boolean handleMessage(Message msg) {
        int what = msg.what;
        if (what == LAUNCH_ACTIVITY) {
            try {
                final Object activityClientRecord = msg.obj;
                if (activityClientRecord == null) {
                    return false;
                }
                // 反射ActivityClientRecord.intent获取之前startActivity传给ams的intent
                final Field intentField = ShareReflectUtil.findField(activityClientRecord, "intent");
                final Intent maybeHackedIntent = (Intent) intentField.get(activityClientRecord);
                if (maybeHackedIntent == null) {
                    ShareTinkerLog.w(TAG, "cannot fetch intent from message received by mH.");
                    return false;
                }
                ShareIntentUtil.fixIntentClassLoader(maybeHackedIntent, mContext.getClassLoader());
                // 获取原始activity ComponentName
                final ComponentName oldComponent = maybeHackedIntent.getParcelableExtra(EnvConsts.INTENT_EXTRA_OLD_COMPONENT);
                if (oldComponent == null) {
                    ShareTinkerLog.w(TAG, "oldComponent was null, start " + maybeHackedIntent.getComponent() + " next.");
                    return false;
                }
                final Field activityInfoField = ShareReflectUtil.findField(activityClientRecord, "activityInfo");
                // 代理activityInfo
                final ActivityInfo aInfo = (ActivityInfo) activityInfoField.get(activityClientRecord);
                if (aInfo == null) {
                    return false;
                }
                // 原始activityInfo
                final ActivityInfo targetAInfo = IncrementComponentManager.queryActivityInfo(oldComponent.getClassName());
                if (targetAInfo == null) {
                    return false;
                }
                // 由于代理activity没有screenOrientation信息,这里需要还原target Activity相关信息
                fixActivityScreenOrientation(activityClientRecord, targetAInfo.screenOrientation);
                // target activityInfo字段copy到代理activityInfo,并替换componentName为target的
                fixStubActivityInfo(aInfo, targetAInfo);
                maybeHackedIntent.setComponent(oldComponent);
                maybeHackedIntent.removeExtra(EnvConsts.INTENT_EXTRA_OLD_COMPONENT);
            } catch (Throwable thr) {
                ShareTinkerLog.e(TAG, "exception in handleMessage.", thr);
            }
        }

        return false;
    }
}

TinkerHackInstrumentation还原activity

TinkerHackInstrumentation继承Instrumentation,通过反射将ActivityThread中Instrumentation替换完成hook,除了hook点不同外其他逻辑和AMSInterceptHandler差不多,这里就不贴代码了。

后记

加载补丁这部分通过大量的反射对于framework层进行了Hack,随着安卓版本的变迁以及高版本权限的收紧,以后将会越来越难适配,兼容性也难以保证,对于维护者来说道阻且长。

三篇文章完结,自此对于Tinker源码分析告一段落,我自己算是小有收获,也希望能帮到读者。