代碼基于tinker 1.9.14.7命雀。
Tinker熱修復(fù)從使用上來(lái)看主要有三方面:
- TinkerApplication 啟動(dòng)過(guò)程相關(guān)
- TinkerInstaller.installTinker 初始化相關(guān)
- TinkerInstaller.onReceiveUpgradePatch 加載patch包
這三點(diǎn)形成一個(gè)完整的熱修復(fù)閉環(huán)扒秸,后續(xù)文章依次從這三個(gè)點(diǎn)鋪開(kāi)來(lái)分析,先對(duì)Tinker有個(gè)全面了解牺堰。本篇文章先從TinkerApplication 啟動(dòng)過(guò)程相關(guān)開(kāi)始饶米。
一增炭、動(dòng)態(tài)生成Application
接入 Tinker 官方推薦是利用 @DefaultLifeCycle 動(dòng)態(tài)生成 Application凰盔。這里用到了apt在編譯時(shí)生成類(lèi)的方式。通過(guò)AnnotationProcessor 按resouces/TinkerAnnoApplication.tmpl模板生成掷贾。
public class %APPLICATION% extends TinkerApplication {
public %APPLICATION%() {
super(%TINKER_FLAGS%, "%APPLICATION_LIFE_CYCLE%", "%TINKER_LOADER_CLASS%", %TINKER_LOAD_VERIFY_FLAG%);
}
}
動(dòng)態(tài)生成的application是繼承于TinkerApplication睛榄。
protected TinkerApplication(int tinkerFlags) {
this(tinkerFlags, "com.tencent.tinker.entry.DefaultApplicationLike",
TinkerLoader.class.getName(), false);
}
TinkerApplication初始化時(shí)關(guān)聯(lián)DefaultApplicationLike,它作為生成的application的代理類(lèi)供外部調(diào)用者使用胯盯。
接下來(lái)再看
private void onBaseContextAttached(Context base) {
try {
final long applicationStartElapsedTime = SystemClock.elapsedRealtime();
final long applicationStartMillisTime = System.currentTimeMillis();
loadTinker();
mCurrentClassLoader = base.getClassLoader();
mInlineFence = createInlineFence(this, tinkerFlags, delegateClassName,
tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime,
tinkerResultIntent);
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);
}
}
在TinkerApplication的onBaseContextAttached中懈费,主要做了兩件事:
- loadTinker: 反射執(zhí)行TinkerLoader.tryLoad 方法。
- createInlineFence博脑,綁定代理DefaultApplicationLike憎乙。
二、TinkerLoader.tryLoad 加載補(bǔ)丁包
這里重點(diǎn)看看TinkerLoader.tryLoad叉趣,tryLoad直接調(diào)用tryLoadPatchFilesInternal,這個(gè)方法非常長(zhǎng)泞边,那么來(lái)耐心看下:
private void tryLoadPatchFilesInternal(TinkerApplication app, Intent resultIntent) {
final int tinkerFlag = app.getTinkerFlags();
//確保tinker enable 且非patch進(jìn)程
if (!ShareTinkerInternals.isTinkerEnabled(tinkerFlag)) {
Log.w(TAG, "tryLoadPatchFiles: tinker is disable, just return");
ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_DISABLE);
return;
}
if (ShareTinkerInternals.isInPatchProcess(app)) {
Log.w(TAG, "tryLoadPatchFiles: we don't load patch with :patch process itself, just return");
ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_DISABLE);
return;
}
//tinker
//PatchDirectory:/data/data/tinker.sample.android/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();
// 檢查tinker目錄是否存在
//check patch directory whether exist
if (!patchDirectoryFile.exists()) {
Log.w(TAG, "tryLoadPatchFiles:patch dir not exist:" + patchDirectoryPath);
ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_DIRECTORY_NOT_EXIST);
return;
}
//tinker/patch.info 補(bǔ)丁信息文件
File patchInfoFile = SharePatchFileUtil.getPatchInfoFile(patchDirectoryPath);
//check patch info file whether exist
if (!patchInfoFile.exists()) {
Log.w(TAG, "tryLoadPatchFiles:patch info not exist:" + patchInfoFile.getAbsolutePath());
ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_INFO_NOT_EXIST);
return;
}
//獲取patch.info并包裝為SharePatchInfo
//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;
}
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) {
//it is nice to clean patch
Log.w(TAG, "tryLoadPatchFiles:onPatchInfoCorrupted");
ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_INFO_CORRUPTED);
return;
}
boolean mainProcess = ShareTinkerInternals.isInMainProcess(app);
boolean isRemoveNewVersion = patchInfo.isRemoveNewVersion;
//是否清除新patch
if (mainProcess) {
final String patchName = SharePatchFileUtil.getPatchVersionDirectory(newVersion);
// So far new version is not loaded in main process and other processes.
// We can remove new version directory safely.
if (isRemoveNewVersion) {
Log.w(TAG, "found clean patch mark and we are in main process, delete patch file now.");
if (patchName != null) {
// oldVersion.equals(newVersion) means the new version has been loaded at least once
// after it was applied.
final boolean isNewVersionLoadedBefore = oldVersion.equals(newVersion);
if (isNewVersionLoadedBefore) {
// Set oldVersion and newVersion to empty string to clean patch
// if current patch has been loaded before.
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;
}
}
}
if (patchInfo.isRemoveInterpretOATDir) {
// delete interpret odex
// for android o, directory change. Fortunately, we don't need to support android o interpret mode any more
Log.i(TAG, "tryLoadPatchFiles: isRemoveInterpretOATDir is true, try to delete interpret optimize files");
patchInfo.isRemoveInterpretOATDir = false;
SharePatchInfo.rewritePatchInfoFileWithLock(patchInfoFile, patchInfo, patchInfoLockFile);
ShareTinkerInternals.killProcessExceptMain(app);
String patchVersionDirFullPath = patchDirectoryPath + "/" + patchName;
SharePatchFileUtil.deleteDir(patchVersionDirFullPath + "/" + ShareConstants.INTERPRET_DEX_OPTIMIZE_PATH);
}
}
resultIntent.putExtra(ShareIntentUtil.INTENT_PATCH_OLD_VERSION, oldVersion);
resultIntent.putExtra(ShareIntentUtil.INTENT_PATCH_NEW_VERSION, newVersion);
boolean versionChanged = !(oldVersion.equals(newVersion));
boolean oatModeChanged = oatDex.equals(ShareConstants.CHANING_DEX_OPTIMIZE_PATH);
oatDex = ShareTinkerInternals.getCurrentOatMode(app, oatDex);
resultIntent.putExtra(ShareIntentUtil.INTENT_PATCH_OAT_DIR, oatDex);
// 根據(jù)版本變化和是否是主進(jìn)程的條件決定是否加載新patch
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
final String patchVersionFileRelPath = SharePatchFileUtil.getPatchVersionFile(version);
//獲取diff patch文件
File patchVersionFile = (patchVersionFileRelPath != null ? new File(patchVersionDirectoryFile.getAbsolutePath(), patchVersionFileRelPath) : null);
if (!SharePatchFileUtil.isLegalFile(patchVersionFile)) {
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);
// 1. 檢查補(bǔ)丁包 apk 的簽名
// 2. 檢查基準(zhǔn)包的 tinker id 與補(bǔ)丁包中是否一致
// 3. 檢查 tinker 設(shè)置與補(bǔ)丁包中的類(lèi)型是否符合
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);
//com.huawei.ark.app.ArkApplicationInfo arthot是對(duì)華為的一種單獨(dú)修復(fù)支持
final boolean isArkHotRuning = ShareTinkerInternals.isArkHotRuning();
if (!isArkHotRuning && isEnabledForDex) {
//tinker/patch.info/patch-641e634c/dex
boolean dexCheck = TinkerDexLoader.checkComplete(patchVersionDirectory, securityCheck, oatDex, resultIntent);
if (!dexCheck) {
//file not found, do not load patch
Log.w(TAG, "tryLoadPatchFiles:dex check fail");
return;
}
}
final boolean isEnabledForArkHot = ShareTinkerInternals.isTinkerEnabledForArkHot(tinkerFlag);
if (isArkHotRuning && isEnabledForArkHot) {
boolean arkHotCheck = TinkerArkHotLoader.checkComplete(patchVersionDirectory, securityCheck, resultIntent);
if (!arkHotCheck) {
// 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
boolean libCheck = TinkerSoLoader.checkComplete(patchVersionDirectory, securityCheck, resultIntent);
if (!libCheck) {
//file not found, do not load patch
Log.w(TAG, "tryLoadPatchFiles:native lib check fail");
return;
}
}
//check resource
final boolean isEnabledForResource = ShareTinkerInternals.isTinkerEnabledForResource(tinkerFlag);
Log.w(TAG, "tryLoadPatchFiles:isEnabledForResource:" + isEnabledForResource);
if (isEnabledForResource) {
boolean resourceCheck = TinkerResourceLoader.checkComplete(app, patchVersionDirectory, securityCheck, resultIntent);
if (!resourceCheck) {
//file not found, do not load patch
Log.w(TAG, "tryLoadPatchFiles:resource check fail");
return;
}
}
//art環(huán)境且系統(tǒng)做ota升級(jí),需要重新loadDexFile,因?yàn)橹暗膐dex會(huì)失效疗杉。
//only work for art platform oat阵谚,because of interpret, refuse 4.4 art oat
//android o use quicken default, we don't need to use interpret mode
boolean isSystemOTA = ShareTinkerInternals.isVmArt()
&& ShareTinkerInternals.isSystemOTA(patchInfo.fingerPrint)
&& Build.VERSION.SDK_INT >= 21 && !ShareTinkerInternals.isAfterAndroidO();
resultIntent.putExtra(ShareIntentUtil.INTENT_PATCH_SYSTEM_OTA, isSystemOTA);
//we should first try rewrite patch info file, if there is a error, we can't load jar
if (mainProcess) {
if (versionChanged) {
patchInfo.oldVersion = version;
}
if (oatModeChanged) {
patchInfo.oatDir = oatDex;
patchInfo.isRemoveInterpretOATDir = true;
}
}
if (!checkSafeModeCount(app)) {
resultIntent.putExtra(ShareIntentUtil.INTENT_PATCH_EXCEPTION, new TinkerRuntimeException("checkSafeModeCount fail"));
ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_UNCAUGHT_EXCEPTION);
Log.w(TAG, "tryLoadPatchFiles:checkSafeModeCount fail");
return;
}
//now we can load patch jar
if (!isArkHotRuning && isEnabledForDex) {
//加載dex補(bǔ)丁
boolean loadTinkerJars = TinkerDexLoader.loadTinkerJars(app, patchVersionDirectory, oatDex, resultIntent, isSystemOTA, isProtectedApp);
if (isSystemOTA) {
// update fingerprint after load success
patchInfo.fingerPrint = Build.FINGERPRINT;
patchInfo.oatDir = loadTinkerJars ? ShareConstants.INTERPRET_DEX_OPTIMIZE_PATH : ShareConstants.DEFAULT_DEX_OPTIMIZE_PATH;
// reset to false
oatModeChanged = false;
if (!SharePatchInfo.rewritePatchInfoFileWithLock(patchInfoFile, patchInfo, patchInfoLockFile)) {
ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_REWRITE_PATCH_INFO_FAIL);
Log.w(TAG, "tryLoadPatchFiles:onReWritePatchInfoCorrupted");
return;
}
// update oat dir
resultIntent.putExtra(ShareIntentUtil.INTENT_PATCH_OAT_DIR, patchInfo.oatDir);
}
if (!loadTinkerJars) {
Log.w(TAG, "tryLoadPatchFiles:onPatchLoadDexesFail");
return;
}
}
if (isArkHotRuning && isEnabledForArkHot) {
boolean loadArkHotFixJars = TinkerArkHotLoader.loadTinkerArkHot(app, patchVersionDirectory, resultIntent);
if (!loadArkHotFixJars) {
Log.w(TAG, "tryLoadPatchFiles:onPatchLoadArkApkFail");
return;
}
}
//now we can load patch resource
if (isEnabledForResource) {
//加載資源補(bǔ)丁
boolean loadTinkerResources = TinkerResourceLoader.loadTinkerResources(app, patchVersionDirectory, resultIntent);
if (!loadTinkerResources) {
Log.w(TAG, "tryLoadPatchFiles:onPatchLoadResourcesFail");
return;
}
}
// Init component hotplug support.
if ((isEnabledForDex || isEnabledForArkHot) && isEnabledForResource) {
ComponentHotplug.install(app, securityCheck);
}
if (!AppInfoChangedBlocker.tryStart(app)) {
Log.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);
Log.w(TAG, "tryLoadPatchFiles:onReWritePatchInfoCorrupted");
return;
}
ShareTinkerInternals.killProcessExceptMain(app);
}
//all is ok!
ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_OK);
Log.i(TAG, "tryLoadPatchFiles: load end, ok!");
}
簡(jiǎn)單總結(jié)這個(gè)長(zhǎng)方法的工作:
- 1.一系列檢查:tinker功能是否打開(kāi)、tinker文件夾是否存在烟具、patch.info文件是否存在梢什。
- 2.通過(guò)patch.info校驗(yàn)patch有效性,決定是否加載new patch朝聋。
- 3.補(bǔ)丁包校驗(yàn):簽名檢查嗡午、tinkerid是否與基準(zhǔn)包一致等。
- 4.檢查tinkerFlag確認(rèn)開(kāi)啟了哪些修復(fù)功能:
application初始化時(shí)配置的tinkerFlag:
ShareConstants.java
public static final int TINKER_DISABLE = 0x00;
public static final int TINKER_DEX_MASK = 0x01;
public static final int TINKER_NATIVE_LIBRARY_MASK = 0x02;
public static final int TINKER_RESOURCE_MASK = 0x04;
public static final int TINKER_ARKHOT_MASK = 0x08;
public static final int TINKER_DEX_AND_LIBRARY = TINKER_DEX_MASK | TINKER_NATIVE_LIBRARY_MASK | TINKER_ARKHOT_MASK;
public static final int TINKER_ENABLE_ALL = TINKER_DEX_MASK | TINKER_NATIVE_LIBRARY_MASK | TINKER_RESOURCE_MASK | TINKER_ARKHOT_MASK;
- 5.加載dex冀痕、arthot荔睹、resource補(bǔ)丁狸演。這里并沒(méi)有嘗試加載so,arthot是針對(duì)華為的僻他,在1.9.6之后的某個(gè)版本專(zhuān)門(mén)針對(duì)華為新增的修復(fù)類(lèi)型宵距。
6.art環(huán)境且系統(tǒng)做ota升級(jí),需要重新loadDexFile,因?yàn)橹暗膐dex會(huì)失效吨拗。
另外满哪,要強(qiáng)調(diào)的是:tinker文件夾是在執(zhí)行合成的時(shí)候生成的,后續(xù)再分析丢胚,這里先看看文件夾內(nèi)容:
cepheus:/data/data/com.stan.tinkersdkdemo/tinker # ls -al
total 36
drwx------ 3 u0_a350 u0_a350 4096 2020-08-12 10:14 .
drwx------ 8 u0_a350 u0_a350 4096 2020-08-12 10:14 ..
-rw------- 1 u0_a350 u0_a350 0 2020-08-12 10:15 info.lock
drwx------ 4 u0_a350 u0_a350 4096 2020-08-12 10:14 patch-8b79c8cc
-rw------- 1 u0_a350 u0_a350 367 2020-08-12 10:15 patch.info // patch信息描述文件
cepheus:/data/data/com.stan.tinkersdkdemo/tinker # cat patch.info
#from old version:8b79c8cc9881601c1e53e20bbe320026 to new version:8b79c8cc9881601c1e53e20bbe320026
#Wed Aug 12 10:15:25 GMT+08:00 2020
old=8b79c8cc9881601c1e53e20bbe320026 //舊包md5
is_remove_interpret_oat_dir=0
print=Xiaomi/cepheus/cepheus\:10/QKQ1.190825.002/9.10.29\:user/release-keys
dir=odex
is_protected_app=0
is_remove_new_version=0
new=8b79c8cc9881601c1e53e20bbe320026 //新包md5
cepheus:/data/data/com.stan.tinkersdkdemo/tinker/patch-8b79c8cc # ls -al
total 40
drwx------ 4 u0_a350 u0_a350 4096 2020-08-12 10:14 .
drwx------ 3 u0_a350 u0_a350 4096 2020-08-12 10:14 ..
drwx------ 3 u0_a350 u0_a350 4096 2020-08-12 10:14 dex
drwx------ 2 u0_a350 u0_a350 4096 2020-08-12 10:14 odex
-rw------- 1 u0_a350 u0_a350 3443 2020-08-12 10:14 patch-8b79c8cc.apk //合成前的diff patch包
cepheus:/data/data/com.stan.tinkersdkdemo/tinker/patch-8b79c8cc/dex # ls -al
total 2328
drwx------ 3 u0_a350 u0_a350 4096 2020-08-12 10:14 .
drwx------ 4 u0_a350 u0_a350 4096 2020-08-12 10:14 ..
drwxrwx--x 3 u0_a350 u0_a350 4096 2020-08-12 10:14 oat
-rw------- 1 u0_a350 u0_a350 2351677 2020-08-12 10:14 tinker_classN.apk //合成后的patch包
再看看patch包構(gòu)成:
信息保存在assets中
- package_meta.txt 補(bǔ)丁包的基本信息
- dex_meta.txt dex補(bǔ)丁的信息
- so_meta.txt so補(bǔ)丁的信息
- res_meta.txt 資源補(bǔ)丁的信息
package_meta.txt
#base package config field
#Wed Aug 12 10:13:45 CST 2020
NEW_TINKER_ID=tinker_id_1.1
TINKER_ID=tinker_id_1.1
is_protected_app=0
patchMessage=fix the 1.0 version's bugs
patchVersion=1.0
dex_meta.txt
classes.dex,,4d97b3f8bcfdffb05e3ed5db5cbb8f83,4d97b3f8bcfdffb05e3ed5db5cbb8f83,35b4ca58525ecb64e20cd315d5832c70,3533914992,3549684149,jar
test.dex,,56900442eb5b7e1de45449d0685e6e00,56900442eb5b7e1de45449d0685e6e00,0,0,0,jar
TinkerApplication啟動(dòng)過(guò)程核心功能主要分兩點(diǎn):
- 1通過(guò)@DefaultLifeCycle 利用apt動(dòng)態(tài)生成 Application翩瓜。這里tinker使用ApplicationLike作為Application的代理受扳,它綁定了Application的生命周期携龟,提供給客戶(hù)端使用。
- 2 TinkerLoader.tryLoad 嘗試加載補(bǔ)丁包勘高。
這里加載補(bǔ)丁包類(lèi)型:
- dex
- so
- arkhot 針對(duì)華為方舟os
- resource