今晚实在不想coding,于是想着整理点知识点,那么简单整理了下插件化开发实现动态更换皮肤。插件化开发大家应该不陌生或多或少用过或听过,插件化开发在项目业务拓展、模块化等方面有不小优势,当然实现一个完美的插件是有困难的。本文如果存在问题恳请指正!欢迎评论交流哦!
效果图:
1、换肤方案分析
-
res下放多种皮肤的资源文件
-
加载插件apk使用其中的皮肤资源
方案一:
优点:容易实现。
缺点:res下放多种皮肤的资源文件,无疑会加大apk文件大小,而且资源文件是写死的,不利于后期拓展,若有其他皮肤需求只能通过版本迭代。
方案二:
优点:不会加大apk包,更容易扩展,有新的皮肤只需下载新的插件包即可,无需更新。(相对于方案一)
缺点:相对于方案一实现较困难,若有更多业务处理需要插件,如使用插件中的四大组件及处理它们的生命周期则是比较麻烦的,需用到动态代理。不过还好有360手机助手团队,在github上开源出来了DroidPlugin插件框架,https://github.com/Qihoo360/DroidPlugin,大家以后用这个就OK啦!
2、换肤实现分析
通过插件化实现更换皮肤,其实做的就是把插件apk中的皮肤资源文件拿到当前apk中使用而已。那么不妨先看下如何获取资源文件;
getResources().getDrawable(R.drawable.ic_launcher);
以上代码通过getResources()获取Resources对象,通过getDrawable(int id)获取Drawable对象,其中参数id R.drawable.ic_launcher是R文件类中的静态内部类drawable类的ic_launcher成员变量的值,如下。
public final class R {
public static final class drawable {
public static final int ic_launcher=0x7f020000;
}
}
那么如何加载一个插件apk中的资源文件呢?肯定也是先获取Resources对象,在获取Drawable对象。不过使用getResources()方法显然是不可行的,因为插件是一个apk,要想拿到里面的资源必须先把该apk加载到内存中来,然后利用反射拿到相关类的方法并使用。如果你对反射技术不是很了解可以看下http://blog.csdn.net/magic_jss/article/details/52187726;
基本步骤:
- 通过DexClassLoader把插件apk加载到内存
- 利用反射技术获取相关类的方法并使用,构造Resources对象
通过DexClassLoader加载插件apk,只需指定插件路径及创建一个优化目录即可,不在赘述,本文主要结合源码讲解如何通过反射构造Resources对象。
查看getResources()源码:
//ContextThemeWrapper.java
@Override
public Resources getResources() {
if (mResources != null) {
return mResources;
}
if (mOverrideConfiguration == null) {
mResources = super.getResources();
return mResources;
} else {
Context resc = createConfigurationContext(mOverrideConfiguration);
//通过源码可以看出mResources对象是通过Context的getResources()方法获取的,查看Context源码
mResources = resc.getResources();
return mResources;
}
}
查看Context源码:
// 抽象类 则通过查看该类的实现类ContextImpl
public abstract class Context {
/** Return a Resources instance for your application's package. */
// 抽象方法
public abstract Resources getResources();
}
查看ContextImpl源码:
@Override
public Resources getResources() {
return mResources;
}
@Override
public Context createConfigurationContext(Configuration overrideConfiguration) {
if (overrideConfiguration == null) {
throw new Ille