作者归档:贺 利华

关于贺 利华

正在学习编程,享受编程 热爱文学,闲来读读《读库》 有思想,没理想 正在学会专注

Android手机真的那么难用吗?

首先声明一下,写这篇文章,我力求能客观的描述一下我自己在使用Android手机过程中遇到的各种问题,不爽以及比较爽的事情,只从手机正常用户的角度来说一说。

假期前,跟同事一起吃饭,同事跟我聊起来关于这个iPhone手机的各种优势(Android手机没有),因为这位同事之前一直使用的是Android手机(HTC Desire 欧版水货,搭配MIUI),在将近两年的时间里他的手机每次刷机都是我帮他完成的(此处做下说明,我自己使用的手机从未刷过机,我目前自己拥有两部Android手机,一部是HTC Legend,一部是Google Galaxy Nexus),在这两年的过程中我见过他的手机不断自己死机重启,抠电池(我们称这个为绝大部分Android手机的杀手锏,如同PC电脑的重启Reset,屡试不爽),因为手机存储空间的问题装不上应用而烦恼。

 

  1. 内存问题,在我使用HTC Legend的一年多时间里,我的手机死机过,抠过电池,因为内存空间太小我的手机目前只能装7个应用,不过目前我已经不再使用这款手机了,为何现在只能安装7个应用了,这个原因很简单,随着系统的进化升级,首先系统所占的空间会变大一些,其次整个系统生态的进化,开发者对应用使用的各种图素越来越多越来越精致,而且设备的多样性,让应用适配各种设备的需求也越来越多了,为了让应用能在所有设备上很好的得到适配,应用中会有多个分辨率的图素资源,也就让应用越来越大了。这样就会弄得用户最后只会在手机留下最需要的应用,一些平时不怎么用又占地方的无用软件就会被丢弃。当然随着整个时间的推移,你的手机会退役,会有新的机器来替代你之前的机器,目前我使用的Google Galaxy Nexus手机内置存储,也就意味着你能将所有的应用都直接安装到内置存储中,16G乃至32G的空间,足够你装一阵子的了,不过很多的朋友可能使用的手机还不是内置存储的,不过目前新的手机大部分ROM空间大小也都在512M左右了,对于正常使用来说肯定是没有任何问题了。
  2. 软件经常崩溃的问题,甚至系统自己都会崩溃(其实很多时候我们认为是系统崩溃了,实际上我们只是看到了手机的桌面程序崩溃重启了)。在我使用Android手机并且作为Android开发者的两年时间里,我碰到过无数软件崩溃的问题,QQ,微博,Google Earth, Google Maps等等都曾出现过,难道他们都应该被钉到耻辱柱上?软件崩溃很多时候都是因为程序员小的失误而造成的简单的NullPointer异常,在软件进化的过程中这些都是会变得越来越少,至少我的感觉是这样的。因为开发者在这个平台上开发的经验丰富了,做事情的时候能更多地为这个平台上的特性作出改变,最终提供一个更为流畅体验的软件。
  3. 软件体验差的问题,Android有成千上万的Rom,各自宣称自己是最人性化的Rom,拥有无与伦比的操作体验感,我作为开发者接触过的手机较多,也见识过不少的Rom,体验各有所长,甚至有些真的是非常贴心的设计,同样因为Android市场的开放性(开放到你在Play市场中都能下载安装百度应用这种类似于Play市场的应用程序),Android的应用程序在软件交互设计上各自发挥自己的想象,很多的东西不统一,不过作为一个用户,我倒是并不认为这有什么问题,因为每个软件解决的都是特定的问题,播放器就应该能全屏,拨号器就应该默认打开9宫格键盘,我在日常生活中常用的软件不超过10个,对于每个软件的操作方式我还是能轻松的应付的(当然这又可能又涉及到“还有很多的非专业用户”这样的问题,这关我屁事啊,我只是一个用户恰好同时是个专业人员而且,我只能保证我自己做的东西不让大家用起来费解)。
  4. 关于系统和硬件的一些问题,例如Android手机在通话过程中无法使用移动网络(貌似可以使用WIFI,没有求证过),例如插入耳机之后,如果耳机根本没有话筒那么在通电话的时候你只能听,系统不会判断这个耳机是否能输入语音,而是直接阻塞了话筒的语音输入,例如手机的系统各种落后,Google每次高调宣布新系统发布,一大群Android用户要么集体唏嘘,要么就是开始混迹各大论坛等适合自己手机的Rom出现,赶紧刷上感受一把,尼玛确实坑爹啊。不过我作为一个开发者我每天正常使用的手机从未刷过机,不是因为担心保修的问题(因为我买的手机都是水货,保修根本就没有任何保证),但是我担心的问题是Rom给我带来的问题远超过能带给我的便利,我只能相信我买的手机的生产厂家才能完美适配他们自己手机的硬件驱动等等一系列的东西,根本不是某些大牛改改资源删删文本就能做到的。
  5. 价格的问题,我不只一次听到很多的人说“我也想买个iPhone”这样的话,可是最终出手的人委实不多,为何?价格摆在那里,我们可以称Android是为屌丝们意淫白富美的利器,但是现实确实是很多人还无法承担一部手机价格超过4000这样的消费。这些人如何来满足自己想通过手机来达成自己娱乐和通信的双重需求呢?厂家们很清楚这个市场的潜力,他们推出众多的低端机,中低端机,中端机,中高端机,高端机,原本就是为了不同的人群而设定的,Android中的高端机价格远超一部iPhone 4S 32G版本的价格,依然能在市场上有所作为,这不是那些人傻,也不是那些人没有用过iPhone不了解iPhone的优点,而是因为他们有某些需求被这些机器满足了,而iPhone没法做到。
  6. 电池的问题,我非常痛恨我的手机总得让我带着移动电源出门,我不是一个手机控,我不会在地铁上和公车上不断地把玩自己的手机,我不会周末在家躺在沙发上刷微博,可是我的手机从未正常在一天24小时内保持坚挺,我每天到了公司就必须把手机接上充电器(当然因为我是开发者,即便不充电也是需要调试的),每天晚上手机都是满电回到家中,第二天早上回到公司,手机又必须赶紧开始续上电,智能手机的屏越来越大,用起来越来越爽(当然小手的同鞋会因为抓不住手机,导致手机经常摔到地上,我就是其中之一),可是你的电肿么办?目前无解,要么多带电板,否则携带移动电源。
  7. 信号的问题,常常听到人说“买个iPhone用移动的2G卡,网络好慢”,我倒是觉得这个跟什么手机什么系统关系不大,主要还是运营商网络的问题,不过就我自己使用的情况来看,Android手机的信号真的不算出众,相对于之前使用的非智能机,我依然认为我的非智能机在使用同一张卡的时候表现良好,而目前的机器稍显欠佳,手机我依然最信赖诺基亚制造。
  8. 格调问题,很多人都曾说过“果粉很在意他身边的人是否也在使用苹果设备,如果使用的话,希望知道对方使用的是哪款设备”,其实我想很多人都会有这样的潜意识,希望自己的选择能够得到认可,如果因为你买了一部手机,公司其他的同事都买了同一部手机,我想大部分人都会感觉良好(我也会)。不过关于屌丝和白富美这样的对比,我倒是觉得大可不必,手机如今确实不只是工具,更像是手表这般的物件(能体现自己的品味和格调),那么Android代表的是什么呢?我确实不知道,因为我自己确实可以被归到屌丝之中,但是我并不希望Android成为一个屌丝代名词,可能也没有多少人这么想(我自己也没有这么想),我对于Android没有过多的热爱之情,他只是手机的操作系统而已,我喜欢它是因为我了解它,因为它的开源让我作为一个开发者能一窥它那庞大的代码组织是如何造就了这么一个成功的生态,让我能拜读到很多成功而美丽的代码。我想我的格调还是会因为Android手机的存在产生一些变化,不过我倒是更相信我个人的格调能改变我身边的人对Android的看法。
  9. 应用的问题,很多时候我们会听到“Android没有那么好用的应用啊”类似的说法,确实如此,Android整个生态中确实少见到非常精品的应用和游戏,原因是什么呢?这个显然还是取决于众多企业在移动平台上布局的策略,跟某个系统的关系实在太小了。国内有不少的团队声称他们只做iOS市场,说iOS投资回报率高,在Android生态中,你无法赚到钱,所以只能选择iOS,所以还是利益驱使的原因,现在Android用户越来越多,我想这些曾经这么说的人,除非他们真可以只依赖iOS就能生存下来,否则他们早晚还是会重新认识Android整个生态带来的一些改变的。就我个人而言,我还真没有发现有什么应用程序是某个平台独享,然后缺了这个应用会对我造成什么障碍,或者说有某个平台独享应用能提升效率,而在Android上我会因为没有这个工具效率就较低(当然,这只适用于我个人)。
  10. 用户自由度的问题,我想这个问题就不说了,因为说也等于没说,有人觉得不需要给用户很多的选择(我就是这么想的),有人觉得用户有权利选择管理自己的手机(通过各种方法),虽然我很喜欢Android的自由管理方式,因为我是开发者我能完全理解Android的存储(我保证有一大堆人其实是不太明白的,但是他们可能也能使用得挺好的),而像iTunes这样的产品会让诸多普通用户用得非常之爽。所以这个还是回到习惯的问题,很多人都有使用U盘的经验,所以大部分人在使用Android进行存储的时候没有任何困难,而使用iTunes确实需要学习,我身边的同事就有很多用iTunes会碰到很多问题不知道怎么折腾(请注意不是某些困难问题,而只是简单需求,需要学习)。
  11. Google的问题,因为我自己是Google控,Android天生的优势在这个方面还是存在的,虽然Google总是会兼顾绝大部分的用户,会在推出新版本的时候保证多平台同步更新,在Android平台使用Google的产品全然没有障碍,特别是有了Chrome之后,生活更加美好了。

