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里的 一个类和一个方法:

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

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

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

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

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

 1  //这个changeTv: 一按就自动加载主题apk里的资源 并且更换themetv 这个tv里的背景色了
 2         changeTv = (TextView) findViewById(R.id.changeTv);
 3         //themeTv: 就是用于展现效果的textview 替换背景色 就是替换这个textview的
 4         themeTv = (TextView) findViewById(R.id.themeTv);
 5         changeTv.setOnClickListener(new View.OnClickListener() {
 6             @Override
 7             public void onClick(View v) {
 8                 //这个fileDir 一般都是返回/data/data/你程序的包名/cache/
 9                 String fileDir = getCacheDir() + File.separator;
10                 //我们是把theme.apk这个文件push到/data/data/你程序的包名/cache/这个路径下的
11                 //注意如果你自己做的话,这些主题包 当然是从网上下载下来 注意下载下来以后放在/data/data/你程序的包名/
12                 //这个路径下 任何一个目录都可以 不一定非要是/cache/这个目录
13                 String filePath = fileDir + "theme.apk";
14                 //这个目录是用来构建DexClassLoader对象的 ,用作构造函数里的第二个参数
15                 //是dex的输出路径(因为加载apk/jar的时候会解压出dex文件,这个路径就是保存dex文件的)
16                 String optimizedDirectory = getCacheDir() + File.separator;
17                 //DexClassLoader可以加载任何路径的apk/dex/jar 这里要注意了PathClassLoader只能加载/data/app中的apk,也就是已经安装到手机中的apk。
18                 //这个也是PathClassLoader作为默认的类加载器的原因,因为一般程序都是安装了,在打开,这时候PathClassLoader就去加载指定的apk(解压成dex,然后在优化成odex)就可以了。
19                 ClassLoader classLoader = new DexClassLoader(filePath, optimizedDirectory, null, getClassLoader());
20                 //把我们主题apk包里的资源 加载到本apk自己的resouce里
21                 addOtherResourcesToMain(filePath);
22                 try {
23                     //DexClassLoader对象来 加载theme.apk包里的ResourceUtils这个类的getTextViewBackGroundDrawable这个方法
24                     Class clazz = classLoader.loadClass("com.example.administrator.themeapk.ResourceUtils");
25                     Method method = clazz.getMethod("getTextViewBackGroundDrawable", Resources.class);
26                     //invoke 也就是执行方法的时候 可以看到我们传的参数是mResource 而这个mResource是我们自己新构造出来的
27                     //里面包含了theme.apk里的资源。
28                     Drawable drawable = (Drawable) method.invoke(null, mResource);
29                     //成功获取了 主题apk里的图片资源以后 剩下的事情就水稻渠成了.
30                     themeTv.setBackgroundDrawable(drawable);
31                 } catch (ClassNotFoundException e) {
32                     e.printStackTrace();
33                 } catch (NoSuchMethodException e) {
34                     e.printStackTrace();
35                 } catch (InvocationTargetException e) {
36                     e.printStackTrace();
37                 } catch (IllegalAccessException e) {
38                     e.printStackTrace();
39                 }
40             }
41         });

 1  //这个方法把我们主题apk里的resource 加入到我们自己的主apk里的resource里
 2     //这个dexPath就是 我们theme.apk在 我们主apk 的存放路径
 3     private void addOtherResourcesToMain(String dexPath) {
 4         try {
 5             AssetManager assetManager = AssetManager.class.newInstance();
 6             //反射调用addAssetPath这个方法 就可以
 7             Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
 8             addAssetPath.invoke(assetManager, dexPath);
 9             mAssetManager = assetManager;
10         } catch (InstantiationException e) {
11             e.printStackTrace();
12         } catch (IllegalAccessException e) {
13             e.printStackTrace();
14         } catch (NoSuchMethodException e) {
15             e.printStackTrace();
16         } catch (InvocationTargetException e) {
17             e.printStackTrace();
18         }
19         //把themeapk里的资源 通过addAssetPath 这个方法增加到本apk自己的path里面以后 就可以重新构建出resource对象了
20         mResource = new Resources(mAssetManager, getResources().getDisplayMetrics(), getResources().getConfiguration());
21     }

