惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
Darknet – Hacking Tools, Hacker News & Cyber Security
Jina AI
Jina AI
博客园_首页
J
Java Code Geeks
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 司徒正美
Hugging Face - Blog
Hugging Face - Blog
S
SegmentFault 最新的问题
MyScale Blog
MyScale Blog
P
Proofpoint News Feed
L
Lohrmann on Cybersecurity
Forbes - Security
Forbes - Security
大猫的无限游戏
大猫的无限游戏
Vercel News
Vercel News
Y
Y Combinator Blog
Google DeepMind News
Google DeepMind News
The Register - Security
The Register - Security
N
News | PayPal Newsroom
S
Security Archives - TechRepublic
量子位
Cisco Talos Blog
Cisco Talos Blog
V
V2EX
C
Cisco Blogs
The Cloudflare Blog
Stack Overflow Blog
Stack Overflow Blog
L
LangChain Blog
Scott Helme
Scott Helme
S
Securelist
Security Latest
Security Latest
爱范儿
爱范儿
TaoSecurity Blog
TaoSecurity Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
I
Intezer
L
LINUX DO - 最新话题
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
C
Check Point Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
美团技术团队
Know Your Adversary
Know Your Adversary
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
PCI Perspectives
PCI Perspectives
月光博客
月光博客
T
Tailwind CSS Blog
Cloudbric
Cloudbric
小众软件
小众软件
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
K
Kaspersky official blog
D
DataBreaches.Net
博客园 - 【当耐特】
有赞技术团队
有赞技术团队

博客园 - duguguiyu

『Android开发精要』推荐 深入Android【八】 —— Activity间数据传输 深入Android 【六】 —— 界面构造 深入Android 【五】 —— 任务和进程 深入Android 【四】 —— 组件调用 深入Android 【三】 —— 组件入门 深入Android 【二】 —— 架构和学习 深入Android 【一】 —— 序及开篇 Symbian手记【五】 —— Symbian的异步框架 Symbian手记【四】 —— Symbian的容器 Symbian手记【三】 —— Symbian的描述符 Symbian手记【二】 —— Symbian对象构造 Symbian手记【一】 —— Symbian命名法 分布式基础学习【二】 —— 分布式计算系统(Map/Reduce) 分布式基础学习【一】 —— 分布式文件系统 Chrome源码剖析 【五】 Chrome源码剖析 【四】 Chrome源码剖析【三】 Chrome源码剖析【二】
深入Android【七】 —— 资源文件
duguguiyu · 2010-05-02 · via 博客园 - duguguiyu

资源文件

作为一枚coder,做界面,很多时候都是一场梦魇。很多时候,我们会感觉对于底层逻辑实现的很有把握性,哪怕需求一直在变,也可以通过不断的重构一直跟进,一切尽在掌握。但遭遇界面,往往就不再如此,它的好坏总是和审美、体验之类的词汇扯在一起,在凤姐芙蓉出没的年头,谈审美成为一件恐怖的事情。你可能会被要求不停的改代码,就为了移动一个像素,调整一枚按钮,琐碎而无聊。
为了改变这样的状况,挽救coder们于水生活热之中,很多开发平台,都采用了类似于资源文件的解决方案。此类方案的基本思想是,将界面的实现与底层逻辑的实现完全剥离开来,用资源文件这样的东西来描述界面。资源文件的描述语言,往往是结构化很强,比如Html,Xml(及其变形体)之类的。于开发语言相比,此类语言逻辑性较弱但结构更好可读性更强更容易理解,并对自动化工具非常友好,可以于界面的拖拽配置结合的更加完美。这样的剥离,可以是的底层逻辑和上层界面独立变化,甚至不同的人员开发(这一点在web开发上表现的应该很明显...),两者之间的耦合性非常的小,coder们的负担,陡然减少(好吧,一个很挫的资源架构也会额外增加开发人员的负担,Symbian同学,请不要对号入座...)。

结构和格式

Android的资源文件,是由目录结构Xml格式的文件,和纯数据文件构成。从格式上来看,无疑,学习门槛非常低。Xml作为coder们的瑞士军刀,哪怕使不习惯,弄得清楚并会用至少是没有问题。从配套的工具来看,Android的ADT,提供了一套可视化的配置工具,说不上特别好用,但至少是差强人意能凑合着用,比不上iPhone的,调戏Symbian还是没有问题的[强档广告首播:有道词典 for iPhone新版火热上线,增加了超强单词本功能,特有的触电式颤抖单词切换功能,让你欲罢不能,持有相关设备的童鞋不要犹豫,一拥而上吧...]。
Android的资源文件,覆盖面超级广,只要是和界面相关的,都可以用资源文件表示,比如:UI的样式,菜单,配置文件,各种描述性字符串,图片,音频视频文件,动画,颜色,尺寸,风格和样式,等等等。所有的资源文件(不考虑asset,它和讨论暂无关联...),都放在res目录下,不同类别的资源,需要放置在不同的特定名称的子文件夹中,或者是写在特定文件名的文件中(或者ms不是必须的,但,不用在这里特立独行,寻章办事也挺好...)。比如,所有作为UI背景之类的图片,都需要扔在drawable这类的文件夹中,所有字符串相关的,都会放到values目录下形如strings.xml这样的文件中(如下图所示,是一个资源文件目录结构的截图...)。

每个xml文件,都有一定的约定。比如一个字符串,会放在<string></string>这样的xml element中(如下图所示...),你可以通过eclipse的ADT插件提供的可是界面去填而不关注具体规范,也可以直接人肉打造,前者对于新手来说更为直观,后者对于老鸟而言更为迅捷。

可配置性