以上是我作为一个用户(同时因为我是开发者,也许我的高度会稍微高一些)对Android的一些看法,对于我个人而言,Android不算难用,但是他好用吗?不好用,因为我很多时候都会因为电量的问题而无法使用,这简直就是坑爹。至于Android能给你带来什么,我想说的是,你会期望一个工具给你带来什么的话,那么恐怕你还需要很多很多工具来给你带来存在感,因为能给你提供存在感的东西实在太多了,数不胜数,手机绝对不是最合适给你带来存在感的。但是因为我是开发者,我的手机能给我带来存在感,但是我依然没有觉得选择一部多么高端或者精美的手机能给我带来什么强烈的感觉,于我而言,如果能拥有一部速度不让我恶心等待,电量不让我无法使用,网络信号不让我难以顺畅通话,屏幕可以用来看电子书的手机,已经是非常好的一款设备了。

Ubuntu下让ADB识别所有设备的两个方法

方法一,完全参考谷歌的文档,内容如下
在/etc/udev/rules.d目录下新建一个51-android.rules文件,将下面所有内容拷贝到该文件中:

SUBSYSTEM=="usb", ATTR{idVender}=="0502", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="0b05", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="413c", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="0489", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="04c5", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="091e", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="18d1", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="109b", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="0bb4", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="12d1", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="24e3", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="2116", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="0482", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="17ef", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="1004", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="22b8", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="0409", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="2080", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="0955", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="2257", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="10a9", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="1d4d", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="0471", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="04da", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="05c6", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="1f53", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="04e8", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="04dd", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="054c", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="0fce", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="2340", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="0930", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVender}=="19d2", MODE="0666", GROUP="plugdev"

