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");    }


可以看到关键的几个字眼:getDeclaredField,Resources.class,mResourcesImpl, sPreloadedDrawables,sPreloadedComplexColors,sPreloadedColorStateLists,DrawablePreloadIntercepter ,colorStateListPreloadIntercepter


再来看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()方法获取图片时即获取到换肤后的图片,从而实现换肤。