程序逻辑总是不变应万变的,但界面往往是需要能够72变。首先一种变化因素,就是状态。想象一下,我们往往会有这样类似的需求,一个按钮,我们需要没有按下去的时候是一种背景,按的过程中刷的变成另一副模样,当它可用的时候需要鲜鲜亮的一个样子,不可用的时候最好是灰不溜秋没人愿点的怂样,诸如此类。传统编程模型下(Symbian,哥叫你出来当模特...),我们总是需要不厌其烦的用代码控制这样的事情。监听不同的事件,见缝插针的切换背景,并祈祷上天,千万别让哥调整,否则哥和你没完。


在Android中,做这个事情,变得简单许多,通过预设的一些Xml属性,能够轻松的搞定。如上图所示,是Radio Button的背景。通过搭配不同的属性,就可以自动转换背景。比如第一个<item>,说的是当Radio Button被选中,并且具有焦点的时候,显示btn_radio_on这幅图片,而最后一个<item>,说的是前述条件都不满足,并且处于选中状态,那么显示btn_radio_on这幅图片。

另外一个更易变的因素,就是手机硬件/软件环境了,毕竟,不是家家都是苹果,一个平台搭一款手机,手机款形多样化,几乎是避免不了的问题。没有人希望自己做的软件在大屏幕手机上闪亮光鲜,换个小屏幕就惨不忍睹,竖屏看像那么回事横屏看就挤做一团。还有就是语言环境了,做为一个有国际眼光的coder,作面向世界的NB软件是咱的梦想,但我们不能因为自己的梦想逼迫大家都去学中文,做一款软件可以根据手机的语言环境选择最合适展示的语言,很多时候,是一个需要具备的功能点。
在Android中,实现这些,都是举手之劳。方法就是将和环境相关的资源,放入特定名称的文件夹中。比如,表示简体中文字符信息的资源,可以放到values-zh-rCN中去,当系统语言环境为简体中文时,就会呈现出中文的字符信息。在Android中,很多相关配置项,都可以按照这样的方式参与到资源自适应的活动中来,包括屏幕大小,屏幕朝向,屏幕分辨率,语言环境,触屏类型,SDK版本等等。系统会给所有配置项一个优先级(或者说权重,次序之类的),当用户提供了多份资源的时候,系统会根据优先级从高到底淘汰备选资源,如果淘汰仅剩了一个,那就是最符合当前系统软硬件语言环境的资源项,如果一个不剩,择启用默认项(最是形如values这样没有任何尾巴目录中的资源...)。因此,默认的资源是非常重要的,它必须是其他所有可选资源项的超集,否则在资源选择失败的情况下,应用会凄凉的崩溃。

R类

在使用资源后,界面逻辑与底层逻辑的耦合被降低了,但这不意味着,两者没有关联了。比如,需要为某个按钮增加一个点击事件,就需要定位到所需的那个按钮;再比如,你需要使用某个字符串资源,通知用户某件事情,就需要能定位到资源中放置的该字串。

最显而易见的一种方式,就是通过字符串比较,用名字信息在资源的xml描述文件中定位到所需的内容,加载并使用。这种方式,解决了查找的问题,但反复的字符串比较,势必带来严重的效率隐患。因此,在Android中,类似于Symbian的方法,引入了一个R类。

它的基本思想是,通过增加一个额外的编译器,为所有的资源项,都赋予一个32位的整形数来表示,同一个资源像的不同配置,都使用同一个id。这个整形数,就相当于这个资源项的门牌号码,能够帮助定位到对应的资源项。所有的这些整形数,都以常量的方式,整合到一个Java类中,这个类就是R类。这样,在程序中,就可以通过使用这个R类,来查找所需的资源,这就将字符串比较,简化成了一个整形数的比较,大大的节约了开销。
不得不说,这整套逻辑和Symbian中的资源文件预编译一致。但两者很不同的点在于Symbian中的整形数,代表的是一个二进制流的偏移量,资源中的内容在编译时决定了。而Android中的整形数,是一个有逻辑意义的数值,它表达了这个资源所处的资源包,类别,和脚标,它的具体内容在运行时才确定,这使得它的灵活性大大增强,付出的则是一定的效率代价

实现

按照惯例,还是要说实现的,以一个查找流程为示例。当在Activity中需要使用字符串的,会调用它的getString方法,传入R.stirng.xxx的一个整形数,换取一个符合当前机器环境配置的字符串。

getString,追根溯源,来到AssetManager类中。Asset类,其实是一个空壳,它仅仅是提供了一些便利的接口,而将请求,通过JNI的接口,传入到了底层C++实现的类库中。

在底层的实现,主要是在C++实现的,AssetManager,ResourceTypes等等之中。其中:

  • JNI文件在:framework/base/core/jni
  • 头文件在:framework/base/include/utils
  • CPP文件在:framework/base/libs/utils

具体实现,和前述的算法逻辑是一致的。每一个资源的id,32位,高8位表示资源包,低16位用于描述脚标,中间8位,用来说明类别。所有资源中的文件,都被预处理了,放入到了一系列的队列和表中,通过id,可以查到具体的位置。然后根据缓存的环境设置对象,跑一次淘汰算法,获得匹配的资源对象的对应文件和偏移量。然后将值读取出来,通过JNI接口,拷贝回去。

以上这些描述,并不能帮助了解真实的实现细节,主要是为了促使大家对读取资源的效率有一个比较直观的认知。整个资源读取的流程比较长,但是实现在C++中,可以预想,效率比Java高一些,开发人员,应该能够根据自己的需求,决定是否将内容写入资源文件中(还是写在代码中...),是不是需要自己稍微缓存一下,诸如此类。 

========================================

申请了自己的空间和域名,总算有了家的感觉,有兴趣的可以移步flyvenus.net,会同步更新相关内容,并提供更好的阅读体验。

========================================