方法二,有点Hack的味道,参考“孔雀的小屋”滴,就是将下面这行内容拷贝至/etc/udev/rules.d/51-android.rules文件中,
通杀所有Android设备,上面的那些都可作为浮云

SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", MODE="0666", GROUP="plugdev"

2011年不完全回顾

2011年是史无前例的一年,也是全然新鲜的一年,史上没有哪一年跟2011年一般,因为历史上只会有一个2011年,我这一辈子中也只有一个2011年,所以我对每年的态度都是一样的,当你们在我死命地拽着你的尾巴的时候,你毅然决然地离开了我,我当然还会是以前那句话“2011,我草你大爷的,我他妈真怀念你”。

2012已经快尼玛过去半个月了,我终于能腾出手来写点东西了(其实这么说真他妈的矫情,感觉我很忙一样的),其实我真不算忙,因为我每天还能有半个多小时能刷刷腾讯微博,能跟同事蛋逼半个小时,吃中饭晚饭还尼玛要费去俩小时,拉屎睡觉费掉的时间也不少,在虾米上听歌耗掉的时间就更多了,其实每天我大部分的时间都是在搞毛线,只有极少数的时间是全心全意地在工作,工作绝大部分的时间是在写代码。

2011是一个特殊的一年,因为这一年我有了一些角色的小小改变,以前自己一个人写代码,只需要自己搞定自己的事情就OK了,2011年我们的郭英同学加入了Android团队,从完全不熟悉Java到如今能完全独立地完成一个功能,我们的郭英同学进步不算慢,虽然郭英童鞋的代码还有很多不成熟的地方,例如:。。。,还是不要说啦,其实这些问题大家都尼玛有,慢慢地会好的,郭英童鞋,我看好你哦!

