Android应用开发之Android照片墙应用技巧
白羽 2018-12-19 来源 :网络 阅读 218 评论 0

摘要:本文将带你了解Android应用开发Android照片墙应用技巧,希望本文对大家学Android有所帮助。

    本文将带你了解Android应用开发Android照片墙应用技巧,希望本文对大家学Android有所帮助。


        

Android照片墙应用技巧。

照片墙应用介绍

应用中需要显示大量的图片,图片从网络获取。

应用有两个问题:

流畅地显示

省流量

流畅性是所有应用都必须要有的,图片要从网上下载,下载过程必须放到工作线程当中来解决。另外图片也有可以有一部分保存在内存当中,直接从内存当中获取并显示,这才是最快的方式。

图片有很多,用户理论上只用下载一遍图片就行了,不能重复去下载,缓存可以帮助用户节省流量。

缓存使用思路

Android 缓存原理一文中说明了缓存的原理以及使用方式,如有不明白可参考此文。

其实缓存思路特别简单,在需要使用图片的时候,先查内存缓存,如果没有再查硬盘缓存,还没有则去网络下载,下载完毕后加入硬盘缓存以及内存缓存,以备下次再用。缓存是不是已满,该如何删除交给缓存工具类决定,但大体思路一定是这样。

本文中也是这么做的,而且硬盘缓存读取相当于IO过程,较缓慢,也可以放到工作线程当中,于是就可以把查内存缓存以外的所有操作都放到工作线程当中完成。

照片墙的核心代码如下:

privatevoidloadBitmaps(ImageViewimageView,Stringurl){

try{

Bitmapbitmap=getBitmapFromMemoryCache(url);

if(bitmap==null){

Tasktask=newTask(imageView,url);

mPools.submit(task);

}else{

if(imageView!=null&&bitmap!=null){

imageView.setImageBitmap(bitmap);

}

}

}catch(Exceptione){

//TODO:handleexception

}

}

如果从内存缓存中无法获取到图片,则向线程池中提交一个任务,在线程池中完成图片的获取。

classTaskimplementsRunnable{

ImageViewiv;

StringimageUrl;

Task(ImageViewview,Stringurl){

iv=view;

imageUrl=url;

}

@Override

publicvoidrun(){

FileDescriptorfileDescriptor=null;

FileInputStreamfileInputStream=null;

Snapshotsnapshot=null;

try{

finalStringkey=hashKeyForDisk(imageUrl);

snapshot=mDiskLruCache.get(key);

if(snapshot==null){

Editoreditor=mDiskLruCache.edit(key);

if(editor!=null){

OutputStreamoutputStream=editor.newOutputStream(0);

if(downloadUrlToStream(imageUrl,outputStream)){

editor.commit();

}else{

editor.abort();

}

}

snapshot=mDiskLruCache.get(key);

}

if(snapshot!=null){

fileInputStream=(FileInputStream)snapshot

.getInputStream(0);

fileDescriptor=fileInputStream.getFD();

}

Bitmapbitmap=null;

if(fileDescriptor!=null){

bitmap=BitmapFactory.decodeFileDescriptor(fileDescriptor);

}

if(bitmap!=null){

addBitmapToMemoryCache(key,bitmap);

Resultresult=newResult(iv,bitmap,imageUrl);

Messagemsg=Message.obtain(mHandler,MSG_SHOW_BITMAP,result);

msg.sendToTarget();

}

}catch(Exceptione){

Log.e("okunu","run",e);

}

}

}

和前文所说思路一样,先从硬盘缓存中读取,如果没有再从网络中下载图片。一定不能忘记将图片添加进入硬盘缓存和内存缓存中来,这一步非常重要。

由于硬盘缓存的使用方法,在从网上下载图片的时候,是直接下载到缓存文件当中的,而不是先下载再复制一份到硬盘缓存当中,因为这样可以节省一次IO过程。

另外还有一些小细节,比如最后如何刷新界面,首先在工作线程中是无法刷新UI的,所以在此处用handler,将结果返回主线程中处理。

