微信tinker导致冷启动变慢的问题优化
1. Android S用户反馈微信启动慢
首先第一个想到的就是dex的状态问题
- 是否有进行oat dex(例如bg dex或者其它类型dex),oat文件和art文件是否正常
- 是否未保护常用通讯类软件,用户感知度强的这类,一般都不建议随意回收,
- 老问题是否,存在tinker(微信热更新,在Google play是禁止这类行为的,一般出现在国内下载的app)
- 其它情况
2. 抓取微信systrace查看一下
=> 可以看到果然出现了tinker
OpenDexFilesFromOat(/data/user/0/com.tencent.mm/tinker/patch-66e50d2a/dex/tinker_classN.apk)

=> 查看一下/data/user/0/com.tencent.mm/tinker/目录,发现是有很多tinker的内容
$ adb shell ls -al /data/user/0/com.tencent.mm/tinker/
drwx------ 3 u0_a211 u0_a211 3452 2021-12-21 16:27 .
drwx------ 46 u0_a211 u0_a211 3452 2021-12-22 08:56 …
-rw------- 1 u0_a211 u0_a211 0 2021-12-22 08:57 info.lock
drwx------ 6 u0_a211 u0_a211 3452 2021-12-21 15:57 patch-66e50d2a
-rw-rw-rw- 1 u0_a211 u0_a211 359 2021-12-21 16:25 patch.info
-rw------- 1 u0_a211 u0_a211 42 2021-12-22 08:57 safemode_count_rec_com.tencent.mm
-rw------- 1 u0_a211 u0_a211 42 2021-12-21 17:32 safemode_count_rec_com.tencent.mm:appbrand0
-rw------- 1 u0_a211 u0_a211 42 2021-12-22 08:57 safemode_count_rec_com.tencent.mm:appbrand1
-rw------- 1 u0_a211 u0_a211 42 2021-12-21 16:26 safemode_count_rec_com.tencent.mm:cuploader
-rw------- 1 u0_a211 u0_a211 42 2021-12-22 08:56 safemode_count_rec_com.tencent.mm:push
-rw------- 1 u0_a211 u0_a211 42 2021-12-21 16:27 safemode_count_rec_com.tencent.mm:recovery
-rw------- 1 u0_a211 u0_a211 42 2021-12-21 17:32 safemode_count_rec_com.tencent.mm:sandbox
=> 里面的内容包含tinker_classN.apk还有odex/vdex/art/so等文件,目前tinker已经是微信优化过后的了,
不过由于文件比较大还是会导致,相当于加载2次dex文件,针对低配置的手机影响还是很容易看出来的。(相当于2次冷启动)
0 /data/user/0/com.tencent.mm/tinker/info.lock
8.4M /data/user/0/com.tencent.mm/tinker/patch-66e50d2a/patch-66e50d2a.apk
36K /data/user/0/com.tencent.mm/tinker/patch-66e50d2a/dex/oat/tinker_classN.apk.cur.prof
2.6M /data/user/0/com.tencent.mm/tinker/patch-66e50d2a/dex/oat/arm/tinker_classN.vdex
8.1M /data/user/0/com.tencent.mm/tinker/patch-66e50d2a/dex/oat/arm/tinker_classN.odex
2.8M /data/user/0/com.tencent.mm/tinker/patch-66e50d2a/dex/oat/arm/tinker_classN.art
14M /data/user/0/com.tencent.mm/tinker/patch-66e50d2a/dex/oat/arm
64K /data/user/0/com.tencent.mm/tinker/patch-66e50d2a/dex/oat/tinker_classN.apk.prof
14M /data/user/0/com.tencent.mm/tinker/patch-66e50d2a/dex/oat
132M /data/user/0/com.tencent.mm/tinker/patch-66e50d2a/dex/tinker_classN.apk
146M /data/user/0/com.tencent.mm/tinker/patch-66e50d2a/dex
3.5K /data/user/0/com.tencent.mm/tinker/patch-66e50d2a/odex
3.7M /data/user/0/com.tencent.mm/tinker/patch-66e50d2a/lib/lib/armeabi-v7a/libliteavsdk.so
9.4M /data/user/0/com.tencent.mm/tinker/patch-66e50d2a/lib/lib/armeabi-v7a/libapp.so
6.3M /data/user/0/com.tencent.mm/tinker/patch-66e50d2a/lib/lib/armeabi-v7a/libflutter.so
1.1M /data/user/0/com.tencent.mm/tinker/patch-66e50d2a/lib/lib/armeabi-v7a/libwechatlv.so
21M /data/user/0/com.tencent.mm/tinker/patch-66e50d2a/lib/lib/armeabi-v7a
21M /data/user/0/com.tencent.mm/tinker/patch-66e50d2a/lib/lib
21M /data/user/0/com.tencent.mm/tinker/patch-66e50d2a/lib
68M /data/user/0/com.tencent.mm/tinker/patch-66e50d2a/res/resources.apk
68M /data/user/0/com.tencent.mm/tinker/patch-66e50d2a/res
243M /data/user/0/com.tencent.mm/tinker/patch-66e50d2a
4.0K /data/user/0/com.tencent.mm/tinker/patch.info
4.0K /data/user/0/com.tencent.mm/tinker/safemode_count_rec_com.tencent.mm
4.0K /data/user/0/com.tencent.mm/tinker/safemode_count_rec_com.tencent.mm:push
4.0K /data/user/0/com.tencent.mm/tinker/safemode_count_rec_com.tencent.mm:appbrand1
4.0K /data/user/0/com.tencent.mm/tinker/safemode_count_rec_com.tencent.mm:appbrand0
4.0K /data/user/0/com.tencent.mm/tinker/safemode_count_rec_com.tencent.mm:sandbox
4.0K /data/user/0/com.tencent.mm/tinker/safemode_count_rec_com.tencent.mm:cuploader
4.0K /data/user/0/com.tencent.mm/tinker/safemode_count_rec_com.tencent.mm:recovery
243M /data/user/0/com.tencent.mm/tinker/
ps:
之前旧版本的tinker(2020年6月),tinker目录里面只有一个热更新的apk,会更加慢
$ tinker$ du -ah
5.1M ./patch-66490a13/patch-66490a13.apk
4.0K ./patch-66490a13/odex
3.9M ./patch-66490a13/lib/lib/armeabi-v7a/libmagicbrush.so
3.4M ./patch-66490a13/lib/lib/armeabi-v7a/libliteavsdk.so
476K ./patch-66490a13/lib/lib/armeabi-v7a/libwechatsight_v7a.so
11M ./patch-66490a13/lib/lib/armeabi-v7a/libapp.so
19M ./patch-66490a13/lib/lib/armeabi-v7a
19M ./patch-66490a13/lib/lib
19M ./patch-66490a13/lib
83M ./patch-66490a13/dex/tinker_classN.apk
44K ./patch-66490a13/dex/oat/tinker_classN.apk.cur.prof
48K ./patch-66490a13/dex/oat
83M ./patch-66490a13/dex
48M ./patch-66490a13/res/resources.apk
48M ./patch-66490a13/res
154M ./patch-66490a13
0 ./info.lock
4.0K ./patch.info
154M .
3. tinker对冷启动时间的影响
=> 带有tinker,验证一下wm_activity_launch_time这个时间,大概在4s左右
I wm_activity_launch_time: [0,263586177,com.tencent.mm/.app.WeChatSplashActivity,4071]
I wm_activity_launch_time: [0,225687646,com.tencent.mm/.app.WeChatSplashActivity,4032]
=> 手动删除整个tinker,验证时间明显减少,那么Android S上微信还是会导致启动时间变慢的问题
1317 1408 I wm_activity_launch_time: [0,244922921,com.tencent.mm/.app.WeChatSplashActivity,2158]
1317 1408 I wm_activity_launch_time: [0,216957026,com.tencent.mm/.app.WeChatSplashActivity,2108]
4. 修改方案
1、加载dex流程中阻断,如在systrace中的OpenDexFilesFromOat,如果不加载/data/user/0/com.tencent.mm/tinker/patch-66e50d2a/dex/tinker_classN.apk,
如果在art中修改
OatFileManager::OpenDexFilesFromOat或者更下面的ArtDexFileLoader::OpenZip/ArtDexFileLoader::OpenAllDexFilesFromZip都是可以阻断其打开流程
识别出tinker直接跳过,如下是在OpenAllDexFilesFromZip中跳过(这个方案只在Android S之前有效,Android S的正常android版本中art已经给mainline,使用的是gms里面的art)
//art/libdexfile/dex/art_dex_file_loader.cc
bool ArtDexFileLoader::OpenAllDexFilesFromZip(
const ZipArchive& zip_archive,
const std::string& location,
bool verify,
bool verify_checksum,
std::string* error_msg,
std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
ScopedTrace trace("Dex file open from Zip " + std::string(location));
//...
//识别location是否包含tinker
if (hasTinker) {
//包含则跳过
return false;
}
//...
}
2、那么Android S现在art修改方案无效,我们怎么做呢?
还是那句话:先调查清楚,再来动笔
5. Open Dex是什么时候触发的?其中传入的location又是那里来的?
1、从OatFileManager::OpenDexFilesFromOat往上找
//
art/runtime/native/dalvik_system_DexFile.cc
static jobject DexFile_openDexFileNative(JNIEnv* env,
jclass,
jstring javaSourceName,
jstring javaOutputName ATTRIBUTE_UNUSED,
jint flags ATTRIBUTE_UNUSED,
jobject class_loader,
jobjectArray dex_elements) {
ScopedUtfChars sourceName(env, javaSourceName);
if (sourceName.c_str() == nullptr) {
return nullptr;
}
std::vector<std::string> error_msgs;
const OatFile* oat_file = nullptr;
std::vector<std::unique_ptr<const DexFile>> dex_files =
Runtime::Current()->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str(),
class_loader,
dex_elements,
/*out*/ &oat_file,
/*out*/ &error_msgs);
return CreateCookieFromOatFileManagerResult(env, dex_files, oat_file, error_msgs);
}
//这是一个jni过来的方法
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(DexFile, openDexFileNative,
"(Ljava/lang/String;"
"Ljava/lang/String;"
"I"
"Ljava/lang/ClassLoader;"
"[Ldalvik/system/DexPathList$Element;"
")Ljava/lang/Object;"),
2、这里上一级目录在libcore中libcore/dalvik/src/main/java/dalvik/system/DexFile.java,
在创建DexFile对象的时候就会打开dex file
private static Object openDexFile(String sourceName, String outputName, int flags,
ClassLoader loader, DexPathList.Element[] elements) throws IOException {
// Use absolute paths to enable the use of relative paths when testing on host.
return openDexFileNative(new File(sourceName).getAbsolutePath(),
(outputName == null)
? null
: new File(outputName).getAbsolutePath(),
flags,
loader,
elements);
}
private DexFile(String sourceName, String outputName, int flags, ClassLoader loader,
DexPathList.Element[] elements) throws IOException {
//...
mCookie = openDexFile(sourceName, outputName, flags, loader, elements);
mInternalCookie = mCookie;
mFileName = sourceName;
//System.out.println("DEX FILE cookie is " + mCookie + " sourceName=" + sourceName + " outputName=" + outputName);
}
3、搜索new DexFile,只有DexPathList.java、DexFile.java才new了DexFile对象
libcore/dalvik$ grep -rn “new DexFile” .
./src/main/java/dalvik/system/DexPathList.java:268: DexFile dex = new DexFile(dexFiles, definingContext, null_elements);
./src/main/java/dalvik/system/DexPathList.java:347: DexFile dex = new DexFile(new ByteBuffer[] { buf }, /* classLoader */ null,
./src/main/java/dalvik/system/DexPathList.java:442: return new DexFile(file, loader, elements);
./src/main/java/dalvik/system/DexFile.java:216: return new DexFile(sourcePathName, outputPathName, flags, loader, elements);
DexPathList(ClassLoader definingContext, String dexPath,
String librarySearchPath, File optimizedDirectory, boolean isTrusted) {
//...
// save dexPath for BaseDexClassLoader
this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
suppressedExceptions, definingContext, isTrusted);
//...
}
private static Element[] makeDexElements(List<File> files, File optimizedDirectory,
List<IOException> suppressedExceptions, ClassLoader loader, boolean isTrusted) {
//...
dex = loadDexFile(file, optimizedDirectory, loader, elements);
//...
}
private static DexFile loadDexFile(File file, File optimizedDirectory, ClassLoader loader,
Element[] elements)
throws IOException {
if (optimizedDirectory == null) {
//初始化时这个是null
return new DexFile(file, loader, elements);
} else {
String optimizedPath = optimizedPathFor(file, optimizedDirectory);
return DexFile.loadDex(file.getPath(), optimizedPath, 0, loader, elements);//打开微信***.apk走的是这里
}
}
4、往上找关联流程
这里就直接找到LoadedApk.java,这里是App加载apk的地方,流程从这里往第3点找
//frameworks/base/core/java/android/app/LoadedApk.java
public ClassLoader getClassLoader() {
synchronized (mLock) {
if (mClassLoader == null) {
createOrUpdateClassLoaderLocked(null /*addedPaths*/);
}
return mClassLoader;
}
}
private void createOrUpdateClassLoaderLocked(List<String> addedPaths) {
//..
//mApplicationInfo.sourceDir就是/data/app/***/com.tencent.mm***/base.apk
makePaths(mActivityThread, isBundledApp, mApplicationInfo, zipPaths, libPaths);
//...
final String zip = (zipPaths.size() == 1) ? zipPaths.get(0) :
TextUtils.

本文分析了微信在AndroidS系统上因Tinker(热更新模块)导致的冷启动变慢问题。通过systrace工具,发现Tinker在冷启动过程中加载额外的dex文件,增加了启动时间。解决方案包括在加载流程中阻断Tinker的dex加载,或在系统层面修改,但AndroidS由于使用了不同的art实现,需要更复杂的规避策略。此外,文中详细梳理了ClassLoader的创建过程,揭示了Tinker如何通过反射注入到系统中。最后,提出了禁止Tinker下载或删除patch.info文件等优化方案。
最低0.47元/天 解锁文章
439

被折叠的 条评论
为什么被折叠?