2011年,喜讯天天从0.4.0 Beta 到 喜讯天天0.6.2 Beta,画说Android版本从0.1.0 Beta 到 0.2.6 Beta,经历了21个版本的发布,平均一个月1.75个版本,工作强度自然不消说了,因为喜讯天天和画说Android版本0.1.0 Beta到0.1.12 Beta版本我是Android客户端唯一的程序员,郭英的加入确实很大程度上减轻了客户端开发的工作量,这可以算是2011年公司的一大收获,因为郭英童鞋非常非常有自己的想法,而且喜欢钻研东西,不过好像郭英童鞋对于客户端编程不是特别感冒,跟我当年的情况比较相似,对自己目前从事的技术总是不能提起十二分兴趣,对于其他的一些新技术或者对自己来说很新鲜的东西倒是非常非常的感兴趣,关键是郭英童鞋还是个文艺青年,受不鸟啊,技术组经常被他hold住,木有人人能接上话啊,有木有???来首古诗尼玛谁能接上啊,有木有???

Android开发中碰到了很多的问题,有一些解决了,有一些灭有(这是废话),跟设计一起的合作更顺利了,也开始在推动一些事情了,不过效果不好,可能是自己主动性还不够吧,再接再厉咯。

o(︶︿︶)o 唉,写到这里吧,刘琪,你大爷,赶紧下班回去啦,哥等你等得都犯困了,你妹啊~

健康,别走

曾几何时,我也曾挥洒泪水在湘江之畔岳麓山下中南大学的塑胶绿茵场,场场满场跑(其实我只是一个后卫啦)得气喘吁吁,大汗淋漓得如此痛快,青春已经渐渐褪去,生活的担子慢慢地沉重了起来,生活尼玛才不管你丫的有木有做好承担的准备,你丫能不能接住,Who care ?

离开校园两年多了,常常会想念校园生活中的点点滴滴,有宿舍的深夜扯淡,有男生扎堆的黄色笑话交流,有窝着看的AV,更有年轻时无所畏惧的强健体魄。成为办公室马铃薯的两年间,自己的产出甚微,在超图待了一年多的时间,正式进入到项目一年,写了一些代码改了一些Bug,算是给SuperMap Objects Java 6R贡献了一些些数据转换模块的代码和自定义控件的代码,至于平时的修修补补工作更是不值一提啦。来到喜讯一年多了,还记得自己是2010年5月18日,正式来喜讯上班,当时我是2010年5月17日从北京超图软件股份有限公司研发中心离职,立马来到喜讯入职。期间没有离职休假,也没有旅行,直接的工作状态切换,非常干脆,看起来就想是移花接木般的直接嫁接。

在喜讯的一年多时间里,经历了几个产品,从《黄金矿工》开始,在《喜讯分享》中跌倒了,在《喜讯天天》中历练,在《画说》中成长,如今依然在《画说》项目中前进,项目中也进来了新的同事,开始能分担一些工作了。伴着自己职业道路的继续,在自己编码素养上的提升是最为明显的,明显得犹如自己日益加粗的水桶腰,明显得犹如自己由每周剧烈运动5小时以上剧减到每周轻微运动时间不足一小时,生活带给你的改变其实还远远不只这些你能看到的。

去年,歪歪的母亲,我们一群人可亲可爱的歪歪伯母检查出有肺癌,当时就几乎击倒了我们一贯坚强的歪歪,不过还好发现得较早,目前已经完全治疗完毕,伯母的身体也渐渐恢复了,只是不似以前那般健朗了,如今歪歪刚刚添了一个千金,明年春节回老家摆喜酒,希望这些喜庆能给我们可亲的伯母带来更多的欢乐,而忘却曾今的病痛吧。

今年年中,听刘琪说起他的一个发小,家中突生变故,父母双方都被查出有重病,父亲更是癌症,母亲的病也需要做脑科大手术才可能完全康复,他的发小还是一个军医,家中境况也才是刚刚才装好,突生变故将全家人打入冷窟,家中的担子差点就要将他压垮,幸好他母亲的手术十分成功,目前应该已经完全康复痊愈,作为一个旁观者,我只能唏嘘感叹和祝福祈愿。

