Android 系统主题更换功能的原理

一、   实现思路

安卓应用在读取资源时是由AssetManager和Resources两个类来实现的。Resouce类是先根据ID来找到资源文件名称,然后再将该文件名称交给AssetManager来打开文件。我们主题开发的核心思路就是在应用读取资源时,先去主题包里读取资源,若有资源,直接返回主题包的资源,若无资源,直接返回应用本身的资源。

参考博客:http://blog.csdn.net/luoshengyang/article/details/8806798

二、   方案实现

a)   修改源码Resource.java

private LoadResourcesCallBack mCallBack;

public interface 
LoadResourcesCallBack{
    ColorStateListloadColorStateList(TypedValue value
, int id);

    
Drawable loadDrawable(TypedValue value, int id);

    
XmlResourceParser loadXmlResourceParser(int idString type);

    int 
getDimensionPixelSize(int index);

    int 
getDimensionPixelOffset(int index);

    float 
getDimension(int index);

    
Integer getInteger(int index);
}

public LoadResourcesCallBack getLoadResourcesCallBack() {
    
return mCallBack;
}

public boolean regitsterLoadResourcesCallBack(LoadResourcesCallBackcallBack) {
    
if (callBack== null || mCallBack != null) {
        
return false;
    
}
    mCallBack = callBack
;
    return true;
}

在Resouces类是主要加这个接口,这个接口就是主题改变的关键所在。接口LoadResourcesCallBack中的抽象方法,从名子中可以发现,Resources类中也有同名的方法。LoadResourcesCallBack中的方法就是在Resources同名方法中调用,即Resouces类中在执行这些方法时,会先执行LoadResourcesCallBack实现的方法。

LoadResourcesCallBack抽象方法实现如下:

context.getResources().regitsterLoadResourcesCallBack(new Resources.LoadResourcesCallBack() {
    
@Override
    
public ColorStateListloadColorStateList(TypedValue typedValue, int i) {
       
  if (i == 0return null;
        
String entryName = context.getResources().getResourceEntryName(i);
        
ColorStateList color = ResourceManagerTPV.getInstance(context).getColorStateList(entryName);
             return 
color;
    
}

    
@Override
    
public DrawableloadDrawable(TypedValue typedValue, int i) {
     
   if(i == 0){
            
return null;
        
}
        String entryName = 
context.getResources().getResourceEntryName(i);
        
Drawable drawable = ResourceManagerTPV.getInstance(context).getDrawable(entryName);
        return 
drawable;
    
}

    
@Override
    
public XmlResourceParserloadXmlResourceParser(int iString s) {
        
return null;
    
}

    
@Override
    
public int getDimensionPixelSize(int i) {
     
  if(i == 0){
            
return -1;
        
}
        String entryName = 
context.getResources().getResourceEntryName(i);
        int 
dimensionPixelSize = ResourceManagerTPV.getInstance(context).getDimensionPixelSize(entryName);
        return 
dimensionPixelSize;
    
}

    
@Override
    
public int getDimensionPixelOffset(int i) {
      
 if(i == 0){
            
return -1;
        
}
        String entryName = 
context.getResources().getResourceEntryName(i);
           return 
ResourceManagerTPV.getInstance(context).getDimensionPixelOffset(entryName);
    
}

    
@Override
    
public float getDimension(int i) {
   
     if(i == 0){
            
return -1;
        
}
        String entryName = 
context.getResources().getResourceEntryName(i);
        return 
ResourceManagerTPV.getInstance(context).getDimen(entryName);
    
}

    
@Override
    
public IntegergetInteger(int i) {
      
 if(i == 0){
            
return null;
        
}
        String entryName = 
context.getResources().getResourceEntryName(i);
        return 
ResourceManagerTPV.getInstance(context).getInteger(entryName);
    
}
})
;

实现原理:在查找资源时会根据提供的ID进行查找,然后通过资源ID查找资源ID对应的资源名称,然后获取当前设置的主题包的Context,然后再由主题包的Context通过资源名称查找当前主题包下是否有要查询的资源,有就返回具体资源的值,如图片,就返回Drawable资源,没有就返回Null,Resouce类就会执行自己的方法。

b)   通知更新

实现原理:参考系统语言切换的实现方法。

参考博客:http://blog.csdn.net/wxlinwzl/article/details/42614117

三、    开发中遇到的问题

a)   开发框架的设计