注释应该写的比较清楚了。相信大家应该能理解的比较好。其原理可以参考老罗的博客: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里的代码也只用写一份即可!非常方便。

转载于:https://www.cnblogs.com/punkisnotdead/p/5082932.html

Android 高仿腾讯旗下app的 皮肤加载技术相关推荐

  1. IOS视频直播:高仿腾讯旗下NOW直播映客直播类型

    效果图如下 处理了对IPad版本的适配 随着直播市场的火热,市场大军都逐步进入直播市场 ,腾讯旗下的NOW直播也不例外 先说说直播设计底层 一 .流媒体 1 - 伪流媒体 1.1 扫盲:边下载边播放 ...

  2. iOS【IOS视频直播:高仿腾讯旗下NOW直播映客直播类型】

    效果图如下 NowGif.gif -> 4349FA93-F15B-4287-9162-3AA27B223A72.png FA783124-4B81-455F-A6E6-461ACA874478 ...

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

    2019独角兽企业重金招聘Python工程师标准>>> 转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/39943 ...

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

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

  5. Android插件化开发基础之App如何动态加载类

    在 Java 里面,我们可以把一些类放到 .jar 文件里面,然后用 ClassLoader 动态加载.例如: URLClassLoader ucl = URLClassLoader.newInsta ...

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

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

  7. Android 高仿腾讯新闻频道定制页面

    先上效果图 移除频道的动画效果模仿得不是很像,如果有更好的实现方法可以在下面留言告诉我. 页面效果拆解 部分已选频道位置固定,不能拖动 已选频道可以长按拖拽改变顺序,长按后背景色改变,删除按钮隐藏,原 ...

  8. android 仿快递步骤_Android实现仿美团、顺丰快递数据加载效果

    我们都知道在Android中,常见的动画模式有两种:一种是帧动画(Frame Animation),一种是补间动画(Tween Animation).帧动画是提供了一种逐帧播放图片的动画方式,播放事先 ...

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

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

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

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

最新文章

  1. 人性经不起金钱的考验,何况是黄金 --- 我看电影《黄金兄弟》
  2. NCBI dbGap数据下载记录
  3. centos7 中文乱码问题解决方法
  4. 「云网络」VS「云计算」- vecloud微云服务器
  5. 121. Leetcode 5. 最长回文子串 (动态规划-子序列问题)
  6. ConcurrentHashMap的源码分析-扩容过程图解
  7. 发布ASP.NET程序至IIS7
  8. python调用.a静态库_Python 调用 C
  9. DotText研究资料整理
  10. scp 免输入密码的方法
  11. VS2015中搭建lua环境
  12. 云鲸扫拖一体机器人说明书_云鲸小白鲸扫拖一体机器人好用吗?真实的测评带你走进智能化家居时代...
  13. c语言给定n个矩形及其长和宽,七年级数学秋季学期期末考试试题
  14. iso12233测试方法_ISO12233图像分辨率测试卡的使用方法
  15. CSS冻结单元格(新增滚动阴影效果)
  16. 系统更换数据库oracle到mysql问题汇总
  17. 集五福华为机试python_支付宝集五福,扫一扫用新年送礼好物华为MatePad Pro画的福试试...
  18. Python编写数字转换成中文大写
  19. 上半年要写的博客文章29
  20. 进程间的通信方式(1)

热门文章

  1. c语言中立方和乘法怎么表示什么区别,c语言中的乘方符号是什么?
  2. 获取远程资源提示msxml3.dll指定资源下载失败
  3. CDN是什么?以及CDN的原理
  4. 俄内政部悬赏破解 Tor 匿名网络
  5. 测试点击屏幕次数的软件_一款让安卓手机更好用的软件——fooView
  6. 北京车展:长城、高合、现代、东风悦达起亚等知名车企展示行业最新产品和技术...
  7. Beautiful Soup 4 的安装与使用
  8. vue校验输入框不能有中文
  9. 数理统计——样本分布
  10. 路由及路由器工作原理深入解析3:路由与端口