http://www.cnblogs.com/punkisnotdead/p/4968851.html

以前写的这篇文章 可以高仿出 知乎 新浪微博等 绝大多数app的换肤技术,但是遗漏了腾讯的效果,

实际上腾讯的这方面比 上述app要稍微复杂一些,有一点像 现在流行的插件技术。

其实也可以理解,腾讯毕竟是可以靠 皮肤赚钱的公司,所谓 “没钱玩你麻痹” 说的就是腾讯。

靠这个赚钱当然做的会更好一点。今天就来看看腾讯是咋做的。我们也来仿一仿!

就拿qq空间来说吧。

你看我使用了一个qq空间的 黑色主题。不使用别的是因为别的要开通什么黄钻绿钻,但是显然我没有钱。 然后去命令行下看点东西:

记住这个路径,这个时候我要提一下,你只有使用了 特殊的主题以后 这个theme.xml才有值的。

你如果不使用这个你下载的主题 用默认的主题的话就会这样:

我一开始分析腾讯app的时候 这里也卡过一会 后来发现是你得使用主题以后 这个theme 文件才有变化~~

回到前面那个有内容的xml文件看一看。他指向了一个地址,我们就去这个地址下面看看 到底是什么

一看到这个,相信大家 就都明白了,这不就是个apk么?我们打包出去的apk 解压缩以后不就是这些内容么?

所以这里你看 腾讯的做法事 把新的皮肤apk 放在自己data data 包名 这个路径下的某个文件夹内。

但是并没有安装他 对吧。

新浪微博 我们那会分析的时候 他们的皮肤包就是得下载下来以后 再安装一次的。从用户体验上来说,腾讯的

这个明显更加优秀。

到这里 应该很多人就明白了,腾讯的所谓换肤技术,无非就是 把新的皮肤包 下载到自己的安装目录下面,

然后自己的app 去加载这个皮肤包apk里的 资源 即可(注意这里要再强调以下,新浪的皮肤apk是安装好了的,

而腾讯的这个根本没让你安装)!这个就是腾讯旗下app 换肤的原理。你可以打开你的设置---应用里面看一下:

你看明显微博的皮肤都已经在应用列表里面了,但是腾讯的可没有~~~

我们下面就来仿照腾讯的 来实现以下这个效果。

这个效果的关键点 其实就在于 如何在我们的主apk里面 加载到 主题apk里的资源。并且这个主题apk 是不可以被安装的。

就好像高德地图sdk 里提供的那些资源包一样,也是不需要安装 自动就可以使用的。

这个资源包里面 一般都包含 字体颜色啊 背景色啊 背景图啊 复杂的甚至会包含布局文件!

那我现在就做一个最简单的效果,主apk里 有一个tv 他有一个背景色,然后我们点击更换主题以后 这个tv就会 把这个背景色

更换成一个 背景图(我这个背景图是用的林熙蕾的照片)。当然了 我们这个背景图显然是放在我们的主题apk里的。我们的

主apk里当然是不会有这张图的,不然还做个毛啊!(如果能做出这个demo 那么很显然其他的就全部都能通了)

我们首先来做一下这个主题apk,

第一步,把我们的背景图片放到相映的路径下:

第二步:定义主题apk里的 一个类和一个方法:

 package com.example.administrator.themeapk;

 import android.content.res.Resources;
import android.graphics.drawable.Drawable; /**
* Created by Administrator on 2015/12/24.
*/
//这里我们因为是demo演示 所以实际上就只有一个返回Drawable的方法
//实际上你可以自己往下面写,返回任何资源,比如theme,比如string,比如color,甚至资源文件等等
public class ResourceUtils { public static Drawable getTextViewBackGroundDrawable(Resources resources) {
return resources.getDrawable(R.mipmap.lxl);
} //可以思考一下 为什么这个地方我们不用这个context作为参数的方法,把这个方法给注释掉了。
//其实原因也很简单 一个Context对应着唯一的一个Recource,如果我们想要在主apk里调用
//我们主题apk里的资源,那这个context参数就无法构造了,因为主apk里只能拿到自己的context,
//肯定是拿不到主题apk里的context的。所以我们要用上面的Resources这个参数,因为虽然我们拿不到
//主题的context,但是我们可以把主题apk里的resource 加入到主apk里的resource。
// public static Drawable getTextViewBackGroundDrawable(Context context)
// {
// return context.getResources().getDrawable(R.mipmap.lxl);
// } }

然后我们的主题apk实际上就编写完成了,然后我们对这个工程进行打包,并且命名为theme.apk

然后我们把这个theme.apk 放到我们主apk的 cache目录下面:

最后我们可以先运行一下程序 看看效果:

最后我们看下最关键的主apk里的代码 应该怎么写:

  //这个changeTv: 一按就自动加载主题apk里的资源 并且更换themetv 这个tv里的背景色了
changeTv = (TextView) findViewById(R.id.changeTv);
//themeTv: 就是用于展现效果的textview 替换背景色 就是替换这个textview的
themeTv = (TextView) findViewById(R.id.themeTv);
changeTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//这个fileDir 一般都是返回/data/data/你程序的包名/cache/
String fileDir = getCacheDir() + File.separator;
//我们是把theme.apk这个文件push到/data/data/你程序的包名/cache/这个路径下的
//注意如果你自己做的话,这些主题包 当然是从网上下载下来 注意下载下来以后放在/data/data/你程序的包名/
//这个路径下 任何一个目录都可以 不一定非要是/cache/这个目录
String filePath = fileDir + "theme.apk";
//这个目录是用来构建DexClassLoader对象的 ,用作构造函数里的第二个参数
//是dex的输出路径(因为加载apk/jar的时候会解压出dex文件,这个路径就是保存dex文件的)
String optimizedDirectory = getCacheDir() + File.separator;
//DexClassLoader可以加载任何路径的apk/dex/jar 这里要注意了PathClassLoader只能加载/data/app中的apk,也就是已经安装到手机中的apk。
//这个也是PathClassLoader作为默认的类加载器的原因,因为一般程序都是安装了,在打开,这时候PathClassLoader就去加载指定的apk(解压成dex,然后在优化成odex)就可以了。
ClassLoader classLoader = new DexClassLoader(filePath, optimizedDirectory, null, getClassLoader());
//把我们主题apk包里的资源 加载到本apk自己的resouce里
addOtherResourcesToMain(filePath);
try {
//DexClassLoader对象来 加载theme.apk包里的ResourceUtils这个类的getTextViewBackGroundDrawable这个方法
Class clazz = classLoader.loadClass("com.example.administrator.themeapk.ResourceUtils");
Method method = clazz.getMethod("getTextViewBackGroundDrawable", Resources.class);
//invoke 也就是执行方法的时候 可以看到我们传的参数是mResource 而这个mResource是我们自己新构造出来的
//里面包含了theme.apk里的资源。
Drawable drawable = (Drawable) method.invoke(null, mResource);
//成功获取了 主题apk里的图片资源以后 剩下的事情就水稻渠成了.
themeTv.setBackgroundDrawable(drawable);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
});
  //这个方法把我们主题apk里的resource 加入到我们自己的主apk里的resource里