if(msg.what==MSG_SHOW_BITMAP){

Objectobj=msg.obj;

if(obj!=null){

Resultresult=(Result)obj;

if(result.iv.getTag().equals(result.url)){

result.iv.setImageBitmap(result.bitmap);

}

}

}

结果中包含图片、imageview以及url,为了防止图片显示错乱,还加以判定,只有imageview的tag等于此url的时候,才更新图片,如此则不会更新错乱。

多线程使用

在这种有大量耗时操作的时候,开启工作线程是非常必要的,但如何优雅地使用线程,其实仍然有门道。

android中有很多种开启工作线程的方式。

AsyncTask,封装地非常好,不同的回调函数还处于不同的线程当中,方便用户拿结果更新UI,但如果有多个AsyncTask实例在执行,它们是顺序执行,并不是想象中的多线程在多核CPU上同时执行。

Thread,原始的线程使用方式,如果构造太多,不优雅,线程不能得到复用,浪费资源,开启一个线程也是有开销的

HandlerThread,能够与handler结合,一种非常优雅的工作线程开启方式,不过不太适合大量任务的情况,这种只相当于线程池中只有一个线程在跑

线程池,重量级武器,适合大量任务的情况

android中开启工作线程包括但不限于以上4种,它们的优劣大致如上所述,需要我们根据不同的情形选用不同的方式,写出优雅的代码。

在照片墙应用中,明显是有大量任务需要工作线程来执行的,以上四种情况中,最适合的就是线程池了,它的速度效率是最高的,有兴趣的同学可以去做做实验,以四种不同方式来完成任务,看看哪个效率最高

杂谈

在文章一开始的时候,就聊到一个话题,OOM的问题,如果图片太大,如果防止OOM呢

这个问题相信很多人都知道答案:

/**

*Ifsettotrue,thedecoderwillreturnnull(nobitmap),but

*theout...fieldswillstillbeset,allowingthecallerto

*querythebitmapwithouthavingtoallocatethememoryforitspixels.

*/

publicbooleaninJustDecodeBounds;

/**

*Ifsettoavalue>1,requeststhedecodertosubsampletheoriginal

*image,returningasmallerimagetosavememory.Thesamplesizeis

*thenumberofpixelsineitherdimensionthatcorrespondtoasingle

*pixelinthedecodedbitmap.Forexample,inSampleSize==4returns

*animagethatis1/4thewidth/heightoftheoriginal,and1/16the

*numberofpixels.Anyvalue<=1istreatedthesameas1.Note:the

*decoderusesafinalvaluebasedonpowersof2,anyothervaluewill

*beroundeddowntothenearestpowerof2.

*/

publicintinSampleSize;

利用inJustDecodeBounds,计算出inSampleSize,相信这样的逻辑网上一找一大堆,本人在此不再复述。如果你的图片应用,图片都是大于5M以上的高清大图,那么一定要考虑这个方法了。

另外由url转化成hash key的时候,怎么这么费劲呢

publicStringhashKeyForDisk(Stringkey){

StringcacheKey;

try{

finalMessageDigestmDigest=MessageDigest.getInstance("MD5");

mDigest.update(key.getBytes());

cacheKey=bytesToHexString(mDigest.digest());

Log.i("okunu","cacheKey="+cacheKey);

}catch(NoSuchAlgorithmExceptione){

cacheKey=String.valueOf(key.hashCode());

}

returncacheKey;

}

privateStringbytesToHexString(byte[]bytes){

StringBuildersb=newStringBuilder();

for(inti=0;iStringhex=Integer.toHexString(0xFF&bytes[i]);

if(hex.length()==1){

sb.append('0');

}

sb.append(hex);

}

returnsb.toString();

}

第一步,我们通常能理解,获取url的md5值,因为url中可能含有各种奇异字符,不适合作为key来使用,但bytesToHexString方法的作用是什么

大家可以仔细地看看硬盘缓存中文件的名字的长度,就是为了防止名字长度不一致,当长度短一位的时候,补0。

代码已经上传到github当中,有需要的可以取用

https://github.com/okunu

照片处理

Android 选择图片上传功能【支持多选拍照预览等】

          

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之Android频道!


本文由 @白羽 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程