在方案实行前,已讨论过主题的实现方案,刚开始实现方案与台北TPV的主题实现方案类似,都是先制作一个默认的主题包。但是在制作完主题包后,发现该方案,资源为只读,不能同时支持多个主题动态切换,且在XML中不能直接引用。后来就参考了TUF的做法,觉得这个方案可行,就按这个方案来实行。

b)   代码中读取color资源,仍是应用本身的资源,不是主题包的资源

在与Launcher和SystemUI调试时,同样的方法,在代码读取图片和Color值时,图片会读取到安装的主题包下的资源,而Color资源没有读取到,仍是应用本身设置的值。后来发现是在Resources源码中getColor,如下红色字体代码中,还没有判断是用哪个资源时,已经直接返回应用的Color资源。最后解决方法,在该段代码前进行判断。

@ColorInt

    public int getColor(@ColorRes int id,@Nullable Theme theme) throws NotFoundException {

      ……..

     if (value.type >=TypedValue.TYPE_FIRST_INT

                    && value.type <=TypedValue.TYPE_LAST_INT) {

                mTmpValue = value;

                return value.data;

            } else if (value.type !=TypedValue.TYPE_STRING) {

                throw new NotFoundException(

                        "Resource ID#0x" + Integer.toHexString(id) + " type #0x"

                                + Integer.toHexString(value.type) + " isnot valid");

            }

            mTmpValue = null;

        }

           …….

      final ColorStateList csl =loadColorStateList(value, id, theme);

      ……..

}

c)   SystemUI不会更新

SystemUI是一个很特殊的应用,切换资源时,无法同步切换,只能通过重启手机才会更新资源,而重启手机又与UX的设计不一致。最后只能通过广播通知其更新。

d)  Widget应用无法更新

在与Clock调试时,同样的方法,同样的步骤,在widget中就是不切换资源,最后也只能像SystemUI一样通过广播进行更新。只有一个Clock,那时感觉用这种方法,也还好,修改的代码不多,也不繁琐。后来,天气也需要更新,问题就来了。天气widget的实现方法与Clock不一样,而且天气设置图片的方法也不一样。若是天气也是接收广播,然后再自己代码中更新,修改的代码量非常多,且本身天气应用的逻辑就比较复杂,如此下去不是一个可行的实现方案,不排除以后其他的Widget也需要修改,所以这个方案不能实行,只能另辟方法。

  所以就一直去看看Widget的实现原理,发现Widget都是通过RemoteView来远程代理的。就去查看RemoteView的源码,

private View inflateView(ContextcontextRemoteViews rvViewGroupparent) {
    
// RemoteViews may be built by an application installedin another
    // user. So build a context thatloads resources from that user but
    // still returns the current usersuserId so settings like data / time formats
    // are loaded without requiring crossuser persmissions.
    
final ContextcontextForResources = getContextForResources(context);
    
Context inflationContext = new ContextWrapper(context){
        
@Override
        
public ResourcesgetResources() {
            
return contextForResources.getResources();
        
}
        
@Override
        
public Resources.ThemegetTheme() {
            
return contextForResources.getTheme();
        
}
        
@Override
        
public StringgetPackageName() {
            
return contextForResources.getPackageName();
        
}
    }
;
    
LayoutInflater inflater = (LayoutInflater)
            context.getSystemService(Context.
LAYOUT_INFLATER_SERVICE);
    
// Clone inflater so we load resources from correctcontext and
    // we don't add a filter to thestatic version returned by getSystemService.
    
inflater = inflater.cloneInContext(inflationContext);
    
inflater.setFilter(this);
    return 
inflater.inflate(rv.getLayoutId()parent, false);
}

    通过这个方法开头的说明,这个方法就是RemoteView获取资源的关键所在,我们只要在getResources()方法中注册一下Resources类中自定义的接口。事实证明,确实是如此,修改了此次代码后,Clock和天气都不需要执行任何操作。主题市场只需要统计需要修改的Color和Drawable的名称。

e)   锁屏壁纸和桌面壁纸与效果不一致

