_StriveG Blog

tinker-application

前言

从上文的一些知识中,知道,真正的Application是注解生成的,并且继承与TinkerApplication。那么我们就来分析下。

attachBaseContext

首先看attachBaseContext这个方法。

1
2
3
4
5
6
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
Thread.setDefaultUncaughtExceptionHandler(new TinkerUncaughtHandler(this));
onBaseContextAttached(base);
}
  • 设置UncaughtExceptionHandler
  • 调用onBaseContextAttached方法。

onBaseContextAttached方法代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private void onBaseContextAttached(Context base) {
applicationStartElapsedTime = SystemClock.elapsedRealtime();
applicationStartMillisTime = System.currentTimeMillis();
loadTinker();
ensureDelegate();
applicationLike.onBaseContextAttached(base);
//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();
}
}
  • 获取系统已运行时间,作为applicationStartElapsedTime
  • 获取系统当前时间,作为applicationStartMillisTime
  • loadTinker() load tinker
  • ensureDelegate 确认下delegate是否没有初始化
  • 调用applicationLike的onBaseContextAttached方法,对应demo中的SampleApplicationLike的onBaseContextAttached方法。
  • 如果是安全模式,就获取当前进程名,preferName,并将安全模式count置为0。

loadTinker

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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);
}
}
  • 首先判断flag,如果为TINKER_DISABLE,直接return
  • 生成tinkerLoadClass的Class
  • 获得tryLoad方法的Method
  • 获取tinkerLoadClass的构造方法
  • 调用tryLoad方法返回Intent对象

ensureDelegate

1
2
3
4
5
private synchronized void ensureDelegate() {
if (applicationLike == null) {
applicationLike = createDelegate();
}
}

在这个方法中,会判断applicationLike是不是null,如果是的话,就调用createDelegate去创建一个代理对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
private ApplicationLike 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);
return (ApplicationLike) constructor.newInstance(this, tinkerFlags, tinkerLoadVerifyFlag,
applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);
} catch (Throwable e) {
throw new TinkerRuntimeException("createDelegate failed", e);
}
}

在这个方法中,通过反射构造出一个代理对象,根据demo传入的参数,这里构造出来的对象,是SampleApplicationLike。

在看下SampleApplicationLike的构造方法吧。

1
2
3
4
public SampleApplicationLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag,
long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent) {
super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);
}

参数很明显,我们还需要看下继承关系。SampleApplicationLike->DefaultApplicationLike->ApplicationLike,ApplicationLike实现了ApplicationLifeCycle的一系列接口。这个接口完全按照Application的一些方法,比如onCreate,onLowMemory等等。

SampleApplicationLike的构造方法会调用父类的构造方法,最后调用到ApplicationLike的。

1
2
3
4
5
6
7
8
9
public ApplicationLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag,
long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent) {
this.application = application;
this.tinkerFlags = tinkerFlags;
this.tinkerLoadVerifyFlag = tinkerLoadVerifyFlag;
this.applicationStartElapsedTime = applicationStartElapsedTime;
this.applicationStartMillisTime = applicationStartMillisTime;
this.tinkerResultIntent = tinkerResultIntent;
}

这里就是把参数进行初始化。

SampleApplicationLike#onBaseContextAttached

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
@Override
public void onBaseContextAttached(Context base) {
super.onBaseContextAttached(base);
//you must install multiDex whatever tinker is installed!
MultiDex.install(base);
SampleApplicationContext.application = getApplication();
SampleApplicationContext.context = getApplication();
TinkerManager.setTinkerApplicationLike(this);
TinkerManager.initFastCrashProtect();
//should set before tinker is installed
TinkerManager.setUpgradeRetryEnable(true);
//optional set logIml, or you can use default debug log
TinkerInstaller.setLogIml(new MyLogImp());
//installTinker after load multiDex
//or you can put com.tencent.tinker.** to main dex
TinkerManager.installTinker(this);
Tinker tinker = Tinker.with(getApplication());
}
  • 首先调用MultiDex.install方法
  • 然后初始化一些个参数
  • 调用TinkerManager的一些方法来初始化或者设置一些值
  • 然后installTinker
  • 生成一个Tinker对象

###

小结

从逻辑流程来看,核心部分在于TinkerLoadeClass的tryLoad,以及TinkerManager的一些方法。其他的方法就是做一些初始化之类的工作,当然,这些也很重要。

TinkerLoader#tryLoad方法

1
2
3
4
5
6
7
8
9
10
@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方法,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
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()) {
Log.w(TAG, "tryLoadPatchFiles:patch dir not exist:" + patchDirectoryPath);
ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_DIRECTORY_NOT_EXIST);
return;
}
//tinker/patch.info
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;
}
//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 (!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);
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
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;
}
}
//only work for art platform oat
boolean isSystemOTA = ShareTinkerInternals.isVmArt() && ShareTinkerInternals.isSystemOTA(patchInfo.fingerPrint);
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 (isSystemOTA
|| (mainProcess && versionChanged)) {
patchInfo.oldVersion = version;
//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;
}
}
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 (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) {
boolean loadTinkerResources = TinkerResourceLoader.loadTinkerResources(app, tinkerLoadVerifyFlag, patchVersionDirectory, resultIntent);
if (!loadTinkerResources) {
Log.w(TAG, "tryLoadPatchFiles:onPatchLoadResourcesFail");
return;
}
}
//all is ok!
ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_OK);
Log.i(TAG, "tryLoadPatchFiles: load end, ok!");
return;
}

这个方法的代码特别长,但是逻辑并不是特别复杂:

  • 首先获取patchDirectoryFile,为/data/user/0/packageName/tinker
  • 获取patchInfoFile、patchInfoLockFile等等
  • 获取旧版本号、新版本号等信息
  • 获取其他的一些信息
  • 进行一些so、dex、resource的check
  • 然后进行load操作
  • 将结果写入intent返回

TinkerManager的一些方法

主要是进行参数设置以及Tinker的install操作,Tinker的install操作,由TinkerInstaller的install方法完成。

TinkerApplication的其他方法

在这里的其他方法中,我们看到的代码几乎都是将这些操作,委托(代理)SampleApplicationLike中对应的方法来完成。

总结

过程如下,先loadTinker,在这里面会进行tryLoad操作,然后在创建代理对象,最后调用applicationLike的#onBaseContextAttached方法来初始化TinkerManager以及Tinker的安装。

最近访客