Android 主题切换/换肤方案 研究(四)
来源:互联网 发布:软件安全性 编辑:程序博客网 时间:2024/05/23 20:48
4. qq和qq空间 (独立app)
分析时用的是:
1. 夜神android模拟器(因为用android studio自带的模拟器运行x86架构的镜像提示不能安装qq空间,安装arm架构的镜像运行又极度慢)
使用过程中遇到adb找不到夜神模拟器的问题, 移步这里 adb devices检测不到模拟器 解决。
2. QQ空间版本7.4.1
3. QQ版本 7.1.8
QQ空间版本7.4.1 和 QQ版本 7.1.8 都是反编译分析时也就是写这篇文章时的最新版本。
qq空间的主题换肤在:我的空间 -> 个性化 -> 原创主题
qq的主题换肤在:左边侧滑栏 -> 个性装扮 -> 主题
下面开始分析。
用smali2java反编译qq空间.apk,有很大一部分代码反编译不出来。
用apktool反编译qq空间.apk, 会报错:
>java -jar apktool_2.2.4.jar d qq_zone.apk -o qq_zoneI: Using Apktool 2.2.4 on qq_zone.apkI: Loading resource table...Exception in thread "main" brut.androlib.AndrolibException: Could not decode arsc file at brut.androlib.res.decoder.ARSCDecoder.decode(ARSCDecoder.java:52) at brut.androlib.res.AndrolibResources.getResPackagesFromApk(AndrolibResources.java:562) at brut.androlib.res.AndrolibResources.loadMainPkg(AndrolibResources.java:72) at brut.androlib.res.AndrolibResources.getResTable(AndrolibResources.java:64) at brut.androlib.Androlib.getResTable(Androlib.java:68) at brut.androlib.ApkDecoder.setTargetSdkVersion(ApkDecoder.java:207) at brut.androlib.ApkDecoder.decode(ApkDecoder.java:109) at brut.apktool.Main.cmdDecode(Main.java:166) at brut.apktool.Main.main(Main.java:80)Caused by: java.io.IOException: Expected: 0x00000008, got: 0x00000003 at brut.util.ExtDataInput.skipCheckShort(ExtDataInput.java:56) at brut.androlib.res.decoder.ARSCDecoder.readValue(ARSCDecoder.java:315) at brut.androlib.res.decoder.ARSCDecoder.readEntry(ARSCDecoder.java:247) at brut.androlib.res.decoder.ARSCDecoder.readTableType(ARSCDecoder.java:226) at brut.androlib.res.decoder.ARSCDecoder.readTableTypeSpec(ARSCDecoder.java:156) at brut.androlib.res.decoder.ARSCDecoder.readTablePackage(ARSCDecoder.java:118) at brut.androlib.res.decoder.ARSCDecoder.readTableHeader(ARSCDecoder.java:80) at brut.androlib.res.decoder.ARSCDecoder.decode(ARSCDecoder.java:47) ... 8 more
因此就看不了qq空间的资源文件了,目前还没有解决这个问题。
然后来看下qq的。
用smali2java反编译查看qq源码,发现也是有很大一部分代码反编译不出来。
只好去看看能不能从资源文件入手吗,用apktool反编译qq.apk文件,查看资源文件:
res\layout下的布局文件:
res\values\colors.xml文件:
<?xml version="1.0" encoding="utf-8"?><resources> <color name="name">#00ffffff</color> <color name="boss_unipay_c_tx">#ff000000</color> <color name="name_APKTOOL_DUPLICATENAME_0x7f0c0002">#ffa6a6a6</color> <color name="name_APKTOOL_DUPLICATENAME_0x7f0c0003">#ffc8c7cc</color> <color name="name_APKTOOL_DUPLICATENAME_0x7f0c0004">#ff808080</color> <color name="name_APKTOOL_DUPLICATENAME_0x7f0c0005">#ffb2b2b2</color> <color name="name_APKTOOL_DUPLICATENAME_0x7f0c0006">#ff333333</color> <color name="name_APKTOOL_DUPLICATENAME_0x7f0c0007">#ffc8c8c8</color> <color name="name_APKTOOL_DUPLICATENAME_0x7f0c0008">#ffffffff</color> <color name="name_APKTOOL_DUPLICATENAME_0x7f0c0009">#ffff6600</color> <color name="name_APKTOOL_DUPLICATENAME_0x7f0c000a">#ffff3b30</color> <color name="name_APKTOOL_DUPLICATENAME_0x7f0c000b">#ff43c959</color> <color name="boss_unipay_c_blue">#ff00a5e0</color> <color name="name_APKTOOL_DUPLICATENAME_0x7f0c000d">#ff0079ff</color> <color name="name_APKTOOL_DUPLICATENAME_0x7f0c000e">#8000a5e0</color> <color name="name_APKTOOL_DUPLICATENAME_0x7f0c000f">#90000000</color> <color name="name_APKTOOL_DUPLICATENAME_0x7f0c0010">#ffe6e6e6</color> <color name="name_APKTOOL_DUPLICATENAME_0x7f0c0011">#ffffffff</color> <color name="name_APKTOOL_DUPLICATENAME_0x7f0c0012">#ff333333</color>
res\values\dimens.xml文件:
<?xml version="1.0" encoding="utf-8"?><resources> <dimen name="name">1.0px</dimen> <dimen name="name_APKTOOL_DUPLICATENAME_0x7f0d0001">45.0sp</dimen> <dimen name="name_APKTOOL_DUPLICATENAME_0x7f0d0002">19.0sp</dimen> <dimen name="name_APKTOOL_DUPLICATENAME_0x7f0d0003">18.0sp</dimen> <dimen name="name_APKTOOL_DUPLICATENAME_0x7f0d0004">15.0sp</dimen> <dimen name="name_APKTOOL_DUPLICATENAME_0x7f0d0005">12.0sp</dimen> <dimen name="name_APKTOOL_DUPLICATENAME_0x7f0d0006">13.0sp</dimen> <dimen name="name_APKTOOL_DUPLICATENAME_0x7f0d0007">14.0sp</dimen> <dimen name="name_APKTOOL_DUPLICATENAME_0x7f0d0008">11.0sp</dimen> <dimen name="name_APKTOOL_DUPLICATENAME_0x7f0d0009">52.0dip</dimen> <dimen name="name_APKTOOL_DUPLICATENAME_0x7f0d000a">34.0dip</dimen> <dimen name="name_APKTOOL_DUPLICATENAME_0x7f0d000b">15.0sp</dimen> <dimen name="name_APKTOOL_DUPLICATENAME_0x7f0d000c">50.0dip</dimen>
res\values\strings.xml文件:
<?xml version="1.0" encoding="utf-8"?><resources> <string name="name">BitApp消息</string> <string name="name_APKTOOL_DUPLICATENAME_0x7f0b0001">http://combo.b.qq.com/crm3mobile/src/zip/</string> <string name="name_APKTOOL_DUPLICATENAME_0x7f0b0002">消息显示失败</string> <string name="name_APKTOOL_DUPLICATENAME_0x7f0b0003">[动态消息]</string> <string name="name_APKTOOL_DUPLICATENAME_0x7f0b0004">请选择邮件客户端:</string> <string name="name_APKTOOL_DUPLICATENAME_0x7f0b0005">你的手机尚未安装邮件应用,请安装邮件应用后再试</string> <string name="name_APKTOOL_DUPLICATENAME_0x7f0b0006">添加失败</string> <string name="name_APKTOOL_DUPLICATENAME_0x7f0b0007">已更新</string> <string name="name_APKTOOL_DUPLICATENAME_0x7f0b0008">保存到电话簿</string> <string name="name_APKTOOL_DUPLICATENAME_0x7f0b0009">QQ公众号</string> <string name="name_APKTOOL_DUPLICATENAME_0x7f0b000a">微信公众号</string> <string name="name_APKTOOL_DUPLICATENAME_0x7f0b000b">重绑</string> <string name="name_APKTOOL_DUPLICATENAME_0x7f0b000c">使用微信扫二维码关注微信公众号</string> <string name="name_APKTOOL_DUPLICATENAME_0x7f0b000d">免费电话</string> <string name="name_APKTOOL_DUPLICATENAME_0x7f0b000e">企业免费电话</string> <string name="name_APKTOOL_DUPLICATENAME_0x7f0b000f">企业接待信息有误</string> <string name="name_APKTOOL_DUPLICATENAME_0x7f0b0010">网络超时</string>
发现这些资源文件的名称都反编译不出来, 看来QQ的混淆程度做的很深。
于是看看能不能从其他方面入手。
我们使用qq空间或者qq的时候注意到,使用主题前必须要先下载皮肤包,下载下来的皮肤包肯定要存储,那么存储在哪?
只可能在两个地方:
1. SD卡
2. 应用程序的目录
下面我们先来看下应用程序的目录,试试看能不能找到有用的信息。
第一步:得到qq空间包名
打开qq空间app,执行命令:adb shell dumpsys activity top 或者执行: adb shell dumpsys activity activities 也可以
这里执行的是:adb shell dumpsys activity activities
得到qq空间包名是:com.qzone
第二步:进入qq空间的应用程序目录,查看资源
进入/data/data目录,执行ls -l 命令列出当前系统所有已安装的包名,
可以看到的确有qq空间:
倒数第四个就是。 进入com.qzone目录。
再次执行ls -l,
进入shared_prefs目录:
现在还没看到什么关键信息,别急,现在进入我的空间 -> 个性化 -> 原创主题, 到主题下载页面,下载并应用一个主题,然后再执行ls -l
找到了theme.xml, 就在倒数第三个。
查看theme.xml文件内容:
找到了皮肤文件存放的目录:
/storage/emulated/0/Android/data/com.qzone/files/cache/qz_external_resource/theme_res/1/res/
从路径中也可以看出qq空间下载的皮肤包是存储在sd卡里的。
然后我又下载并应用另外一个新的皮肤,再来查看该文件内容:
看到路径中数字由1变为了103, 其它均没变,说明不同皮肤包存储路径不一样。
本来想着进入/data/data/com.qzone/files/cache/qz_external_resource/theme_res/103目录查看相关资源,可是发现/data/data/com.qzone/files/cache/目录下没有qz_external_resource目录,这也不难理解,因为/data/data/是应用程序的目录,是在内置存储器上的,而不是sd卡里的目录, 思考了一段时间,执行下列操作:
1. 把当前路径回退到根目录,
2. 执行: find . -name qz_external_resource 即在在根目录下搜索qz_external_resource
终于找到qz_external_resource在哪了:
执行:cd /mnt/shell/emulated/0/Android/data/com.qzone/files/cache/qz_external_resource/theme_res 进入到theme_res目录
再执行:ls -l
看到了 1 目录和103目录,分别是两个皮肤包的存储目录,进入1目录:
终于找到了皮肤包文件! 做过apk解压的一看就知道这是.apk文件解压缩之后得到的文件
到了这里就可以知道qq空间的换肤方案采用的就是动态加载,下载的皮肤包就是一个apk文件,然后动态加载apk,替换资源文件。
再来看看qq的,分析方法同qq空间一样,
打开qq,
执行: adb shell dumpsys activity activities
可以看到qq的包名是:com.tencent.mobileqq
果然在qq的程序目录data/data/com.tencent.mobileqq下找到了theme.xml文件 ,里面的内容和qq空间的几乎一样:
根据路径知道,和qq空间不同的是,qq的皮肤包是放在应用程序目录下,而不是放在SD卡中。
到现在已经可以确定qq和qq空间实现换肤的大体方案:采用动态加载皮肤包apk的方式来实现换肤。
那么具体是采用哪种动态加载的策略呢,接下来就要探讨这个问题。
下面是用 jadx 来反编译:
来看qq的:
展开com.tencent.theme目录,看到有SkinEngine类:
再来看下qq空间反编译后的:
展开com.tencent.theme目录,看到有SkinEngine类:
由以上分析,QQ和QQ空间的换肤用的都是SkinEngine这个关键的类来实现的。
查看QQ和QQ空间的SkinEngine的源码,发现是差不多的,选一个来分析即可,下面就来分析QQ这个关键的SkinEngine类。
虽然很多代码反编译不出来,但是关键类SkinEngine的很多代码还是可以反编译出来的,来看一些关键的代码:
public static void init(Context context, int[] iArr, Class cls, int i, File file) throws UnSupportPlatformException { SkinEngine instances = getInstances(); Resources resources = context.getResources(); instances.mResources = resources; isSupportPlatform(context, resources); instances.u = a(instances.mResources); context.getApplicationContext().registerReceiver(instances.l, new IntentFilter(ACTION_THEME_UPDATE), "com.tencent.msg.permission.pushnotify", null); instances.a(resources, null, iArr, 0, cls, i, file); SharedPreferences sharedPreferences = context.getSharedPreferences(PREFERENCE_NAME, 4); instances.s = sharedPreferences.getString(KEY_THEME, null); instances.o = sharedPreferences.getBoolean(KEY_RESOURCES_IS_COMPLIED, true); }
该方法会调用a()方法,a()方法是反编译失败的,所以很难理清该方法的逻辑流程,但是根据这种代码来推测、分析出逻辑流程才真正有挑战啊,来看下:
/* JADX WARNING: inconsistent code. */ /* Code decompiled incorrectly, please refer to instructions dump. */ private void a(android.content.res.Resources r16, java.lang.Class r17, int[] r18, int r19, java.lang.Class r20, int r21, java.io.File r22) throws com.tencent.theme.UnSupportPlatformException { /* r15 = this; r2 = 0; if (r18 != 0) goto L_0x0007; L_0x0003: if (r17 == 0) goto L_0x0060; L_0x0005: if (r19 == 0) goto L_0x0060; L_0x0007: r1 = b; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } if (r1 == 0) goto L_0x018b; L_0x000b: r1 = android.content.res.Resources.class; r2 = "mResourcesImpl"; r1 = r1.getDeclaredField(r2); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r2 = 1; r1.setAccessible(r2); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r0 = r16; r14 = r1.get(r0); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r1 = r14.getClass(); Catch:{ NoSuchFieldException -> 0x0179, IllegalArgumentException -> 0x01bd, IllegalAccessException -> 0x0242 } r2 = "sPreloadedDrawables"; r1 = r1.getDeclaredField(r2); Catch:{ NoSuchFieldException -> 0x0179, IllegalArgumentException -> 0x01bd, IllegalAccessException -> 0x0242 } L_0x0029: r2 = 1; r1.setAccessible(r2); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r2 = r1.get(r14); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r8 = r1; L_0x0032: r1 = r2 instanceof android.util.LongSparseArray; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } if (r1 == 0) goto L_0x01d4; L_0x0036: if (r18 == 0) goto L_0x01a3; L_0x0038: r1 = new com.tencent.theme.g; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r3 = 1; r6 = new android.util.LongSparseArray[r3]; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r3 = 0; r2 = (android.util.LongSparseArray) r2; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r6[r3] = r2; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r2 = r15; r3 = r16; r4 = r18; r5 = r22; r1.<init>(r2, r3, r4, r5, r6); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r15.v = r1; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } L_0x004e: r1 = b; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } if (r1 == 0) goto L_0x01c4; L_0x0052: if (r14 == 0) goto L_0x01c4; L_0x0054: r1 = new com.tencent.theme.h; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r2 = 0; r3 = r15.v; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r1.<init>(r2, r3); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r8.set(r14, r1); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r2 = r14; L_0x0060: r1 = android.os.Build.VERSION.SDK_INT; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r3 = 23; if (r1 < r3) goto L_0x009c; L_0x0066: r1 = "samsung"; r3 = android.os.Build.BRAND; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r1 = r1.equalsIgnoreCase(r3); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } if (r1 == 0) goto L_0x009c; L_0x0071: r1 = android.content.res.Resources.class; r3 = "mALDC"; r1 = r1.getDeclaredField(r3); Catch:{ Throwable -> 0x0235 } if (r1 == 0) goto L_0x0249; L_0x007c: r3 = 1; r1.setAccessible(r3); Catch:{ Throwable -> 0x0235 } r3 = r15.mResources; Catch:{ Throwable -> 0x0235 } r1 = r1.get(r3); Catch:{ Throwable -> 0x0235 } if (r1 == 0) goto L_0x0217; L_0x0088: r3 = r1 instanceof java.util.Map; Catch:{ Throwable -> 0x0235 } if (r3 == 0) goto L_0x0217; L_0x008c: r1 = (java.util.Map) r1; Catch:{ Throwable -> 0x0235 } r1.clear(); Catch:{ Throwable -> 0x0235 } r1 = "SkinEngine"; r3 = 2; r4 = "clear mALDC suss"; r5 = 0; com.tencent.theme.j.c(r1, r3, r4, r5); Catch:{ Throwable -> 0x0235 } L_0x009c: r1 = "SkinEngine"; r3 = 2; r4 = "initIntercepter DrawablePreloadIntercepter ok"; r5 = 0; com.tencent.theme.j.c(r1, r3, r4, r5); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } if (r20 == 0) goto L_0x00f7; L_0x00a9: if (r21 == 0) goto L_0x00f7; L_0x00ab: r1 = b; Catch:{ Exception -> 0x0292 } if (r1 == 0) goto L_0x0268; L_0x00af: r1 = android.content.res.Resources.class; r2 = "mResourcesImpl"; r1 = r1.getDeclaredField(r2); Catch:{ Exception -> 0x0292 } r2 = 1; r1.setAccessible(r2); Catch:{ Exception -> 0x0292 } r0 = r16; r2 = r1.get(r0); Catch:{ Exception -> 0x0292 } r1 = r2.getClass(); Catch:{ NoSuchFieldException -> 0x0256, IllegalArgumentException -> 0x01bd, IllegalAccessException -> 0x0242 } r3 = "sPreloadedComplexColors"; r3 = r1.getDeclaredField(r3); Catch:{ NoSuchFieldException -> 0x0256, IllegalArgumentException -> 0x01bd, IllegalAccessException -> 0x0242 } L_0x00cd: r1 = 1; r3.setAccessible(r1); Catch:{ Exception -> 0x0292 } r1 = r3.get(r2); Catch:{ Exception -> 0x0292 } r1 = (android.util.LongSparseArray) r1; Catch:{ Exception -> 0x0292 } r4 = r1; r7 = r2; r8 = r3; L_0x00da: r1 = a; Catch:{ Exception -> 0x0292 } if (r1 == 0) goto L_0x0282; L_0x00de: r1 = new com.tencent.theme.f; Catch:{ Exception -> 0x0292 } r2 = r15; r3 = r16; r5 = r20; r6 = r21; r1.<init>(r2, r3, r4, r5, r6); Catch:{ Exception -> 0x0292 } r15.w = r1; Catch:{ Exception -> 0x0292 } L_0x00ec: r1 = b; Catch:{ Exception -> 0x0292 } if (r1 == 0) goto L_0x02c3; L_0x00f0: if (r7 == 0) goto L_0x02c3; L_0x00f2: r1 = r15.w; Catch:{ Exception -> 0x0292 } r8.set(r7, r1); Catch:{ Exception -> 0x0292 } L_0x00f7: r1 = "SkinEngine"; r2 = 2; r3 = "initIntercepter colorStateListPreloadIntercepter ok"; r4 = 0; com.tencent.theme.j.c(r1, r2, r3, r4); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r1 = r16.getClass(); Catch:{ Exception -> 0x02ef } r2 = r1.getName(); Catch:{ Exception -> 0x02ef } r3 = "android.content.res.MiuiResources"; r2 = r2.equals(r3); Catch:{ Exception -> 0x02ef } if (r2 == 0) goto L_0x0133; L_0x0113: r2 = "sPreloadDrawableSources"; r1 = r1.getDeclaredField(r2); Catch:{ Exception -> 0x02ef } r2 = 1; r1.setAccessible(r2); Catch:{ Exception -> 0x02ef } r0 = r16; r2 = r1.get(r0); Catch:{ Exception -> 0x02ef } if (r2 != 0) goto L_0x0133; L_0x0126: r2 = new android.util.SparseArray; Catch:{ Exception -> 0x02ef } r2.<init>(); Catch:{ Exception -> 0x02ef } r0 = r16; r1.set(r0, r2); Catch:{ Exception -> 0x02ef } r1 = 1; IS_PROBLEM_MIUI = r1; Catch:{ Exception -> 0x02ef } L_0x0133: r1 = r16.getClass(); Catch:{ Exception -> 0x0302 } r2 = "mIcons"; r1 = r1.getDeclaredField(r2); Catch:{ Exception -> 0x0302 } mIconsOfCM = r1; Catch:{ Exception -> 0x0302 } r1 = mIconsOfCM; Catch:{ Exception -> 0x0302 } r2 = 1; r1.setAccessible(r2); Catch:{ Exception -> 0x0302 } r1 = mIconsOfCM; Catch:{ Exception -> 0x0302 } r2 = 0; r0 = r16; r1.set(r0, r2); Catch:{ Exception -> 0x0302 } r1 = mIconsOfCM; Catch:{ Exception -> 0x0302 } r2 = 0; r1.setAccessible(r2); Catch:{ Exception -> 0x0302 } r1 = r16.getClass(); Catch:{ Exception -> 0x0302 } r2 = "mComposedIconInfo"; r1 = r1.getDeclaredField(r2); Catch:{ Exception -> 0x0302 } mComposedIconInfoOfCM = r1; Catch:{ Exception -> 0x0302 } r1 = mComposedIconInfoOfCM; Catch:{ Exception -> 0x0302 } r2 = 1; r1.setAccessible(r2); Catch:{ Exception -> 0x0302 } r1 = mComposedIconInfoOfCM; Catch:{ Exception -> 0x0302 } r2 = 0; r0 = r16; r1.set(r0, r2); Catch:{ Exception -> 0x0302 } r1 = mComposedIconInfoOfCM; Catch:{ Exception -> 0x0302 } r2 = 0; r1.setAccessible(r2); Catch:{ Exception -> 0x0302 } r1 = 1; IS_PROBLEM_CM11 = r1; Catch:{ Exception -> 0x0302 } L_0x0178: return; L_0x0179: r1 = move-exception; r1 = r14.getClass(); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r1 = r1.getSuperclass(); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r2 = "sPreloadedDrawables"; r1 = r1.getDeclaredField(r2); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } goto L_0x0029; L_0x018b: r1 = android.content.res.Resources.class; r3 = "sPreloadedDrawables"; r3 = r1.getDeclaredField(r3); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r1 = 1; r3.setAccessible(r1); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r0 = r16; r1 = r3.get(r0); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r14 = r2; r8 = r3; r2 = r1; goto L_0x0032; L_0x01a3: r1 = new com.tencent.theme.g; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r3 = 1; r7 = new android.util.LongSparseArray[r3]; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r3 = 0; r2 = (android.util.LongSparseArray) r2; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r7[r3] = r2; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r2 = r15; r3 = r16; r4 = r17; r5 = r19; r6 = r22; r1.<init>(r2, r3, r4, r5, r6, r7); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r15.v = r1; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } goto L_0x004e; L_0x01bd: r1 = move-exception; r2 = new com.tencent.theme.UnSupportPlatformException; r2.<init>(r1); throw r2; L_0x01c4: r1 = new com.tencent.theme.h; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r2 = 0; r3 = r15.v; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r1.<init>(r2, r3); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r0 = r16; r8.set(r0, r1); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r2 = r14; goto L_0x0060; L_0x01d4: r1 = r2 instanceof android.util.LongSparseArray[]; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } if (r1 == 0) goto L_0x030b; L_0x01d8: r2 = (android.util.LongSparseArray[]) r2; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r0 = r2; r0 = (android.util.LongSparseArray[]) r0; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r6 = r0; if (r18 == 0) goto L_0x01fe; L_0x01e0: r1 = new com.tencent.theme.g; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r2 = r15; r3 = r16; r4 = r18; r5 = r22; r1.<init>(r2, r3, r4, r5, r6); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r15.v = r1; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } L_0x01ee: r1 = 0; L_0x01ef: r2 = r6.length; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } if (r1 >= r2) goto L_0x030b; L_0x01f2: r2 = new com.tencent.theme.h; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r3 = r15.v; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r2.<init>(r1, r3); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r6[r1] = r2; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r1 = r1 + 1; goto L_0x01ef; L_0x01fe: r7 = new com.tencent.theme.g; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r8 = r15; r9 = r16; r10 = r17; r11 = r19; r12 = r22; r13 = r6; r7.<init>(r8, r9, r10, r11, r12, r13); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r15.v = r7; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } goto L_0x01ee; L_0x0210: r1 = move-exception; r2 = new com.tencent.theme.UnSupportPlatformException; r2.<init>(r1); throw r2; L_0x0217: r3 = "SkinEngine"; r4 = 1; r5 = new java.lang.StringBuilder; Catch:{ Throwable -> 0x0235 } r5.<init>(); Catch:{ Throwable -> 0x0235 } r6 = "clear fail, mALDC type:"; r5 = r5.append(r6); Catch:{ Throwable -> 0x0235 } r1 = r5.append(r1); Catch:{ Throwable -> 0x0235 } r1 = r1.toString(); Catch:{ Throwable -> 0x0235 } r5 = 0; com.tencent.theme.j.c(r3, r4, r1, r5); Catch:{ Throwable -> 0x0235 } goto L_0x009c; L_0x0235: r1 = move-exception; r3 = "SkinEngine"; r4 = 1; r5 = "clear mALDC Error"; com.tencent.theme.j.a(r3, r4, r5, r1); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } goto L_0x009c; L_0x0242: r1 = move-exception; r2 = new com.tencent.theme.UnSupportPlatformException; r2.<init>(r1); throw r2; L_0x0249: r1 = "SkinEngine"; r3 = 1; r4 = "clear fail, null == f"; r5 = 0; com.tencent.theme.j.c(r1, r3, r4, r5); Catch:{ Throwable -> 0x0235 } goto L_0x009c; L_0x0256: r1 = move-exception; r1 = r2.getClass(); Catch:{ Exception -> 0x0292 } r1 = r1.getSuperclass(); Catch:{ Exception -> 0x0292 } r3 = "sPreloadedComplexColors"; r3 = r1.getDeclaredField(r3); Catch:{ Exception -> 0x0292 } goto L_0x00cd; L_0x0268: r1 = android.content.res.Resources.class; r3 = "sPreloadedColorStateLists"; r3 = r1.getDeclaredField(r3); Catch:{ Exception -> 0x0292 } r1 = 1; r3.setAccessible(r1); Catch:{ Exception -> 0x0292 } r0 = r16; r1 = r3.get(r0); Catch:{ Exception -> 0x0292 } r1 = (android.util.LongSparseArray) r1; Catch:{ Exception -> 0x0292 } r4 = r1; r7 = r2; r8 = r3; goto L_0x00da; L_0x0282: r1 = new com.tencent.theme.d; Catch:{ Exception -> 0x0292 } r2 = r15; r3 = r16; r5 = r20; r6 = r21; r1.<init>(r2, r3, r4, r5, r6); Catch:{ Exception -> 0x0292 } r15.w = r1; Catch:{ Exception -> 0x0292 } goto L_0x00ec; L_0x0292: r1 = move-exception; r2 = android.content.res.Resources.class; r3 = "mPreloadedColorStateLists"; r7 = r2.getDeclaredField(r3); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r2 = 1; r7.setAccessible(r2); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r0 = r16; r4 = r7.get(r0); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r2 = r4 instanceof android.util.SparseArray; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } if (r2 == 0) goto L_0x02cc; L_0x02aa: r1 = new com.tencent.theme.e; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r4 = (android.util.SparseArray) r4; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r2 = r15; r3 = r16; r5 = r20; r6 = r21; r1.<init>(r2, r3, r4, r5, r6); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r15.x = r1; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r1 = r15.x; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r0 = r16; r7.set(r0, r1); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } goto L_0x00f7; L_0x02c3: r1 = r15.w; Catch:{ Exception -> 0x0292 } r0 = r16; r8.set(r0, r1); Catch:{ Exception -> 0x0292 } goto L_0x00f7; L_0x02cc: r2 = r4 instanceof android.util.LongSparseArray; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } if (r2 == 0) goto L_0x02e9; L_0x02d0: r1 = new com.tencent.theme.d; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r4 = (android.util.LongSparseArray) r4; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r2 = r15; r3 = r16; r5 = r20; r6 = r21; r1.<init>(r2, r3, r4, r5, r6); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r15.w = r1; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r1 = r15.w; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r0 = r16; r7.set(r0, r1); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } goto L_0x00f7; L_0x02e9: r2 = new com.tencent.theme.UnSupportPlatformException; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } r2.<init>(r1); Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } throw r2; Catch:{ IllegalArgumentException -> 0x01bd, NoSuchFieldException -> 0x0210, IllegalAccessException -> 0x0242 } L_0x02ef: r1 = move-exception; r2 = DEBUG; if (r2 == 0) goto L_0x02fd; L_0x02f4: r2 = "SkinEngine"; r3 = ""; android.util.Log.e(r2, r3, r1); L_0x02fd: r1 = 0; IS_PROBLEM_MIUI = r1; goto L_0x0133; L_0x0302: r1 = move-exception; r1 = 0; mIconsOfCM = r1; r1 = 0; mComposedIconInfoOfCM = r1; goto L_0x0178; L_0x030b: r2 = r14; goto L_0x0060; */ throw new UnsupportedOperationException("Method not decompiled: com.tencent.theme.SkinEngine.a(android.content.res.Resources, java.lang.Class, int[], int, java.lang.Class, int, java.io.File):void"); }
再来看unInit()方法,这个方法代码比较清楚:
public void unInit() { Field declaredField; try { if (this.v != null) { declaredField = Resources.class.getDeclaredField("sPreloadedDrawables"); declaredField.setAccessible(true); if (declaredField.getDeclaringClass().isArray()) { declaredField.set(null, this.v.b); } else { declaredField.set(null, this.v.b[0]); } } try { Field declaredField2 = Resources.class.getDeclaredField("sPreloadedColorStateLists"); declaredField2.setAccessible(true); LongSparseArray longSparseArray = (LongSparseArray) declaredField2.get(this.mResources); declaredField2.set(null, this.w.b); } catch (Exception e) { declaredField = Resources.class.getDeclaredField("mPreloadedColorStateLists"); declaredField.setAccessible(true); Object obj = declaredField.get(this.mResources); if (obj instanceof SparseArray) { declaredField.set(null, this.x.b); } else if (obj instanceof LongSparseArray) { declaredField.set(null, this.w.b); } } } catch (Throwable e2) { if (DEBUG) { Log.e(TAG, "resotre SkinEngine failed", e2); } } }
根据上述代码再结合Resources获取资源的getDrawable()方法的实现原理: getDrawable()方法会从 sPreloadedDrawables中获取图片,具体可在Resources类源码中查看。
综合两者可以推出SkinEngine的大致实现原理:通过反射获取Resources类的sPreloadedDrawables成员变量,使其指向自定义实现的DrawablePreloadIntercepter对象,在DrawablePreloadIntercepter中重写get()方法,当系统调用get()方法获取图片时即获取到换肤后的图片,从而实现换肤。
- Android 主题切换/换肤方案 研究(四)
- Android 主题切换/换肤方案 研究(一)
- Android 主题切换/换肤方案 研究(二)
- Android 主题切换/换肤方案 研究(三)
- Android 主题切换/换肤方案 研究(五)
- android 动态切换主题,动态换肤
- Android主题换肤 无缝切换
- Android主题换肤 无缝切换
- Android主题换肤 无缝切换
- Android主题换肤 无缝切换
- android 主题切换(换肤功能)
- Android主题换肤 无缝切换
- Android主题换肤 无缝切换
- Android主题换肤 无缝切换
- Android主题换肤 无缝切换
- Android无缝切换主题,动态换肤
- Android主题换肤 无缝切换
- Android主题切换方案
- 安装搜狗输入法、QQ、谷歌浏览器、WinRAR(从虚拟机创建到项目运行四)
- 业余草总结常见计算广告点击率预估算法
- Task执行源码详细剖析
- MySQL数据库引擎
- Oracle ORA-12638: 身份证明检索失败 一个解决方法
- Android 主题切换/换肤方案 研究(四)
- 使用EventBus
- 抽象类与接口的区别
- 程序员面试、算法研究、编程艺术、红黑树、数据挖掘5大系列集锦
- Android仿IOS的UISegmentedContro分段控制器
- 在搭建Spring框架时,web.xml文件的配置之DispatcherServlet&ContextLoaderListener
- opencv-svm分类
- WebRTC中混音流程分析
- 如何将真彩色图转换为各种灰度图