换肤有什么可行的思路呢?联想一下Windows应用程序的换肤:(1)把所有皮肤相关的配置参数和使用的图片、音频等资源都编译在dll(皮肤)里,应用程序通过加载不同的dll以实现加裁不同的皮肤。(2)自定义一个皮肤结构(相当于自行设计dll的结构),那就更灵活了。

android换肤第一种方案

实现的原理是如果两个APK的签名相同,配置SharedUserId值相同,则可以相互访问任意数据。思路没有问题,不过只能针对某个程序自行换肤。试图想像一下这个情景:有个主题管理器设定了皮肤,它是不是得跟每个APP建立协议、协定接口,广播通知给每个APP进行皮肤的重新加载。这样的话每个APP都要增加有关切换皮肤的复杂代码,如果没有某个APP的代码你就无能为力了。

android换肤第二种方案

通过Android加载皮肤资源实现。Android中APP使用资源的步骤是这样的:通过Context得到Resource的引用,通过Resource的方法getDrawable,getText,getXml等,传入参数resId(关键)来获得各种类型的资源。每个APK应用程序在启动时,会New一个Resource,同时New一个AssetManager对象,AssetManager管理着一个AssetPath数组,可以通过addAssetPath来添加一个Path,即,启动APK->new Resource->new AssetManger->addAssetPath()。调用Context对象的方法getResource获取一个Resource对象,通过Resources对象的方法getDrawable获取Drawale资源,通过AssetManager对象的方法openAssetPath来打开一个path,这个path就是资源包的路径。通过资源Id找到资源包,从资源包里解析出资源。一个APK常常默认就会添加两个资源路径,一个是系统共享资源包,一个是APP本身的资源包。

了解了这些后,思路很容易出来了,假设APP想加载放在另外一个资源包skin.zip的资源,那么只要在APK启动时调用方法addAssetPath(skin.zip),这时会返回一个索引cookie,在RunTime加载资源时使用这个cookie去打开skin.zip这个资源包来获取资源,而不是去APK本身的资源包里取。

资源Id,即resID,蕴含了很多信息,通过资源Id,我们是可以知道对应的资源是什么类型的、属于哪个包的等等信息,另外资源Id以0x01开头就是系统资源,否则是属于本身APK的资源。知道这些后,我们就可以加入我们的资源选择策略代码了。所谓的策略我是指资源应该优先从哪取,取不到怎么办,缺失了怎么办等。

从另一个角度看,上面说的第一种方案是在应用级别的做法,第二种方案是系统API级别的做法。如果改不了Framework的代码不用去折腾第二种。以这个思路可以实现包括Loyout等几乎所有资源的换肤。