我们的ZZM这两天也在发愁,母亲查出重病需要钱来动手术,家中目前境况又不太好,只好四处借款,我们这些刚刚毕业不久的又穷又苦的大学同学义不容辞地需要伸出自己的手,尼玛能帮多少是多少啦,可怜的我才发现自己从毕业到现在,身上从来都没有一分钱积蓄,总是一年一年光一月一月光,从未给过家中半毛钱,自己依然过得不能算是萧条落魄,但也是将就将就得过且过。如今他人需要,自己却很是无奈的表示自己只能力所能及,“艹,这尼玛就像吃了个苍蝇一样,you know ?”。

看到刘琪、ZZM的QQ签名都提到了健康二字,突然发现自己两年多以来,我们不求物质上有太多的收获,当然也不可能有太多的收获,在职业道路上的成长其实究竟有多大,还待积年之后再回首吧,但是我们失去的其实已经可以看到了。今天去慈铭体检中心体检,最后做了一项彩超,医生告诉我左肾囊肿,当时我也没怎么在意,也不太明白那是个什么概念,也没有主动问一下医生需要做些什么,医生可能看我也比较二逼,竟然不发问,也就做了闷葫芦不说话,完了我就提裤子出来了。回到办公室,发布完版本之后,简单查了一下,发现肾囊肿倒并不是非常严重的问题,心中悬着的石头也就下来了,等体检结果出来之后到时候再去医院确诊一下。

连续的一些事情让我开始意识到自己的健康已经慢慢地在溜走,我热爱生命犹如我每天都要吃的米饭,犹如我每天要呼吸的空气(虽然我痛恨北京的空气),犹如我每天要喝的水(其实北京的水也很难喝啦),我当然热爱我的健康,拥有一个健康的身体对于任何一个人来讲,那都是一件如此令人幸福的事情,要让自己成为一个幸福的人,我想我们是需要一些行动的,可是我们究竟要如何开始行动呢?其实我也不知道,不过既然已经意识到了,那么我想我还是需要想办法来避免这个问题的。身体是自己的,谁都给你挣不来,你可以挣到各种东西,也可以挣到健康,恰当地舍弃一些吧,合适地追求一些吧,健康其实并不讨厌我,我也很爱健康。

Android中解决图像解码导致的OOM问题

在上一篇博文Android Bitmap内存限制中我们详细的了解并分析了Android为什么会在Decode Bitmap的时候出现OOM错误,简单的讲就是Android在解码图片的时候使用了本地代码来完成解码的操作,但是使用的内存是堆里面的内存,而堆内存的大小是收VM实例可用内存大小的限制的,所以当应用程序可用内存已经无法再满足解码的需要时,Android将抛出OOM错误。

这里讲一个题外话,也就是为何Android要限制每个应用程序的可用内存大小呢?其实这个问题可能有多方面的解答,目前我自己考虑到的有两点:

  1. 使得内存的使用更为合理,限制每个应用的可用内存上限,可以防止某些应用程序恶意或者无意使用过多的内存,而导致其他应用无法正常运行,我们众所周知的Android是有多进程的,如果一个进程(也就是一个应用)耗费过多的内存,其他的应用还搞毛呢?当然在这里其实是有一个例外,那就是如果你的应用使用了很多本地代码,在本地代码中创建对象解码图像是不会被计算到的,这是因为你使用本地方法创建的对象或者解码的图像使用的是本地堆的内存,跟系统是平级的,而我们通过Framework调用BitmapFactory.decodeFile()方法解码时,系统虽然也是调用本地代码来进行解码的,但是Android Framework在实现的时候,刻意地将这部分解码使用的内存从堆里面分配了而不是从本地堆里分配的内存,所以才会出现OOM,当然并不是说从本地堆里分配就不会出现OOM,本地堆分配内存超过系统可用内存限制的话,通常都是直接崩溃,什么错误可能都看不到,也许会有一些崩溃的错误字节码之类的。
  2. 省电的考虑,呃…,原因我好像也不能很明白地说出来。

回到正题来,我们在应用的设计和开发中可能会经常碰到需要在一个界面上显示数十张图片乃至上百张,当然限于手机屏幕的大小我们通常在设计中会使用类似于列表或者网格的控件来展示,也就是说通常一次需要显示出来图片数还是一个相对确定的数字,通常也不会太大。如果数目比较大的画,通常显示的控件自身尺寸就会比较小,这个时候可以采用缩略图策略。下面我们来看看如果避免出现OOM的错误,这个解决方案参考了Android示范程序XML Adapters中的ImageDownloader.java中的实现,主要是使用了一个二级缓存类似的机制,就是有一个数据结构中直接持有解码成功的Bitmap对象引用,同时使用一个二级缓存数据结构持有解码成功的Bitmap对象的SoftReference对象,由于SoftReference对象的特殊性,系统会在需要内存的时候首先将SoftReference对象持有的对象释放掉,也就是说当VM发现可用内存比较少了需要触发GC的时候,就会优先将二级缓存中的Bitmap回收,而保有一级缓存中的Bitmap对象用于显示。