//这个dexPath就是 我们theme.apk在 我们主apk 的存放路径
private void addOtherResourcesToMain(String dexPath) {
try {
AssetManager assetManager = AssetManager.class.newInstance();
//反射调用addAssetPath这个方法 就可以
Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
addAssetPath.invoke(assetManager, dexPath);
mAssetManager = assetManager;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
//把themeapk里的资源 通过addAssetPath 这个方法增加到本apk自己的path里面以后 就可以重新构建出resource对象了
mResource = new Resources(mAssetManager, getResources().getDisplayMetrics(), getResources().getConfiguration());
}

注释应该写的比较清楚了。相信大家应该能理解的比较好。其原理可以参考老罗的博客:http://blog.csdn.net/luoshengyang/article/details/8791064

总结起来qq的皮肤加载技术 其实就下面几步:

1.实例化 AssetManager 对象,并通过反射调用 addAssetPath(String) 方法加载目标 apk(或与 apk 文件架构一致的目录)
2.通过第一步得到的 AssetManager 实例化 Resource 对象
3.利用第二步得到的 Resource 对象来动态加载资源(这个方案是比较简单的方案 但是有一定局限性 读者可以自己这样写一个,我这篇blog里的方案是直接第四步)

4.通过dexclassloader 来反射调用 主题包里的方法 来得到资源。参数就用我们第二步得到的Resource对象。这样做的好处是,我们可以定义一个规范的接口出来,

我们的主apk 直接调用接口方法 即可,theme.apk里 实现这个接口就行了。这样你就算有100个主题包,我们的主apk里的代码也只用写一份即可!非常方便。

Android 高仿腾讯旗下app的 皮肤加载技术的更多相关文章

  1. [转]Android 超高仿微信图片选择器 图片该这么加载

    快速加载本地图片缩略图的方法: 原文地址:Android 超高仿微信图片选择器 图片该这么加载 其示例代码下载: 仿微信图片选择器 ImageLoader

  2. Android 超高仿微信图片选择器 图片该这么加载

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/39943731,本文出自:[张鸿洋的博客] 1.概述 关于手机图片加载器,在当今像 ...

  3. Android 高仿豌豆荚 一键安装app 功能 实现

    以往我们那些应用市场 帮我们安装app的时候  我们都得点确定,当然你如果 root 以后 不用点确定 也能自动安装了,后来豌豆荚 推出了一个功能 非root的手机也能不点确定 直接帮你安装好.(如果 ...

  4. android电子书App、自定义图表、仿腾讯漫画App、仿淘宝优惠券、3D选择容器等源码

    Android精选源码 仿支付宝记账本功能,饼状图:数字键盘 android一款功能完善的电子书应用源码 Android自定义图标库,使用方便,扩展性强 android 3D立体无限旋转容器源码 an ...

  5. Android 高仿 频道管理----网易、今日头条、腾讯视频 (可以拖动的GridView)附源码DEMO

    距离上次发布(android高仿系列)今日头条 --新闻阅读器 (二) 相关的内容已经半个月了,最近利用空闲时间,把今日头条客户端完善了下.完善的功能一个一个全部实现后,就放整个源码.开发的进度就是按 ...

  6. Android 高仿 频道管理----网易、今日头条、腾讯视频 (能够拖动的GridView)附源代码DEMO

    距离上次公布(android高仿系列)今日头条 --新闻阅读器 (二) 相关的内容已经半个月了.近期利用空暇时间,把今日头条client完好了下.完好的功能一个一个所有实现后.就放整个源代码.开发的进 ...

  7. 高仿腾讯QQ最终版

    之前写过一篇关于高仿腾讯QQ的博客,不知道的看这:http://blog.csdn.net/htq__/article/details/51840273 ,主要是从界面上高仿了腾讯QQ,在UI上基本上 ...

  8. (android高仿系列)今日头条 --新闻阅读器 (三) 完结 、总结 篇

    从写第一篇今日头条高仿系列开始,到现在已经过去了1个多月了,其实大体都做好了,就是迟迟没有放出来,因为我觉得,做这个东西也是有个过程的,我想把这个模仿中一步一步学习的过程,按照自己的思路写下来,在根据 ...

  9. Android 高仿QQ5.2双向側滑菜单DrawerLayout实现源代码

    Android 高仿QQ5.2双向側滑菜单DrawerLayout实现源代码 左右側滑效果图 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a ...

随机推荐

  1. BZOJ 1191: [HNOI2006]超级英雄Hero 二分匹配

    1191: [HNOI2006]超级英雄Hero Description 现在电视台有一种节目叫做超级英雄,大概的流程就是每位选手到台上回答主持人的几个问题,然后根据回答问题的多少获得不同数目的奖品或 ...

  2. IDA 与VC 加载符号表

    将Windbg路径下的symsrv.yes 拷贝到ida 的安装目录,重新分析ntoskrnl.exe, 加载本地的符号表 添加环境变量  变量名:_NT_SYMBOL_PATH变量值:SRV*{$P ...

  3. 传入sql数组字符串,输出table

    CREATE function [dbo].[split](@aString varchar(),@pattern varchar()) returns @temp table([Sid] [, ) ...

  4. Struts2.0 去掉action后缀名

    刚刚接触Struts2.0,发现默认请求都会带着后缀名:action 就如下图,url地址中会暴露login.action(请原谅struts拼写错误..) 作为一个URL简洁爱(chu)好(nv)者 ...

  5. C. Tourist's Notes

    题目链接 题意:n天内登山,相邻两次登山的高度差的绝对值小于等于1,也就是说每次高度变化只能是:0,1,-1.我们已经知道n天中部分天数人所在的山的高度,求最大的登山高度. 输入: n m  n 是天 ...

  6. android-exploitme(五):不安全的数据存储

    今天我来看看如果android将数据存储在sdcard,它的权限是什么样的 1. 打开emm软件,做一笔转账.

  7. sftp的安装和使用

    http://blog.srmklive.com/2013/04/24/how-to-setup-sftp-server-ftp-over-ssh-in-ubuntu/ In my previous ...

  8. CentOS编译安装Python3

    前话 最近想学一下一门新的高级语言,无意中看到用python仿AIphaGo的github项目,就决定是他了. AIphaGo的Git传送门: https://github.com/Rochester ...

  9. MyBatis学习总结_03_优化MyBatis配置文件中的配置

    一.连接数据库的配置单独放在一个properties文件中 之前,我们是直接将数据库的连接配置信息写在了MyBatis的conf.xml文件中,如下: 1 <?xml version=" ...

  10. poj -2229 Sumsets (dp)

    http://poj.org/problem?id=2229 题意很简单就是给你一个数n,然后选2的整数幂之和去组成这个数.问你不同方案数之和是多少? n很大,所以输出后9位即可. dp[i] 表示组 ...