Android应用程序换肤实现系列(四)

来源:互联网 发布:轰炸机模型淘宝 编辑:程序博客网 时间:2024/05/03 17:29

转载请标明出处:http://blog.csdn.net/EdisonChang/article/details/50083337

Android换肤系列终于迎来了大结局,各位看官一定憋坏了。通过前几篇啰啰嗦嗦的介绍后,感兴趣的朋友对android的换肤一定有了自己的理解。

本节所介绍两种Android 应用的换肤方案,都可动态扩展换肤,关于动态换肤的优势我就不再重复。与上一节介绍的通用皮肤包不同的地方在于,本节所采用的皮肤文件格式,都是标准的apk包,所以在技术实现上有明显的差异,而且就本节所要介绍的两种方式在实现手段上也是各有特点。

android:sharedUserId 共享进程方式读取皮肤apk资源

在介绍这种换肤之前,我们有必要对android:shareUserId进行相关了解。Android为每个APK进程分配一个单独的用户空间,在AndroidManifest文件中的User Id就是对应一个Linux用户,而通过共享User Id的方式,可以让拥有相同User Id的多个APK运行在同一个进程中。我们知道如果应用运行在同一进程,就可以互相访问进程间数据,同时也可以共享数据目录下的数据库和资源文件,原则上与访问本程序的数据没有差别。

实际应用中,皮肤apk 和主程序必须有同样的签名,使用过程时首先将皮肤apk进行下载安装,注意这种应用方式皮肤apk是需要安装的,至于安装顺序上则没有严格要求,只要保证换肤时,皮肤apk已经正常安装即可。这样就可以保证两个apk 运行在同一个进程中,然后通过resource访问皮肤apk 的资源,主要步骤如下:

(1)在主程序和皮肤程序中的AndroidManifest.xml中进行配置,demo中的User Id采用的是主程序的包名,

android:sharedUserId="com.example.skinmanager"

(2)主程序和皮肤程序的换肤对应的资源文件,必须Id一致

private void changeSkin() {        Context context = null;        try {            context = createPackageContext(Consts.SKIN_PLUGIN_PACKAGE_NAME, Context.CONTEXT_IGNORE_SECURITY);        } catch (PackageManager.NameNotFoundException e) {            e.printStackTrace();        }        //make sure id in the skin is same as host apk        if (context != null) {            Drawable drawable = context.getResources().getDrawable(R.drawable.bg1);            imageView.setImageDrawable(drawable);            //string id in skin1.apk is not the same as host apk            //so we getIdentifier id by resource name            String desc = ResourceHelper.getString(context.getResources(), "skin_info");            textView.setText(desc);        }    }

调用android createPackageContext方法,传入皮肤应用的包名,获取皮肤应用的context , 有了皮肤应用的context, 就可以获取应用的Resources管理类。如果资源id一致,就可以直接用id获取资源引用。

context.getResources().getDrawable(R.drawable.bg1) 

由于资源id由系统编译时自动生成,在两个apk中由于资源类型数量,命名规则随着需求的增加很难约束,所以用资源id作为换肤用的Id非常的不合理。当然我们还可以用Resources getIdentifier方法,传入资源的类型,应用的包名, 还有资源的名称,这样就可以不受资源Id的约束。

public class ResourceHelper {    private static int getIdentifier(Resources resource, String type, String name) {        return resource.getIdentifier(name, type, Consts.SKIN_PLUGIN_PACKAGE_NAME);    }    public static int getId(Resources resource,String name) {        return getIdentifier(resource, Consts.R.ID, name);    }    public static String getString(Resources resource, String name) {        return resource.getString(getIdentifier(resource, Consts.R.STRING, name));    }    public static int getColor(Resources resource, String name) {        return resource.getColor(getIdentifier(resource, Consts.R.COLOR, name));    }    public static Drawable getDrawable(Resources resource, String name) {        return resource.getDrawable(getIdentifier(resource, Consts.R.DRAWABLE, name));    }}

共享进程读取资源的主要步骤到这也差不多了,存在的缺陷还是很明显的。首先就是制作皮肤包过程的复杂性,需要严格要求签名和User Id, 再者就是应用前需要安装,这些问题都给使用过程中带来不便,有优化余地吗?答案是肯定的。

皮肤包作为插件apk,宿主程序动态加载

这种方式下的皮肤apk就不用有User Id和签名的束缚,应用过程也不需要安装。实现过程中反射调用AssetManager addAssetPath 方法加整个皮肤apk插件加载到AssetManager 创建的实例中,然后就可以通过AssetManager 构造皮肤apk的Resources 资源管理实例。

   private void initResource() {        AssetManager assetManager = null;        try {            assetManager = AssetManager.class.newInstance();            Method addAssetPath = assetManager.getClass().getMethod(                    "addAssetPath", String.class);            addAssetPath.invoke(assetManager, getApkFullPath());        } catch (Exception e) {            e.printStackTrace();        }        if (assetManager != null) {            mResources = new Resources(assetManager, super.getResources().getDisplayMetrics(),                    super.getResources().getConfiguration());        }    }

获取Resources的引用后,就可以和前面介绍的方式一样获取皮肤apk的资源,实现换肤。

小结一下,本篇所介绍的两种方案都是需要皮肤制作成标准的apk格式,第二种方式在实现方案上解决了皮肤apk的签名约束和安装问题。相比前一篇文章介绍的换肤方式,也不需要处理资源解析,提高了效率;但是从皮肤文件制作的灵活角度考虑,制作一个apk包对于普通用户还是有一定门槛。总之,几种换肤方式都有可取之处,主要还得取决于应用场景。

有任何疑问可以回复,欢迎继续关注换肤系列的下一篇
源码下载请点击此,欢迎下载,star

0 0