1、Tinker初始化
- Application
@Override
public void onBaseContextAttached(Context base) {
super.onBaseContextAttached(base);
TinkerInstaller.install(this);
}
public static Tinker install(ApplicationLike applicationLike) {
Tinker tinker = new Tinker.Builder(applicationLike.getApplication()).build();
Tinker.create(tinker);
tinker.install(applicationLike.getTinkerResultIntent());
return tinker;
}
在Application中调用TinkerInstaller.install()初始化,在install()中使用Tinker.Builder()创建Tinker对象,然后调用create()设置Tinker对外单例提供,
- Tinker.Builder
this.context = context;
this.mainProcess = TinkerServiceInternals.isInMainProcess(context);
this.patchProcess = TinkerServiceInternals.isInTinkerPatchServiceProcess(context);
this.patchDirectory = SharePatchFileUtil.getPatchDirectory(context);
if (this.patchDirectory == null) {
TinkerLog.e(TAG, "patchDirectory is null!");
return;
}
this.patchInfoFile = SharePatchFileUtil.getPatchInfoFile(patchDirectory.getAbsolutePath());
this.patchInfoLockFile = SharePatchFileUtil.getPatchInfoLockFile(patchDirectory.getAbsolutePath());
}
public Tinker build() {
if (loadReporter == null) {
loadReporter = new DefaultLoadReporter(context);
}
if (patchReporter == null) {
patchReporter = new DefaultPatchReporter(context);
}
if (listener == null) {
listener = new DefaultPatchListener(context);
}
if (tinkerLoadVerifyFlag == null) {
tinkerLoadVerifyFlag = false;
}
return new Tinker(context, status, loadReporter, patchReporter, listener, patchDirectory,
patchInfoFile, patchInfoLockFile, mainProcess, patchProcess, tinkerLoadVerifyFlag);
}
在TinkerBuiler的构造函数中主要完成了Tinker所需要的文件和文件夹的初始化,在build()中初始化loadReporter、patchReporter、listener,这些属性在Tinker的加载过程中至关重要。
2、下载补丁合成Patch包
- 在程序中获取到修复补丁包后开始加载
TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(),
Environment.getExternalStorageDirectory().getAbsolutePath() + "/patch.patch");
- 通过网络下载或本地保存的方式获取补丁包
- 调用TinkerInstaller.onReceiveUpgradePatch()传入补丁路径,通知Tinker有新的补丁包需要合成
- TinkerInstaller.onReceiveUpgradePatch()
public static void onReceiveUpgradePatch(Context context, String patchLocation) {
Tinker.with(context).getPatchListener().onPatchReceived(patchLocation);
}
- TinkerInstaller方法中直接调用getPatcherListener()获取Tinker中设置的PatchListener实例,此处获得的是在TinkerBuilder中默认的DefaultPatchListener,具体的设置过程在加载补丁包时说明;
- onPatchReceived(String path)
public int onPatchReceived(String path) {
File patchFile = new File(path); //创建补丁包文件
//(1)、
int returnCode = patchCheck(path, SharePatchFileUtil.getMD5(patchFile)); // ————————检查的细节
//(2)
if (returnCode == ShareConstants.ERROR_PATCH_OK) {
TinkerPatchService.runPatchService(context, path);//启动 TinkerPatchService
} else { //(3)、校验是失败调用LoadReporter通知失败
Tinker.with(context).getLoadReporter().onLoadPatchListenerReceiveFail(new File(path), returnCode);
}
return returnCode;
}
onPatchReceived()中执行以下操作:
- 获取补丁包文件后,检查Tinker的配置和path的合法性,主要检测是否开启Tinker、Tinker的服务是否可用、服务是否运行;
- 检查通过则启动服务,否则调用默认的LoadReporter通知检车失败
- TinkerPatchService.runPatchService()
public static void runPatchService(Context context, String path) {
TinkerLog.i(TAG, "run patch service...");
Intent intent = new Intent(context, TinkerPatchService.class);
intent.putExtra(PATCH_PATH_EXTRA, path);
intent.putExtra(RESULT_CLASS_EXTRA, resultServiceClass.getName());
try {
enqueueWork(context, TinkerPatchService.class, JOB_ID, intent);
} catch (Throwable thr) {
TinkerLog.e(TAG, "run patch service fail, exception:" + thr);
}
}
在runPatchService()中创建Intent保存补丁路径,调用enqueueWork()方法启动TinkerPatchService服务,enqueueWork()在TinkerJobIntentService类中,enqueueWork()中根据Android版本的不同分别使用JobSchedule和PowerManager执行服务
public static void enqueueWork(@NonNull Context context, @NonNull ComponentName component,
int jobId, @NonNull Intent work) {
synchronized (sLock) {
WorkEnqueuer we = getWorkEnqueuer(context, component, true, jobId);
we.ensureJobId(jobId);
we.enqueueWork(work);
}
}
static WorkEnqueuer getWorkEnqueuer(Context context, ComponentName cn, boolean hasJobId,
int jobId) {
WorkEnqueuer we = sClassWorkEnqueuer.get(cn); // 从缓存中获取任务
if (we == null) {
if (Build.VERSION.SDK_INT >= 26) {//SDK > 26 使用JobSchedule执行服务
we = new JobWorkEnqueuer(context, cn, jobId);
} else {
we = new CompatWorkEnqueuer(context, cn);
}
sClassWorkEnqueuer.put(cn, we);
}
return we;
}
使用JobSchedule或PowerManager启动TinkerPatchService后,程序最终执行将调用TinkerPatchService.onHandleWork()
@Override
protected void onHandleWork(Intent intent) {
//(1)进程保活
increasingPriority();
doApplyPatch(this, intent); //(2)执行解析
}
onHandleWork()中做了两件大事:
- 进程保活,为了让程序能在后台加载补丁完成,必须让进程在后台保留一段时间
- 调用doApplyPatch()解析补丁包,获取补丁包中的代码和资源;
- doApplyPatch()
private static void doApplyPatch(Context context, Intent intent) {
Tinker tinker = Tinker.with(context);
tinker.getPatchReporter().onPatchServiceStart(intent); //(1)
String path = getPatchPathExtra(intent); //(2)
File patchFile = new File(path); //(3)创建patch文件
//开始时间
long begin = SystemClock.elapsedRealtime();
//(3)
PatchResult patchResult = new PatchResult();
try {
//(4)执行Patch文件解析,此处的upgradePatchProcessor是UpgradePatch的实例
//tryPath中执行文件的复制并返回结果,主要工作都在这
result = upgradePatchProcessor.tryPatch(context, path, patchResult);
} catch (Throwable throwable) {
tinker.getPatchReporter().onPatchException(patchFile, e); //通知异常
}
cost = SystemClock.elapsedRealtime() - begin;
tinker.getPatchReporter().onPatchResult(patchFile, result, cost); //通知解析花费的时长
//(5)保存结果信息在patchResult中
patchResult.isSuccess = result;
patchResult.rawPatchFilePath = path;
patchResult.costTime = cost;
patchResult.e = e;
//(3)
AbstractResultService.runResultService(context, patchResult, getPatchResultExtra(intent));
sIsPatchApplying.set(false);
}
doApplyPatch()中干了跟多重要的事情:
- 调用PathReporter通知服务开始执行
- 从intent中获取传入的Patch路径并获取补丁包文件
- 创建PatchResult实例,保存执行解析的状态和结果
- 调用tryPatch()执行文件的检查和复制等操作,此处的upgradePatchProcessor是UpgradePatch的实例
- 保存结果信息在patchResult中
- 从Intent中获取设置的DefaultTinkerResultService类名,使用JibSchedule执行服务
- tryPatch():真正执行patch的地方,会在dataDir文件夹下创建dataDir/tinker/patch-xxx/patch-xxx.apk,并将要加载的Path文件内容复制到其中
@Override
public boolean tryPatch(Context context, String tempPatchPath, PatchResult patchResult) {
Tinker manager = Tinker.with(context);
final File patchFile = new File(tempPatchPath);
//(1)创建检查器检查补丁的签名和TinkerId
ShareSecurityCheck signatureCheck = new ShareSecurityCheck(context);
int returnCode = ShareTinkerInternals.checkTinkerPackage(context, manager.getTinkerFlags(), patchFile, signatureCheck);
//(2)从File文件中读取字节流,并将字节流转换成MD5字符串
String patchMd5 = SharePatchFileUtil.getMD5(patchFile);
//(3)保存Md5 的字符串作为本次补丁文件的版本代号
patchResult.patchVersion = patchMd5;
//获取的是dataDir/tinker文件路径(/data/data/com.xxxx.sample/tinker)
final String patchDirectory = manager.getPatchDirectory().getAbsolutePath();
////(4)在tinker文件夹下创建info.lock文件
File patchInfoLockFile = SharePatchFileUtil.getPatchInfoLockFile(patchDirectory);
//(5)在tinker文件夹下创建patch.info文件
File patchInfoFile = SharePatchFileUtil.getPatchInfoFile(patchDirectory);
//(6)读取文件信息,判断当前是否存在旧的补丁程序,(每次添加补丁包则会在patch.info和info.lock保存补丁信息)
SharePatchInfo oldInfo = SharePatchInfo.readAndCheckPropertyWithLock(patchInfoFile, patchInfoLockFile);
SharePatchInfo newInfo; //(7)创建SharePatchInfo保存新、旧的补丁版本信息
if (oldInfo != null) { //存在旧的补丁包,处理
//在新的newInfo中保存旧的版本信息
newInfo = new SharePatchInfo(oldInfo.oldVersion, patchMd5, false, Build.FINGERPRINT, finalOatDir);
} else {//第一次执行补丁包
newInfo = new SharePatchInfo("", patchMd5, false, Build.FINGERPRINT, ShareConstants.DEFAULT_DEX_OPTIMIZE_PATH);
}
final String patchName = SharePatchFileUtil.getPatchVersionDirectory(patchMd5); //创建 patch-xxx 文件名
final String patchVersionDirectory = patchDirectory + "/" + patchName; // 创建tinker/patch-xxx 文件
//(8)创建 dataDir/tinker/patch-xxx/patch-xxx.apk文件,将patch文件复制其中
File destPatchFile = new File(patchVersionDirectory + "/" + SharePatchFileUtil.getPatchVersionFile(patchMd5));
try {
// 校验patch-xxx.apk中生成和的MD5和 patchMd5是否一致,侧面检查内容是否已经复制过此补丁包
if (!patchMd5.equals(SharePatchFileUtil.getMD5(destPatchFile))) {
//(9)执行文件的复制
SharePatchFileUtil.copyFileUsingStream(patchFile, destPatchFile);
}
}
return true;
}
ShareTinkerInternals.checkTinkerPackage()中主要执行和校验一下内容:
- 检验patch的签名文件,将patch包下的以meta.txt结尾的文件(dex_meta.txt、package_meta.txt、res_meta.txt),以文件名为Key、内容中的字符串为Value 保存在ShareSecurityCheck的metaContentMap中
public boolean verifyPatchMetaSignature(File path) {
JarFile jarFile = null;
try {
jarFile = new JarFile(path);
final Enumeration<JarEntry> entries = jarFile.entries(); //获取JarFile中的所有文件
while (entries.hasMoreElements()) { // 遍历文件
JarEntry jarEntry = entries.nextElement();
final String name = jarEntry.getName();
if (name.startsWith("META-INF/")) {
continue;
}
if (!name.endsWith(ShareConstants.META_SUFFIX)) { // 查找后缀为meta.txt文件
continue;
}
metaContentMap.put(name, SharePatchFileUtil.loadDigestes(jarFile, jarEntry)); //读取meta.txt中的内容保存
Certificate[] certs = jarEntry.getCertificates();
}
}
return true;
}
- 读取package_meta.text中的Tinker和Patch的版本信息,并将属性保存在ShareSecurityCheck的packageProperties中
// package_meta.txt 中的信息
* #base package config field
* #Tue Jan 22 18:16:52 CST 2019
* platform=all
* NEW_TINKER_ID=tinker_id_1.0
* TINKER_ID=tinker_id_1.0
* patchMessage=tinker is sample to use
* patchVersion=1.0
public HashMap<String, String> getPackagePropertiesIfPresent() {
String property = metaContentMap.get(ShareConstants.PACKAGE_META_FILE); //从metaContentMap中获取package_meta.txt
String[] lines = property.split("\n");
for (final String line : lines) {
final String[] kv = line.split("=", 2); //Key、Value保存属性信息
packageProperties.put(kv[0].trim(), kv[1].trim());
}
return packageProperties;
}
- 执行校验patch 的TinkerId和 注册清单中的Id是否一致,不一致抛出异常
- 检验是否支持dex、so、resource修复
在执行checkTinkerPackage()检查OK后继续执行以下流程(具体见代码注释): 5. 从File文件中读取字节流,并将字节流转换成MD5字符串,此MD5字符串就作为补丁的版本号 6. 在dataDir/tinker文件路径下创建info.lock文件、patch.info文件 7. 读取文件信息,判断当前是否存在旧的补丁程序,创建SharePatchInfo保存新、旧的补丁版本信息 8. 创建 dataDir/tinker/patch-xxx/patch-xxx.apk文件,将patch文件即补丁包复制其中 9. 校验patch-xxx.apk中生成和的MD5和 patchMd5是否一致,侧面检查内容是否已经复制过此补丁包
- 复制好补丁包后对补丁包的处理(dex、Resource、SO),此处以dex的修复加载为例,调用DexDiffPatchInternal.tryRecoverDexFiles()加载dex文件
if (!DexDiffPatchInternal.tryRecoverDexFiles(manager, signatureCheck, context, patchVersionDirectory, destPatchFile)) {
return false;
}
- DexDiffPatchInternal.tryRecoverDexFiles()
protected static boolean tryRecoverDexFiles(Tinker manager, ShareSecurityCheck checker, Context context,
String patchVersionDirectory, File patchFile) {
String dexMeta = checker.getMetaContentMap().get(DEX_META_FILE); //
boolean result = patchDexExtractViaDexDiff(context, patchVersionDirectory, dexMeta, patchFile);
return result;
}
首先从checker的metaContentMa中获取assets/dex_meta.txt中内容,由上面的分析知道,metaContentMap中保存着meta.txt文件中的信息,这里获取其中的assets/dex_meta.txt中内容,dex_meta中记载着此次修改的补丁中包好的.dex文件
- 解析meta中的dex文件,并执行对比合并
private static boolean patchDexExtractViaDexDiff(Context context, String patchVersionDirectory, String meta, final File patchFile) {
String dir = patchVersionDirectory + "/" + DEX_PATH + "/“; //创建tinker/patch-xxxx/dex/
if (!extractDexDiffInternals(context, dir, meta, patchFile, TYPE_DEX)) {//解析assets/dex_meta.txt信息
return false;
}
File dexFiles = new File(dir);
File[] files = dexFiles.listFiles(); //遍历patch-xxxx/dex/中修复文件列表,找出其中dex、jar、apk的文件
List<File> legalFiles = new ArrayList<>();
if (files != null) {
for (File file : files) {
final String fileName = file.getName();
if (file.isFile()
&& (fileName.endsWith(ShareConstants.DEX_SUFFIX)
|| fileName.endsWith(ShareConstants.JAR_SUFFIX)
|| fileName.endsWith(ShareConstants.PATCH_SUFFIX))
) {
legalFiles.add(file); //legalFiles中保存所有的修复文件
}
}
}
final String optimizeDexDirectory = patchVersionDirectory + "/" + DEX_OPTIMIZE_PATH + "/“; //创建tinker/patch-xxxx/odex/
return dexOptimizeDexFiles(context, legalFiles, optimizeDexDirectory, patchFile); ????
}
在patchDexExtractViaDexDiff()中,首先创建tinker/patch-xxxx/dex/文件夹,然后解析meta中的信息保存在此文件中,然后遍历dex文件找出其中的dex、jar、apk文件保存在legalFiles集合中,然后执行dexOptimizeDexFiles()方法,在此先看一下如何解析meta信息的;
- extractDexDiffInternals()
private static boolean extractDexDiffInternals(Context context, String dir, String meta, File patchFile, int type) {
patchList.clear();
ShareDexDiffPatchInfo.parseDexDiffPatchInfo(meta, patchList); // 遍历meta中的信息,并封装在patchList集合中
// 创建 tinker/patch-xxxx/dex/ 目录
File directory = new File(dir);
Tinker manager = Tinker.with(context);
try {
ApplicationInfo applicationInfo = context.getApplicationInfo();
String apkPath = applicationInfo.sourceDir; //获取apk的路径
apk = new ZipFile(apkPath); //创建apk的ZipFile
patch = new ZipFile(patchFile); //创建补丁包 patch-xxx.apk的Zip文件
for (ShareDexDiffPatchInfo info : patchList) { //遍历patchFile中的文件
if (infoPath.equals("")) {
patchRealPath = info.rawName;
} else {
patchRealPath = info.path + "/" + info.rawName;
}
File extractedFile = new File(dir + info.realName); //在dex目录下创建每个dexFile
ZipEntry patchFileEntry = patch.getEntry(patchRealPath); //从补丁包中取出dex文件
ZipEntry rawApkFileEntry = apk.getEntry(patchRealPath); //从apk中取出dex文件
patchDexFile(apk, patch, rawApkFileEntry, patchFileEntry, info, extractedFile);//合并dex
}
}
//dex_meta.txt
classes.dex,,d4a8261c0b1ee8b309ac869fba20e1e1,d4a8261c0b1ee8b309ac869fba20e1e1,dac09804b9bf6728bcca1da866e3e8a4,2491048140,4231045191,jar
test.dex,,56900442eb5b7e1de45449d0685e6e00,56900442eb5b7e1de45449d0685e6e00,0,0,0,jar
//解析dex_meta.txt
public static void parseDexDiffPatchInfo(String meta, ArrayList<ShareDexDiffPatchInfo> dexList) {
String[] lines = meta.split("\n");
for (final String line : lines) {
final String name = kv[0].trim(); //class.dex
final String path = kv[1].trim();
final String destMd5InDvm = kv[2].trim();
final String destMd5InArt = kv[3].trim();
final String dexDiffMd5 = kv[4].trim();
final String oldDexCrc = kv[5].trim();
final String newDexCrc = kv[6].trim();
final String dexMode = kv[7].trim();
ShareDexDiffPatchInfo dexInfo = new ShareDexDiffPatchInfo(name, path, destMd5InDvm, destMd5InArt,
dexDiffMd5, oldDexCrc, newDexCrc, dexMode);
dexList.add(dexInfo);
}
}
总结一下:
- 首先执行parseDexDiffPatchInfo()从meta字符串中解析出补丁包的dex文件及其版本信息,将信息封装在ShareDexDiffPatchInfo的实例中并保存在pathList的集合中
- 获取apk原来的ZipFile和patch的ZipFile
- 遍历pathList集合,对其中每个dex文件在 tinker/patch-xxxx/dex/目录下创建对应的目标文件
- 分别从apk和patch的ZipFile中取出对应的dex文件,执行patchDexFile()通过算法合并写入目标文件中,此文件即为加载的修复文件
- patchDexFile():根据apk和补丁包中的dex文件通过算法对比合并生成修复文件
private static void patchDexFile(ZipFile baseApk, ZipFile patchPkg, ZipEntry oldDexEntry, ZipEntry patchFileEntry,
ShareDexDiffPatchInfo patchInfo, File patchedDexFile) throws IOException {
try {
//获取apk中要修复文件的输入流
InputStream oldDexStream = new BufferedInputStream(baseApk.getInputStream(oldDexEntry));
//获取补丁包中修复文件的输入流
InputStream patchFileStream = (patchFileEntry != null ? new BufferedInputStream(patchPkg.getInputStream(patchFileEntry)) : null);
final boolean isRawDexFile = SharePatchFileUtil.isRawDexFile(patchInfo.rawName); //判断patch是否是.dex文件
if (!isRawDexFile || patchInfo.isJarMode) { //处理.jar文件
ZipOutputStream zos = null;
try {
zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(patchedDexFile)));
zos.putNextEntry(new ZipEntry(ShareConstants.DEX_IN_JAR));
if (!isRawDexFile) {
ZipInputStream zis = null;
try {
zis = new ZipInputStream(oldDexStream);
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
if (ShareConstants.DEX_IN_JAR.equals(entry.getName())) break;
}
new DexPatchApplier(zis, patchFileStream).executeAndSaveTo(zos);
}
} else {
new DexPatchApplier(oldDexStream, patchFileStream).executeAndSaveTo(zos);
}
}
} else {
new DexPatchApplier(oldDexStream, patchFileStream).executeAndSaveTo(patchedDexFile); //处理.dex文件
}
}
}
- 合并下发的pathch后,执行DefaultTinkerResultService服务杀死进程在重启时加载使用补丁
//从Intent中获取设置的DefaultTinkerResultService类名,使用JibSchedule执行服务
AbstractResultService.runResultService(context, patchResult, getPatchResultExtra(intent));//从Intent中获取
- DefaultTinkerResultService
public void onPatchResult(PatchResult result) {
//停止TinkerPatchService服务进程
TinkerServiceInternals.killTinkerPatchServiceProcess(getApplicationContext());
if (result.isSuccess) {
deleteRawPatchFile(new File(result.rawPatchFilePath)); //rawPatchFilePath为保存的是传入时的Patch路径
if (checkIfNeedKill(result)) {
android.os.Process.killProcess(android.os.Process.myPid()); //杀死当前进程
}
}
}
3、重启时加载补丁包
在获取和解析补丁包后系统会重启,在重启时会执行Application的onBaseContextAttached(),在TinkerApplication的onBaseContextAttached中调用loadTinker(),loadTinker中通过反射执行TinkerLoader的tryLoad()方法:
- TinkerApplication
private void onBaseContextAttached(Context base) {
loadTinker();
}
private static final String TINKER_LOADER_METHOD = "tryLoad";
private void loadTinker() {
try {
Class<?> tinkerLoadClass = Class.forName(loaderClassName, false, getClassLoader());
Method loadMethod = tinkerLoadClass.getMethod(TINKER_LOADER_METHOD, TinkerApplication.class);
Constructor<?> constructor = tinkerLoadClass.getConstructor();
tinkerResultIntent = (Intent) loadMethod.invoke(constructor.newInstance(), this);
}
}
- tryLoad():直接调用tryLoadPatchFilesInternal()
public Intent tryLoad(TinkerApplication app) {
tryLoadPatchFilesInternal(app, resultIntent);
return resultIntent;
}
- tryLoadPatchFilesInternal()
//(1)检查并获取工作目录dataDir/tinker文件
File patchDirectoryFile = SharePatchFileUtil.getPatchDirectory(app);
String patchDirectoryPath = patchDirectoryFile.getAbsolutePath();
//(2)检查获取tinker/patch.info 和 info.lock文件
File patchInfoFile = SharePatchFileUtil.getPatchInfoFile(patchDirectoryPath);
File patchInfoLockFile = SharePatchFileUtil.getPatchInfoLockFile(patchDirectoryPath);
//(3)从patch.info 和 info.lock两个文件中读取保存的PatchInfo信息
patchInfo = SharePatchInfo.readAndCheckPropertyWithLock(patchInfoFile, patchInfoLockFile);
//patch-641e634c
String patchName = SharePatchFileUtil.getPatchVersionDirectory(version);
//获取 tinker/patch-641e634c
String patchVersionDirectory = patchDirectoryPath + "/" + patchName;
//(4)检查获取补丁包目录 tinker/patch-641e634c
File patchVersionDirectoryFile = new File(patchVersionDirectory);
//(5)检查获取保存拷贝内容的 tinker/patch-641e634c/patch-641e634c.apk
final String patchVersionFileRelPath = SharePatchFileUtil.getPatchVersionFile(version);
File patchVersionFile = (patchVersionFileRelPath != null ? new File(patchVersionDirectoryFile.getAbsolutePath(), patchVersionFileRelPath) : null);
//(6) 校验patch-641e634c.apk文件 (重点 :检验签名)
ShareSecurityCheck securityCheck = new ShareSecurityCheck(app); ////和下发时校验一样校验patch-641e634c.apk
int returnCode = ShareTinkerInternals.checkTinkerPackage(app, tinkerFlag, patchVersionFile, securityCheck);
//(7)检查meta文件中记录的dex文件是否存在,并将结果保存在loadDexList集合中
boolean dexCheck = TinkerDexLoader.checkComplete(patchVersionDirectory, securityCheck, oatDex, resultIntent);
//处理DexLoader
if (isEnabledForDex) {
//执行dex文件的加载
boolean loadTinkerJars = TinkerDexLoader.loadTinkerJars(app, patchVersionDirectory, oatDex, resultIntent, isSystemOTA);
}
}
上面的整个流程主要执行拿到上面解析保存的patch文件,经过系列检验后加载合并所有的dex文件,具体细节如下:
- 检查并获取工作目录dataDir/tinker文件,并从中获取tinker/patch.info 和 info.lock文件
- 从tinker的信息文件中读取信息保存在PatchInfo中,获取插件的版本
- 根据补丁版本获取到前面复制的补丁apk文件
- 校验补丁apk和宿主apk的签名是否一致
- 调用TinkerDexLoader.checkComplete()检车meta文件中记录的dex文件,并将结果保存在loadDexList集合中
public static boolean checkComplete(String directory, ShareSecurityCheck securityCheck, String oatDir, Intent intentResult) {
//(1)从metaContentMap中获取assets/dex_meta.txt文件中的字符串
String meta = securityCheck.getMetaContentMap().get(DEX_MEAT_FILE);
ArrayList<ShareDexDiffPatchInfo> allDexInfo = new ArrayList<>();
ShareDexDiffPatchInfo.parseDexDiffPatchInfo(meta, allDexInfo);// (2)解析meta中的信息保存在allDexInfo集合中
HashMap<String, String> dexes = new HashMap<>();
for (ShareDexDiffPatchInfo info : allDexInfo) { //(3)遍历allDexInfo集合
if (isJustArtSupportDex(info)) {
continue;
}
if (isVmArt && info.rawName.startsWith(ShareConstants.TEST_DEX_NAME)) {
testInfo = info; //获取test.dex
} else if (isVmArt && ShareConstants.CLASS_N_PATTERN.matcher(info.realName).matches()) {
classNDexInfo.add(info); //保存.jar文件
} else {
dexes.put(info.realName, getInfoMd5(info));
loadDexList.add(info); //保存补丁的文件信息
}
}
if (isVmArt
&& (testInfo != null || !classNDexInfo.isEmpty())) {
if (testInfo != null) {
classNDexInfo.add(ShareTinkerInternals.changeTestDexToClassN(testInfo, classNDexInfo.size() + 1));
}
dexes.put(ShareConstants.CLASS_N_APK_NAME, "");
}
return true;
}
- 执行所有补丁包中dex文件的加载、合并
public static boolean loadTinkerJars(final TinkerApplication application, String directory, String oatDir, Intent intentResult, boolean isSystemOTA) {
//获取系统加载的ClassLoader
PathClassLoader classLoader = (PathClassLoader) TinkerDexLoader.class.getClassLoader();
//在tinker/patch-641e634c/下创建dex文件夹
String dexPath = directory + "/" + DEX_PATH + "/";
ArrayList<File> legalFiles = new ArrayList<>();
for (ShareDexDiffPatchInfo info : loadDexList) {
//忽略仅支持art的文件
if (isJustArtSupportDex(info)) {
continue;
}
//遍历文件集合,
String path = dexPath + info.realName;
File file = new File(path);
legalFiles.add(file); //遍历集合找到dex/目录下要加载的File集合
}
}
SystemClassLoaderAdder.installDexes(application, classLoader, optimizeDir, legalFiles); //执行ClassLoader加载
}
installDexes()方法中根据不同的版本,调用对应的install()方法执行加载,在install()中使用ClassLoader加载每个文件中的dex文件,然后将获取到的Elements合并在原本程序类的前面,达到替换原来类文件的目的,关于ClassLoader的合并和加载参见Android热修复之路(一)——ClassLoader
private static void install(ClassLoader loader, List<File> additionalClassPathEntries,
File optimizedDirectory) {
Field pathListField = ShareReflectUtil.findField(loader, "pathList");
Object dexPathList = pathListField.get(loader); //获取loader中的pathList
ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
//先反射调用dexPathList中的makePathElements()将List<File>解析成Elements数组
//将获取到的解析成Elements数组合并原来的数组,然后设置为dexPathList
ShareReflectUtil.expandFieldArray(dexPathList, "dexElements", makePathElements(dexPathList,
new ArrayList<File>(additionalClassPathEntries), optimizedDirectory,
suppressedExceptions));
}
整个Tinker的执行流程就到此结束了,也让我从源码的角度理解了Tinker的强大,希望本篇能对想深入学习Tinker的同学有所帮助。