其实这个解决方案最为关键的一点是使用了一个比较合适的数据结构,那就是LinkedHashMap类型来进行一级缓存Bitmap的容器,由于LinkedHashMap的特殊性,我们可以控制其内部存储对象的个数并且将不再使用的对象从容器中移除,这就给二级缓存提供了可能性,我们可以在一级缓存中一直保存最近被访问到的Bitmap对象,而已经被访问过的图片在LinkedHashMap的容量超过我们预设值时将会把容器中存在时间最长的对象移除,这个时候我们可以将被移除出LinkedHashMap中的对象存放至二级缓存容器中,而二级缓存中对象的管理就交给系统来做了,当系统需要GC时就会首先回收二级缓存容器中的Bitmap对象了。在获取对象的时候先从一级缓存容器中查找,如果有对应对象并可用直接返回,如果没有的话从二级缓存中查找对应的SoftReference对象,判断SoftReference对象持有的Bitmap是否可用,可用直接返回,否则返回空。

主要的代码段如下:

private static final int HARD_CACHE_CAPACITY = 16;

// Hard cache, with a fixed maximum capacity and a life duration
private static final HashMap<String, Bitmap> sHardBitmapCache = new LinkedHashMap<String, Bitmap>(HARD_CACHE_CAPACITY / 2, 0.75f, true) {
    private static final long serialVersionUID = -57738079457331894L;

    @Override
    protected boolean removeEldestEntry(LinkedHashMap.Entry<String, Bitmap> eldest) {
        if (size() > HARD_CACHE_CAPACITY) {
            // Entries push-out of hard reference cache are transferred to soft reference cache
            sSoftBitmapCache.put(eldest.getKey(), new SoftReference<Bitmap>(eldest.getValue()));
            return true;
        } else
            return false;
    }
};

// Soft cache for bitmap kicked out of hard cache
private final static ConcurrentHashMap<String, SoftReference<Bitmap>> sSoftBitmapCache = new ConcurrentHashMap<String, SoftReference<Bitmap>>(HARD_CACHE_CAPACITY / 2);

/**
* @param id
*            The ID of the image that will be retrieved from the cache.
* @return The cached bitmap or null if it was not found.
*/
public Bitmap getBitmap(String id) {
    // First try the hard reference cache
    synchronized (sHardBitmapCache) {
        final Bitmap bitmap = sHardBitmapCache.get(id);
        if (bitmap != null) {
            // Bitmap found in hard cache
            // Move element to first position, so that it is removed last
            sHardBitmapCache.remove(id);
            sHardBitmapCache.put(id, bitmap);
            return bitmap;
        }
    }

    // Then try the soft reference cache
    SoftReference<Bitmap> bitmapReference = sSoftBitmapCache.get(id);
    if (bitmapReference != null) {
        final Bitmap bitmap = bitmapReference.get();
        if (bitmap != null) {
            // Bitmap found in soft cache
            return bitmap;
        } else {
            // Soft reference has been Garbage Collected
            sSoftBitmapCache.remove(id);
        }
    }

    return null;
}

public void putBitmap(String id, Bitmap bitmap) {
    synchronized (sHardBitmapCache) {
        if (sHardBitmapCache != null) {
            sHardBitmapCache.put(id, bitmap);
        }
    }
}

上面这段代码中使用了id来标识一个Bitmap对象,这个可能大家在实际的应用中可以选择不同的方式来索引Bitmap对象,图像的解码在这里就不做赘述了。这里主要讨论的就是如何管理Bitmap对象,使得在实际应用中不要轻易出现OOM错误,其实在这个解决方案中,HARD_CACHE_CAPACITY的值就是一个经验值,而且这个跟每个应用中需要解码的图片的实际大小直接相关,如果图片偏大的话可能这个值还得调小,如果图片本身比较小的话可以适当的调大一些。本解决方案主要讨论的是一种双缓存结合使用SoftReference的机制,通过使用二级缓存和系统对SoftReference对象的回收特性,让系统自动回收不再敏感的图片Bitmap对象,而保有一级缓存也就是敏感的图片Bitmap对象。