Skip to content

Commit fac7c90

Browse files
committed
Revert "[tinker] Reduce memory cost by resources.apk when patch is loaded."
This reverts commit a9e8f11.
1 parent 1162871 commit fac7c90

File tree

3 files changed

+60
-113
lines changed

3 files changed

+60
-113
lines changed

tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/PatchedResourcesInsuranceLogic.java

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.tencent.tinker.loader;
22

3-
import android.app.Application;
43
import android.content.Context;
54
import android.content.pm.ApplicationInfo;
65
import android.os.Handler;
@@ -33,10 +32,10 @@ public final class PatchedResourcesInsuranceLogic {
3332

3433
private static long sStoredPatchedResModifiedTime = 0L;
3534

36-
public static boolean install(Application application, String newResourcesApkPath) {
35+
public static boolean install(Context context, String newResourcesApkPath) {
3736
try {
3837
ShareTinkerLog.i(TAG, "install called.");
39-
interceptHandler(application, fetchMHObject(application), newResourcesApkPath);
38+
interceptHandler(context, fetchMHObject(context), newResourcesApkPath);
4039
ShareTinkerLog.i(TAG, "install done.");
4140
return true;
4241
} catch (Throwable e) {
@@ -77,11 +76,11 @@ private static Handler fetchMHObject(Context context) throws Exception {
7776
return (Handler) mHField.get(activityThread);
7877
}
7978

80-
private static void interceptHandler(Application application, Handler mH, String newResourcesApkPath) throws Exception {
79+
private static void interceptHandler(Context context, Handler mH, String newResourcesApkPath) throws Exception {
8180
final Field mCallbackField = ShareReflectUtil.findField(Handler.class, "mCallback");
8281
final Handler.Callback originCallback = (Handler.Callback) mCallbackField.get(mH);
8382
if (!(originCallback instanceof HackerCallback)) {
84-
HackerCallback hackerCallback = new HackerCallback(application, originCallback, mH.getClass(), newResourcesApkPath);
83+
HackerCallback hackerCallback = new HackerCallback(context, originCallback, mH.getClass(), newResourcesApkPath);
8584
mCallbackField.set(mH, hackerCallback);
8685
} else {
8786
ShareTinkerLog.w(TAG, "Already intercepted, skip rest logic.");
@@ -91,31 +90,23 @@ private static void interceptHandler(Application application, Handler mH, String
9190
private static class HackerCallback implements Handler.Callback {
9291
private final int LAUNCH_ACTIVITY;
9392
private final int RELAUNCH_ACTIVITY;
94-
private final int RECEIVER;
95-
private final int CREATE_SERVICE;
96-
private final int SERVICE_ARGS;
97-
private final int BIND_SERVICE;
98-
private final int INSTALL_PROVIDER;
9993
private final int EXECUTE_TRANSACTION;
10094

101-
private final Application mApplication;
95+
private final Context mContext;
96+
10297
private Method mGetCallbacksMethod = null;
98+
10399
private boolean mSkipInterceptExecuteTransaction = false;
104100
private final Handler.Callback mOriginalCallback;
101+
105102
private final String mNewResourcesApkPath;
106-
private boolean mAvoidInfiniteLoop = false;
107103

108-
HackerCallback(Application application, Handler.Callback originalCallback, Class<?> mhClazz, String newResourcesApkPath) {
109-
mApplication = application;
104+
HackerCallback(Context context, Handler.Callback originalCallback, Class<?> mhClazz, String newResourcesApkPath) {
105+
mContext = context;
110106
mOriginalCallback = originalCallback;
111107
mNewResourcesApkPath = newResourcesApkPath;
112108
LAUNCH_ACTIVITY = fetchMessageId(mhClazz, "LAUNCH_ACTIVITY", 100);
113109
RELAUNCH_ACTIVITY = fetchMessageId(mhClazz, "RELAUNCH_ACTIVITY", 126);
114-
RECEIVER = fetchMessageId(mhClazz, "RECEIVER", 113);
115-
CREATE_SERVICE = fetchMessageId(mhClazz, "CREATE_SERVICE", 114);
116-
SERVICE_ARGS = fetchMessageId(mhClazz, "SERVICE_ARGS", 115);
117-
BIND_SERVICE = fetchMessageId(mhClazz, "BIND_SERVICE", 121);
118-
INSTALL_PROVIDER = fetchMessageId(mhClazz, "INSTALL_PROVIDER", 145);
119110
if (ShareTinkerInternals.isNewerOrEqualThanVersion(28, true)) {
120111
EXECUTE_TRANSACTION = fetchMessageId(mhClazz, "EXECUTE_TRANSACTION ", 159);
121112
} else {
@@ -135,25 +126,19 @@ private int fetchMessageId(Class<?> hClazz, String name, int defVal) {
135126

136127
@Override
137128
public boolean handleMessage(Message msg) {
138-
if (mAvoidInfiniteLoop) {
139-
ShareTinkerLog.w(TAG, "found a loop invocation to handleMessage.");
140-
return false;
141-
}
142129
boolean consume = false;
143130
if (hackMessage(msg)) {
144131
consume = true;
145132
} else if (mOriginalCallback != null) {
146-
mAvoidInfiniteLoop = true;
147133
consume = mOriginalCallback.handleMessage(msg);
148-
mAvoidInfiniteLoop = false;
149134
}
150135
return consume;
151136
}
152137

153138
@SuppressWarnings("unchecked")
154139
private boolean hackMessage(Message msg) {
155140
if (msg.obj instanceof ApplicationInfo) {
156-
ShareTinkerLog.w(TAG, "intercepted APPLICATION_INFO_CHANGED, update sourceDir and " +
141+
ShareTinkerLog.w(TAG, "Intercepted APPLICATION_INFO_CHANGED, update sourceDir and " +
157142
"publicSourceDir before dispatching back to system.");
158143
final ApplicationInfo appInfo = ((ApplicationInfo) msg.obj);
159144
appInfo.sourceDir = appInfo.publicSourceDir = mNewResourcesApkPath;
@@ -162,11 +147,6 @@ private boolean hackMessage(Message msg) {
162147

163148
if (msg.what == LAUNCH_ACTIVITY ||
164149
msg.what == RELAUNCH_ACTIVITY ||
165-
msg.what == RECEIVER ||
166-
msg.what == CREATE_SERVICE ||
167-
msg.what == SERVICE_ARGS ||
168-
msg.what == BIND_SERVICE ||
169-
msg.what == INSTALL_PROVIDER ||
170150
(EXECUTE_TRANSACTION != -1 && msg.what == EXECUTE_TRANSACTION)
171151
) {
172152
if (!isPatchedResModifiedAfterLastLoad(mNewResourcesApkPath)) {
@@ -207,13 +187,11 @@ private boolean hackMessage(Message msg) {
207187
} catch (Throwable thr) {
208188
ShareTinkerLog.printErrStackTrace(TAG, thr, "fail to call getLifecycleStateRequest " +
209189
"method, skip rest insurance logic.");
210-
return false;
211190
}
212191
}
213192

214193
try {
215-
ShareTinkerLog.i(TAG, "re-inject patched resources since its backed APK file was updated.");
216-
TinkerResourcePatcher.monkeyPatchExistingResources(mApplication, mNewResourcesApkPath, true);
194+
TinkerResourcePatcher.monkeyPatchExistingResources(mContext, mNewResourcesApkPath, true);
217195
} catch (Throwable thr) {
218196
ShareTinkerLog.printErrStackTrace(TAG, thr, "fail to ensure patched resources available " +
219197
"after it's modified.");

tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/SystemClassLoaderAdder.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@ public class SystemClassLoaderAdder {
5151
private static final String TAG = "Tinker.ClassLoaderAdder";
5252
private static int sPatchDexCount = 0;
5353

54-
private static ClassLoader sInjectedClassLoader = null;
55-
5654
public static void installDexes(Application application, ClassLoader loader, File dexOptDir, List<File> files,
5755
boolean isProtectedApp, boolean useDLC) throws Throwable {
5856
ShareTinkerLog.i(TAG, "installDexes dexOptDir: " + dexOptDir.getAbsolutePath() + ", dex size:" + files.size());
@@ -65,7 +63,6 @@ public static void installDexes(Application application, ClassLoader loader, Fil
6563
} else {
6664
injectDexesInternal(classLoader, files, dexOptDir);
6765
}
68-
sInjectedClassLoader = classLoader;
6966
//install done
7067
sPatchDexCount = files.size();
7168
ShareTinkerLog.i(TAG, "after loaded classloader: " + classLoader + ", dex size:" + sPatchDexCount);
@@ -78,13 +75,6 @@ public static void installDexes(Application application, ClassLoader loader, Fil
7875
}
7976
}
8077

81-
static ClassLoader getInjectedClassLoader() {
82-
if (sInjectedClassLoader == null) {
83-
throw new TinkerRuntimeException("Please call installDexes() first.");
84-
}
85-
return sInjectedClassLoader;
86-
}
87-
8878
static void injectDexesInternal(ClassLoader cl, List<File> dexFiles, File optimizeDir) throws Throwable {
8979
if (Build.VERSION.SDK_INT >= 23) {
9080
V23.install(cl, dexFiles, optimizeDir);

tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/TinkerResourcePatcher.java

Lines changed: 48 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import static com.tencent.tinker.loader.shareutil.ShareReflectUtil.findField;
2323
import static com.tencent.tinker.loader.shareutil.ShareReflectUtil.findMethod;
2424

25-
import android.app.Application;
2625
import android.content.Context;
2726
import android.content.pm.ApplicationInfo;
2827
import android.content.res.AssetManager;
@@ -57,6 +56,7 @@ class TinkerResourcePatcher {
5756

5857
private static Map<Object, WeakReference<Object>> resourceImpls = null;
5958
private static Object currentActivityThread = null;
59+
private static AssetManager newAssetManager = null;
6060

6161
// method
6262
private static Constructor<?> newAssetManagerCtor = null;
@@ -66,11 +66,8 @@ class TinkerResourcePatcher {
6666

6767
// field
6868
private static Field assetsFiled = null;
69-
private static Field resourcesImplField = null;
69+
private static Field resourcesImplFiled = null;
7070
private static Field resDir = null;
71-
private static Field resources = null;
72-
private static Field application = null;
73-
private static Field classloader = null;
7471
private static Field packagesFiled = null;
7572
private static Field resourcePackagesFiled = null;
7673
private static Field publicSourceDirField = null;
@@ -101,9 +98,6 @@ public static void isResourceCanPatch(Context context) throws Throwable {
10198
}
10299

103100
resDir = findField(loadedApkClass, "mResDir");
104-
resources = findField(loadedApkClass, "mResources");
105-
application = findField(loadedApkClass, "mApplication");
106-
classloader = findField(loadedApkClass, "mClassLoader");
107101
packagesFiled = findField(activityThread, "mPackages");
108102
try {
109103
resourcePackagesFiled = findField(activityThread, "mResourcePackages");
@@ -175,7 +169,7 @@ public static void isResourceCanPatch(Context context) throws Throwable {
175169
if (SDK_INT >= 24) {
176170
try {
177171
// N moved the mAssets inside an mResourcesImpl field
178-
resourcesImplField = findField(resources, "mResourcesImpl");
172+
resourcesImplFiled = findField(resources, "mResourcesImpl");
179173
} catch (Throwable ignore) {
180174
// for safety
181175
assetsFiled = findField(resources, "mAssets");
@@ -192,32 +186,21 @@ public static void isResourceCanPatch(Context context) throws Throwable {
192186
}
193187

194188
/**
195-
* @param app
189+
* @param context
196190
* @param externalResourceFile
197191
* @throws Throwable
198192
*/
199193
@SuppressWarnings({"ConstantConditions", "unchecked"})
200-
public static void monkeyPatchExistingResources(Application app, String externalResourceFile, boolean isReInject) throws Throwable {
194+
public static void monkeyPatchExistingResources(Context context, String externalResourceFile, boolean isReInject) throws Throwable {
201195
if (externalResourceFile == null) {
202196
return;
203197
}
204198

205-
final ApplicationInfo appInfo = app.getApplicationInfo();
199+
final ApplicationInfo appInfo = context.getApplicationInfo();
206200

207-
// Ensure mPackages and mResourcePackages cache contains LoadedApk instance related to this app.
208-
packageContext = app.createPackageContext(app.getPackageName(), Context.CONTEXT_INCLUDE_CODE);
209-
packageResContext = app.createPackageContext(app.getPackageName(), 0);
210-
211-
final Resources oldResources = packageResContext.getResources();
212-
Field implAssetsField = null;
213-
Object appAssetManager = null;
214-
if (resourcesImplField != null) {
215-
final Object appResourcesImpl = resourcesImplField.get(oldResources);
216-
implAssetsField = findField(appResourcesImpl, "mAssets");
217-
appAssetManager = implAssetsField.get(appResourcesImpl);
218-
} else {
219-
appAssetManager = assetsFiled.get(oldResources);
220-
}
201+
// Prevent cached LoadedApk being recycled.
202+
packageContext = context.createPackageContext(context.getPackageName(), Context.CONTEXT_INCLUDE_CODE);
203+
packageResContext = context.createPackageContext(context.getPackageName(), 0);
221204

222205
final Field[] packagesFields = new Field[]{packagesFiled, resourcePackagesFiled};
223206
for (Field field : packagesFields) {
@@ -235,29 +218,35 @@ public static void monkeyPatchExistingResources(Application app, String external
235218
final String resDirPath = (String) resDir.get(loadedApk);
236219
if (appInfo.sourceDir.equals(resDirPath)) {
237220
resDir.set(loadedApk, externalResourceFile);
238-
if (isReInject) {
239-
application.set(loadedApk, app);
240-
classloader.set(loadedApk, SystemClassLoaderAdder.getInjectedClassLoader());
241-
}
242-
resources.set(loadedApk, null);
243221
}
244222
}
245223
}
246224

247-
// Let modified resDir take effect and use it to create new Context instance.
248-
packageContext = app.createPackageContext(app.getPackageName(), Context.CONTEXT_INCLUDE_CODE);
249-
packageResContext = app.createPackageContext(app.getPackageName(), 0);
225+
if (isReInject) {
226+
ShareTinkerLog.i(TAG, "Re-injecting, skip rest logic.");
227+
PatchedResourcesInsuranceLogic.recordCurrentPatchedResModifiedTime(externalResourceFile);
228+
return;
229+
}
230+
231+
newAssetManager = (AssetManager) newAssetManagerCtor.newInstance();
232+
// Create a new AssetManager instance and point it to the resources installed under
233+
if (((Integer) addAssetPathMethod.invoke(newAssetManager, externalResourceFile)) == 0) {
234+
throw new IllegalStateException("Could not create new AssetManager");
235+
}
250236
PatchedResourcesInsuranceLogic.recordCurrentPatchedResModifiedTime(externalResourceFile);
251237

252-
final Resources newResources = packageResContext.getResources();
253-
Object newAssetManager = null;
254-
if (resourcesImplField != null) {
255-
// N
256-
final Object resourceImpl = resourcesImplField.get(newResources);
257-
newAssetManager = implAssetsField.get(resourceImpl);
258-
} else {
259-
//pre-N
260-
newAssetManager = assetsFiled.get(newResources);
238+
// Add SharedLibraries to AssetManager for resolve system resources not found issue
239+
// This influence SharedLibrary Package ID
240+
if (shouldAddSharedLibraryAssets(appInfo)) {
241+
for (String sharedLibrary : appInfo.sharedLibraryFiles) {
242+
if (!sharedLibrary.endsWith(".apk")) {
243+
continue;
244+
}
245+
if (((Integer) addAssetPathAsSharedLibraryMethod.invoke(newAssetManager, sharedLibrary)) == 0) {
246+
throw new IllegalStateException("AssetManager add SharedLibrary Fail");
247+
}
248+
ShareTinkerLog.i(TAG, "addAssetPathAsSharedLibrary " + sharedLibrary);
249+
}
261250
}
262251

263252
// Kitkat needs this method call, Lollipop doesn't. However, it doesn't seem to cause any harm
@@ -273,65 +262,55 @@ public static void monkeyPatchExistingResources(Application app, String external
273262
continue;
274263
}
275264
// Set the AssetManager of the Resources instance to our brand new one
276-
if (resourcesImplField != null) {
277-
// N
278-
final Object resourcesImpl = resourcesImplField.get(resources);
279-
final Object assetManager = implAssetsField.get(resourcesImpl);
280-
if (assetManager == appAssetManager) {
281-
implAssetsField.set(resourcesImpl, newAssetManager);
282-
}
283-
} else {
265+
try {
284266
//pre-N
285-
final Object assetManager = assetsFiled.get(resources);
286-
if (assetManager == appAssetManager) {
287-
assetsFiled.set(resources, newAssetManager);
288-
}
267+
assetsFiled.set(resources, newAssetManager);
268+
} catch (Throwable ignore) {
269+
// N
270+
final Object resourceImpl = resourcesImplFiled.get(resources);
271+
// for Huawei HwResourcesImpl
272+
final Field implAssets = findField(resourceImpl, "mAssets");
273+
implAssets.set(resourceImpl, newAssetManager);
289274
}
290275

291276
clearPreloadTypedArrayIssue(resources);
277+
292278
resources.updateConfiguration(resources.getConfiguration(), resources.getDisplayMetrics());
293279
}
294280

295281
try {
296282
if (resourceImpls != null) {
297283
for (WeakReference<Object> wr : resourceImpls.values()) {
298-
final Object resourcesImpl = wr.get();
299-
if (resourcesImpl != null) {
300-
final Object assetManager = implAssetsField.get(resourcesImpl);
301-
if (assetManager == appAssetManager) {
302-
implAssetsField.set(resourcesImpl, newAssetManager);
303-
}
284+
final Object resourceImpl = wr.get();
285+
if (resourceImpl != null) {
286+
final Field implAssets = findField(resourceImpl, "mAssets");
287+
implAssets.set(resourceImpl, newAssetManager);
304288
}
305289
}
306290
}
307291
} catch (Throwable ignored) {
308292
// Ignored.
309293
}
310294

311-
if (isReInject) {
312-
ShareTinkerLog.i(TAG, "Re-injecting, skip rest logic.");
313-
return;
314-
}
315-
316295
// Handle issues caused by WebView on Android N.
317296
// Issue: On Android N, if an activity contains a webview, when screen rotates
318297
// our resource patch may lost effects.
319298
// for 5.x/6.x, we found Couldn't expand RemoteView for StatusBarNotification Exception
320299
if (Build.VERSION.SDK_INT >= 24) {
321300
try {
322301
if (publicSourceDirField != null) {
323-
publicSourceDirField.set(app.getApplicationInfo(), externalResourceFile);
302+
publicSourceDirField.set(context.getApplicationInfo(), externalResourceFile);
324303
}
325304
} catch (Throwable ignore) {
326305
// Ignored.
327306
}
328307
}
329308

330-
if (!checkResUpdate(app)) {
309+
if (!checkResUpdate(context)) {
331310
throw new TinkerRuntimeException(ShareConstants.CHECK_RES_INSTALL_FAIL);
332311
}
333312

334-
if (!PatchedResourcesInsuranceLogic.install(app, externalResourceFile)) {
313+
if (!PatchedResourcesInsuranceLogic.install(context, externalResourceFile)) {
335314
ShareTinkerLog.w(TAG, "tryLoadPatchFiles:PatchedResourcesInsuranceLogic install fail.");
336315
throw new TinkerRuntimeException("fail to install PatchedResourcesInsuranceLogic.");
337316
}

0 commit comments

Comments
 (0)