Android 主题切换/换肤方案 研究(四) - qq和qq空间

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_zone
I: Using Apktool 2.2.4 on qq_zone.apk
I: 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()方法获取图片时即获取到换肤后的图片,从而实现换肤。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值