壁纸的设置,系统有提供一些接口进行设置。该开始就用了系统提供的接口进行设置,发现桌面的壁纸不会动,而且显示得很模糊,锁屏的壁纸模糊效果与壁纸不在同一个位置,出现分层。显然这样的设置方法是不可行的,询问了Launcher负责人,具体Launcher的裁剪方法也不是非常清楚。最后自己去下载了Launcher的代码来看,设置壁纸确实好复杂,非常多个类,各种逻辑,要完全明白,真的有点困难。在这个设置壁纸上,也花费了较长时间进行分析。虽然现在也不是完全明白怎么设置的,但是通过各方面的测试,最终达到了效果。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
 本书内容   本书的各个章节及其组织方式如下所示。   第1章“Android系统概述”,概述Android系统方面的内容,包括智能手机平台的发展历程、Android系统的特点、Android的3种开发工作,以及Android的2种不同的开发方式。   第2章“Android系统开发综述”,介绍Android系统开发的综述性内容,包括工具使用、获得代码、编译系统、仿真器运行、SDK使用等。   第3章“Android的Linux内核与驱动程序”,介绍Android内核的特点、Android中使用的专用驱动程序、Android系统可能使用的标准设备驱动。   第4章“Android的底层库和程序”,介绍Android系统的基础程序,以本地程序为主。   第5章“Android的Java虚拟机和Java环境”,这是介于本地和Java层之间的相关内容,主要介绍Android的Java虚拟机Dalvik的基本概念、Android Java程序的环境、JNI的使用方法,以及Java框架的启动流程等。   第6章“Android的GUI系统”,包括Android GUI系统架构、底层的pixelflinger和libui库、Surface系统、Skia和2D图形系统Android的OpenGL和3D图形系统等内容。   第7章“Android的Audio系统”,主要是音频的输入输出环节,音频系统从驱动程序、本地框架到Java框架都具有内容。   第8章“Android的Video输入输出系统”,介绍Android的Video输入输出系统,包括Overlay系统和Camera系统两个部分,前者只有本地的内容,后者各个层次均有内容。   第9章“Android的多媒体系统”,介绍Android的多媒体系统的核心部分,包括Android中多媒体系统的业务、结构、多媒体系统的核心框架、OpenCore系统结构和使用等内容。   第10章“Android的电话部分”,介绍Android系统在电话方面的功能Android是智能手机系统,电话是其核心部分。   第11章“Android的连接部分”,主要包括WiFi、蓝牙及定位系统等,这些Android的连接部分也具有从驱动层到Java应用层的结构。   第12章“Android的传感器系统”,传感器系统涉及Android的各个层次,具有完整的结构,相比其他系统,传感器系统的各个层次都比较简单。   第6~12章分模块介绍Android的几个核心系统,主要是本地框架和Java框架方面的内容,兼顾应用程序和驱动层,这是本书的重点。   第13章“Android应用程序概述及框架”,介绍Android应用程序层的基本概念和应用程序框架,这部分内容是Android自下而上的第4个层次,可以基于源代码或者SDK开发,它们之间的差别非常小。   第14章“Android应用程序的主要方面”,介绍Android应用程序层开发的各个方面,基本上是横向内容,包括应用程序的基本控制、各种UI元素的使用、图形API使用3个方面的内容。   第15章“Android应用程序的设计思想”,本章的内容是基于通用的应用程序和GUI程序的通用设计思想,结合Android系统自身的特点,提出一些值得注意的问题和设计方法。   本书读者   本书适应广大的读者群,力求在Android系统移植、应用程序开发系统框架改进方面给读者全面的支持。不同的读者在学习本书时,应该使用不同的方法。   Android初级开发者:在本书指引下阅读代码,搭建系统开发环境,对于Android应用程序的开发者,重点关注后3章的内容。   Android中、高级开发者:通过本书的引导,学习系统架构,关注开发要点,并尽量使用手机系统的通用设计思想、软件工程思想、系统工程思想来指导Android系统学习。   嵌入式Linux系统学习者:将Android作为一个集Linux核心和应用层程序于一体的系统进行学习,并可以利用Android的仿真环境运行和调试程序。   读者在学习本书的过程中,应尽量对照本书的框图和手头的Android源代码,这样可以达到事半功倍的效果。本书在重点代码中加上大量的注释,帮助读者阅读,对于非重点的代码,不占用本书的篇幅,读者可以参考开放的源代码。可以采用顺序读和重点读相结合的方式学习本书,顺序读关注系统框架,重点读关注开发要点。   本书作者   本书在编写过程中提炼和综合Android早期开发者的经验、中国大陆Androidin社区的开发成果,以及各位专家的经验和技术,这是本书出版的知识源泉。本书主要由Androidin社区的两名核心技术专家韩超和梁泉领衔规划和编著,韩超统稿。总部设在南加州、专注于Android平台并提供其移动应用开发及解决方案的迈奔无线(mAPPn Inc.)也投入技术和人力参与了本书的工作。   参与本书编写的还有于仕林、张宇、张超等人,赵家维、黄亮、沈桢、徐威特、杨钰等参与了审校工作。   由于时间仓促,可能依然存在一些错误和问题,请读者见谅,欢迎读者讨论和指点。 编辑本段 目 录   第1章 Android系统概述 1   1.1 基础知识 1   1.1.1 Android开发系统的由来 1   1.1.2 移动电话系统开发模式 2   1.1.3 未来移动电话的功能Android的优势 4   1.2 Android开发工作 6   1.2.1 Android移植开发 6   1.2.2 Android应用开发 8   1.2.3 Android系统开发 9   1.3 Android的SDK与源代码 10   1.3.1 基于SDK的Android开发 10   1.3.2 基于源代码SDK Android开发 11   第2章 Android系统开发综述 13   2.1 Android系统架构 13   2.1.1 软件结构 13   2.1.2 Android的工具 16   2.2 Android源代码的开发环境 18   2.2.1 Android源代码的获取和提交 18   2.2.2 Android源代码结构 21   2.2.3 编译 24   2.2.4 系统的运行 25   2.3 Android SDK的开发环境 32   2.3.1 SDK的结构 32   2.3.2 Windows环境SDK开发 33   2.3.3 Linux环境SDK开发 42   第3章 Android的Linux内核与驱动程序 44   3.1 Linux核心与驱动 44   3.2 Android专用驱动 45   3.2.1 Ashmem 45   3.2.2 Binder 45   3.2.3 Logger 46   3.3 Android使用的设备驱动 46   3.3.1 Framebuffer显示驱动 46   3.3.2 Event输入设备驱动 48   3.3.3 v4l2摄像头——视频驱动 50   3.3.4 OSS音频驱动 53   3.3.5 ALSA音频驱动 54   3.3.6 MTD驱动 56   3.3.7 蓝牙驱动 57   3.3.8 Wlan驱动 58   第4章 Android的底层库和程序 60   4.1 底层库和程序的结构 60   4.1.1 本地实现的基本结构 60   4.1.2 增加本地程序和库的方法 61   4.2 标准C/C++库bionic 64   4.3 C语言工具库libcutils 65   4.4 init可执行程序 66   4.5 Shell工具 72   4.6 C++工具库libutils 75   4.6.1 libutils的基本内容 75   4.6.2 Binder 76   4.6.3 libutils中的其他内容 82   4.7 Android系统进程 85   4.7.1 servicemanager 85   4.7.2 zygote 87   第5章 Android的Java虚拟机和Java环境 88   5.1 Dalvik虚拟机和核心库 88   5.1.1 dex工具库和虚拟机的实现 89   5.1.2 核心库 90   5.1.3 nativehelper库 91   5.2 Android的Java程序环境 91   5.2.1 Java类的层次结构 91   5.2.2 Android Java类的代码 92   5.2.3 Android系统API 92   5.3 JNI的使用 96   5.3.1 JNI的架构和实现方式 97   5.3.2 JNI的实现方式 97   5.3.3 在应用程序中使用JNI 99   5.4 系统服务的Java部分 101   5.4.1 Binder 102   5.4.2 ServiceManager 103   5.4.3 系统进程 103   第6章 Android的GUI系统 106   6.1 Android GUI系统综述 106   6.2 pixelflinger和libui库 108   6.2.1 pixelflinger 108   6.2.2 libui 108   6.2.3 输出/输入与硬件的接口 109   6.3 Surface系统 113   6.3.1 Surface系统本地接口 113   6.3.2 SurfaceFlinger本地代码 115   6.3.3 Surface的Java和JNI代码 119   6.4 Skia和2D图形系统 121   6.4.1 Skia底层库 121   6.4.2 Android图形系统的JNI接口 124   6.4.3 Android的图形包(graphics) 125   6.5 Android的OpenGL系统与3D图形系统 125   6.5.1 OpenGL的本地代码 125   6.5.2 OpenGL的JNI代码 130   6.5.3 OpenGL的Java类 130   第7章 Android的Audio系统 132   7.1 Audio系统综述 132   7.2 Audio系统和上层接口 134   7.2.1 Audio系统的各个层次 134   7.2.2 media库中的Audio框架部分 135   7.2.3 AudioFlinger本地代码 138   7.2.4 Audio系统的JNI代码 140   7.2.5 Audio系统的Java代码 142   7.3 Audio的硬件抽象层 142   7.3.1 Audio硬件抽象层的接口定义 142   7.3.2 AudioFlinger中自带Audio硬件抽象层实现 144   7.3.3 Audio硬件抽象层的真正实现 150   第8章 Android的Video输入输出系统 151   8.1 Video输入输出系统综述 151   8.1.1 Android的Overlay系统结构 152   8.1.2 Android的Camera系统结构 153   8.2 Overlay系统 155   8.2.1 Overlay系统的框架部分定义 156   8.2.2 SurfaceFlinger系统的Overlay部分 158   8.3 Overlay的硬件抽象层 161   8.3.1 Overlay系统硬件抽象层的接口 161   8.3.2 Overlay系统硬件实现框架 164   8.3.3 Overlay系统硬件实现的注意事项 166   8.4 Camera系统与上层接口 169   8.4.1 Camera本地代码框架 169   8.4.2 CameraService 176   8.4.3 Camera的JNI代码 179   8.4.4 Camera的Java代码 182   8.5 Camera的硬件抽象层 182   8.5.1 Camera硬件抽象层的接口定义 182   8.5.2 Camera硬件抽象层的桩实现 184   8.5.3 Camera硬件抽象层的硬件实现 188   第9章 Android的多媒体系统 190   9.1 Android多媒体系统的结构和业务 190   9.1.1 多媒体系统的宏观结构 190   9.1.2 多媒体的各种业务 192   9.2 多媒体系统的各个层次 199   9.2.1 libmedia的框架部分 199   9.2.2 多媒体服务 208   9.2.3 多媒体部分的JNI代码 213   9.2.4 多媒体部分的Java框架代码 215   9.2.5 android.widget.VideoView类 216   9.3 多媒体实现的核心部分OpenCore 216   9.3.1 OpenCore概述 216   9.3.2 OpenCore的层次结构 217   9.3.3 OpenCore的OSCL部分 219   9.3.4 OpenCore的文件格式和编解码部分 221   9.3.5 OpenCore 的Node 222   9.3.6 OpenCore 的功能扩展 223   9.3.7 OpenCore的 Player 226   9.3.8 OpenCore 的Author 236   第10章 Android的电话部分 243   10.1 Android电话部分综述 243   10.2 Modem驱动 243   10.3 本地的RIL代码 245   10.3.1 简介 245   10.3.2 RILD守护进程 246   10.3.3 libril库 247   10.3.4 RIL的实现库Reference RIL 247   10.3.5 Request(请求)流程 248   10.3.6 Response(响应)流程 249   10.3.7 RIL的移植工作 251   10.4 Java框架及应用 251   10.4.1 基本架构 252   10.4.2 呼叫 255   10.4.3 短信 256   10.4.4 数据连接 257   10.4.5 其他框架部分及其他应用 258   第11章 Android的连接部分 259   11.1 WiFi部分 259   11.1.1 WiFi基本架构 259   11.1.2 WiFi本地实现 260   11.1.3 WiFi的Java和JNI 263   11.1.4 Settings中的WiFi设置 265   11.1.5 WiFi工作流程实例 265   11.2 蓝牙部分 267   11.2.1 蓝牙基本架构 268   11.2.2 蓝牙用户空间库bluez 269   11.2.3 bluez适配层 272   11.2.4 蓝牙的JNI和Java部分 272   11.3 GPS和定位部分 280   11.3.1 定位系统基本架构 281   11.3.2 定位系统驱动层 281   11.3.3 GPS本地实现 282   11.3.4 GPS JNI实现 283   11.3.5 定位系统Java实现 284   第12章 Android的传感器系统 286   12.1 传感器系统综述 286   12.2 传感器系统层次结构 288   12.2.1 传感器系统的各个层次 288   12.2.2 传感器系统的JNI 288   12.2.3 传感器系统的Java代码 290   12.3 传感器系统的硬件抽象层 291   12.3.1 传感器系统硬件抽象层的接口定义 291   12.3.2 传感器系统硬件抽象层的示例实现 293   12.3.3 传感器系统硬件抽象层的实现要点 296   12.4 Sensor的使用 296

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值