Android 的图片异步请求加三级缓存 ACE
2016-02-19 00:18
495 查看
使用xUtils等框架是很方便,但今天要用代码实现bitmapUtils的功能,很简单,
1AsyncTask请求一张图片
####AsyncTask
#####AsyncTask是线程池+handler的封装第一个泛型:传参的参数类型类型(和doInBackground一致)第二个泛型:
#####更新进度的参数类型(和onProgressUpdate一致)第三个泛型:返回结果的参数类型(和onPostExecute一致,
#####和doInBackground返回类型一致)
看AsyncTask源码:
核心线程5最大线程128这是AsyncTask的线程池然后通过handler发送消息,它内部实例化了一个静态的自定义类InternalHandler,这个类是继承自Handler的,在这个自定义类中绑定了一个叫做AsyncTaskResult的对象,每次子线程需要通知主线程,就调用sendToTarget发送消息给handler自己。然后在handler的handleMessage中AsyncTaskResult根据消息的类型不同(例如MESSAGE_POST_PROGRESS会更新进度条,MESSAGE_POST_CANCEL取消任务)而做不同的操作,值得一提的是,这些操作都是在UI线程进行的,意味着,从子线程一旦需要和UI线程交互,内部自动调用了handler对象把消息放在了主线程了。
下面看代码第一步我们先请求一张图片并解析注释写的很详细了.
NetCacheUtils.java
*<p>Ifyourcachedvaluesholdresourcesthatneedtobeexplicitlyreleased,
*override{@link#entryRemoved}.
*如果你cache的某个值需要明确释放,重写entryRemoved()
*<p>Bydefault,thecachesizeismeasuredinthenumberofentries.Override
*{@link#sizeOf}tosizethecacheindifferentunits.Forexample,thiscache
*islimitedto4MiBofbitmaps:默认cache大小是测量的item的数量,重写sizeof计算不同item的
*大小。
{@code
*intcacheSize=4*1024*1024;//4MiB
*LruCache<String,Bitmap>bitmapCache=newLruCache<String,Bitmap>(cacheSize){
*protectedintsizeOf(Stringkey,Bitmapvalue){
*returnvalue.getByteCount();
*}
*}}
-------------------------------------------------------------------
<p>Thisclassisthread-safe.Performmultiplecacheoperationsatomicallyby
*synchronizingonthecache:<pre>{@code
*synchronized(cache){
*if(cache.get(key)==null){
*cache.put(key,value);
*}
*}}</pre>
*他是线程安全的,自动地执行多个缓存操作并且加锁
-------------------------
<p>Thisclassdoesnotallownulltobeusedasakeyorvalue.Areturn
*valueofnullfrom{@link#get},{@link#put}or{@link#remove}is
*unambiguous:thekeywasnotinthecache.
*不允许key或者value为null
*当get(),put(),remove()返回值为null时,key相应的项不在cache中
最后一级缓存本地缓存把网络下载的图片文件名以MD5的形式保存到内存卡的制定目录
MD5Encoder
最后新建一个工具类来使用我们上面的三个缓存工具类
1AsyncTask请求一张图片
####AsyncTask
#####AsyncTask是线程池+handler的封装第一个泛型:传参的参数类型类型(和doInBackground一致)第二个泛型:
#####更新进度的参数类型(和onProgressUpdate一致)第三个泛型:返回结果的参数类型(和onPostExecute一致,
#####和doInBackground返回类型一致)
看AsyncTask源码:
publicabstractclassAsyncTask<Params,Progress,Result>{ privatestaticfinalStringLOG_TAG="AsyncTask"; privatestaticfinalintCORE_POOL_SIZE=5; privatestaticfinalintMAXIMUM_POOL_SIZE=128; privatestaticfinalintKEEP_ALIVE=1; privatestaticfinalThreadFactorysThreadFactory=newThreadFactory(){ privatefinalAtomicIntegermCount=newAtomicInteger(1); publicThreadnewThread(Runnabler){ returnnewThread(r,"AsyncTask#"+mCount.getAndIncrement()); } };
核心线程5最大线程128这是AsyncTask的线程池然后通过handler发送消息,它内部实例化了一个静态的自定义类InternalHandler,这个类是继承自Handler的,在这个自定义类中绑定了一个叫做AsyncTaskResult的对象,每次子线程需要通知主线程,就调用sendToTarget发送消息给handler自己。然后在handler的handleMessage中AsyncTaskResult根据消息的类型不同(例如MESSAGE_POST_PROGRESS会更新进度条,MESSAGE_POST_CANCEL取消任务)而做不同的操作,值得一提的是,这些操作都是在UI线程进行的,意味着,从子线程一旦需要和UI线程交互,内部自动调用了handler对象把消息放在了主线程了。
privatestaticfinalInternalHandlersHandler=newInternalHandler();
mFuture=newFutureTask<Result>(mWorker){ @Override protectedvoidMore...done(){ Messagemessage; Resultresult=null; try{ result=get(); }catch(InterruptedExceptione){ android.util.Log.w(LOG_TAG,e); }catch(ExecutionExceptione){ thrownewRuntimeException("AnerroroccuredwhileexecutingdoInBackground()", e.getCause()); }catch(CancellationExceptione){ message=sHandler.obtainMessage(MESSAGE_POST_CANCEL, newAsyncTaskResult<Result>(AsyncTask.this,(Result[])null)); message.sendToTarget(); return; }catch(Throwablet){ thrownewRuntimeException("Anerroroccuredwhileexecuting" +"doInBackground()",t); } message=sHandler.obtainMessage(MESSAGE_POST_RESULT, newAsyncTaskResult<Result>(AsyncTask.this,result)); message.sendToTarget(); } }; privatestaticclassInternalHandlerextendsHandler{ @SuppressWarnings({"unchecked","RawUseOfParameterizedType"}) @Override publicvoidMore...handleMessage(Messagemsg){ AsyncTaskResultresult=(AsyncTaskResult)msg.obj; switch(msg.what){ caseMESSAGE_POST_RESULT: //Thereisonlyoneresult result.mTask.finish(result.mData[0]); break; caseMESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); break; caseMESSAGE_POST_CANCEL: result.mTask.onCancelled(); break; } } }
下面看代码第一步我们先请求一张图片并解析注释写的很详细了.
NetCacheUtils.java
importjava.io.InputStream; importjava.net.HttpURLConnection; importjava.net.URL; importandroid.graphics.Bitmap; importandroid.graphics.BitmapFactory; importandroid.os.AsyncTask; importandroid.widget.ImageView; /** *网络缓存 * *@authorAce *@date2016-02-18 */ publicclassNetCacheUtils{ privateLocalCacheUtilsmLocalUtils; privateMemoryCacheUtilsmMemoryUtils; publicNetCacheUtils(LocalCacheUtilslocalUtils, MemoryCacheUtilsmemoryUtils){ mLocalUtils=localUtils; mMemoryUtils=memoryUtils; } publicvoidgetBitmapFromNet(ImageViewimageView,Stringurl){ BitmapTasktask=newBitmapTask(); task.execute(imageView,url); } /** *AsyncTask是线程池+handler的封装第一个泛型:传参的参数类型类型(和doInBackground一致)第二个泛型: *更新进度的参数类型(和onProgressUpdate一致)第三个泛型:返回结果的参数类型(和onPostExecute一致, *和doInBackground返回类型一致) */ classBitmapTaskextendsAsyncTask<Object,Integer,Bitmap>{ privateImageViewmImageView; privateStringurl; //主线程运行,预加载 @Override protectedvoidonPreExecute(){ super.onPreExecute(); } //子线程运行,异步加载逻辑在此方法中处理 @Override protectedBitmapdoInBackground(Object...params){ mImageView=(ImageView)params[0]; url=(String)params[1]; mImageView.setTag(url);//将imageView和url绑定在一起 //publishProgress(values)//通知进度 //下载图片 returndownload(url); } //主线程运行,更新进度 @Override protectedvoidonProgressUpdate(Integer...values){ super.onProgressUpdate(values); } //主线程运行,更新主界面 @Override protectedvoidonPostExecute(Bitmapresult){ if(result!=null){ //判断当前图片是否就是imageView要的图片,防止listview重用导致的图片错乱的情况出现 StringbindUrl=(String)mImageView.getTag(); if(bindUrl.equals(url)){ //给imageView设置图片 mImageView.setImageBitmap(result); //将图片保存在本地 mLocalUtils.setBitmapToLocal(result,url); //将图片保存在内存 mMemoryUtils.setBitmapToMemory(url,result); } } } } /** *下载图片 * *@paramurl */ publicBitmapdownload(Stringurl){ HttpURLConnectionconn=null; try{ conn=(HttpURLConnection)(newURL(url).openConnection()); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); conn.setRequestMethod("GET"); conn.connect(); intresponseCode=conn.getResponseCode(); if(responseCode==200){ InputStreamin=conn.getInputStream(); //将流转化为bitmap对象 Bitmapbitmap=BitmapFactory.decodeStream(in); returnbitmap; } }catch(Exceptione){ e.printStackTrace(); }finally{ if(conn!=null){ conn.disconnect(); } } returnnull; } }
MemoryCacheUtils.java用到了LruCache很简单 我简单翻译下文档:
*Acachethatholdsstrongreferencestoalimitednumberofvalues.Eachtime *avalueisaccessed,itismovedtotheheadofaqueue.Whenavalueis *addedtoafullcache,thevalueattheendofthatqueueisevictedandmay *becomeeligibleforgarbagecollection. *Cache保存一个强引用来限制内容数量,每当Item被访问的时候,此Item就会移动到队列的头部。 *当cache已满的时候加入新的item时,在队列尾部的item会被回收。
*<p>Ifyourcachedvaluesholdresourcesthatneedtobeexplicitlyreleased,
*override{@link#entryRemoved}.
*如果你cache的某个值需要明确释放,重写entryRemoved()
*<p>Bydefault,thecachesizeismeasuredinthenumberofentries.Override
*{@link#sizeOf}tosizethecacheindifferentunits.Forexample,thiscache
*islimitedto4MiBofbitmaps:默认cache大小是测量的item的数量,重写sizeof计算不同item的
*大小。
{@code
*intcacheSize=4*1024*1024;//4MiB
*LruCache<String,Bitmap>bitmapCache=newLruCache<String,Bitmap>(cacheSize){
*protectedintsizeOf(Stringkey,Bitmapvalue){
*returnvalue.getByteCount();
*}
*}}
-------------------------------------------------------------------
<p>Thisclassisthread-safe.Performmultiplecacheoperationsatomicallyby
*synchronizingonthecache:<pre>{@code
*synchronized(cache){
*if(cache.get(key)==null){
*cache.put(key,value);
*}
*}}</pre>
*他是线程安全的,自动地执行多个缓存操作并且加锁
-------------------------
<p>Thisclassdoesnotallownulltobeusedasakeyorvalue.Areturn
*valueofnullfrom{@link#get},{@link#put}or{@link#remove}is
*unambiguous:thekeywasnotinthecache.
*不允许key或者value为null
*当get(),put(),remove()返回值为null时,key相应的项不在cache中
最重要的大概就是以上几点:使用很简单 来看代码
importandroid.graphics.Bitmap; importandroid.support.v4.util.LruCache; /** *内存缓存工具类 * *@authorAce *@date2016-02-19 */ publicclassMemoryCacheUtils{ //Android2.3(APILevel //9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,这让软引用和弱引用变得不再可靠,建议用LruCache,它是强引用 privateLruCache<String,Bitmap>mCache; publicMemoryCacheUtils(){ intmaxMemory=(int)Runtime.getRuntime().maxMemory();//获取虚拟机分配的最大内存 //16M //LRU最近最少使用,通过控制内存不要超过最大值(由开发者指定),来解决内存溢出,就像上面翻译的所说如果cache满了会清理最近最少使用的缓存对象 mCache=newLruCache<String,Bitmap>(maxMemory/8){ @Override protectedintsizeOf(Stringkey,Bitmapvalue){ //计算一个bitmap的大小 intsize=value.getRowBytes()*value.getHeight();//每一行的字节数乘以高度 returnsize; } }; } publicBitmapgetBitmapFromMemory(Stringurl){ returnmCache.get(url); } publicvoidsetBitmapToMemory(Stringurl,Bitmapbitmap){ mCache.put(url,bitmap); } }
最后一级缓存本地缓存把网络下载的图片文件名以MD5的形式保存到内存卡的制定目录
/** *本地缓存工具类 * *@authorAce *@date2016-02-19 */ publicclassLocalCacheUtils{ //图片缓存的文件夹 publicstaticfinalStringDIR_PATH=Environment .getExternalStorageDirectory().getAbsolutePath() +"/ace_bitmap_cache"; publicBitmapgetBitmapFromLocal(Stringurl){ try{ Filefile=newFile(DIR_PATH,MD5Encoder.encode(url)); if(file.exists()){ Bitmapbitmap=BitmapFactory.decodeStream(newFileInputStream( file)); returnbitmap; } }catch(Exceptione){ e.printStackTrace(); } returnnull; } publicvoidsetBitmapToLocal(Bitmapbitmap,Stringurl){ FiledirFile=newFile(DIR_PATH); //创建文件夹文件夹不存在或者它不是文件夹则创建一个文件夹.mkdirs,mkdir的区别在于假如文件夹有好几层路径的话,前者会创建缺失的父目录后者不会创建这些父目录 if(!dirFile.exists()||!dirFile.isDirectory()){ dirFile.mkdirs(); } try{ Filefile=newFile(DIR_PATH,MD5Encoder.encode(url)); //将图片压缩保存在本地,参1:压缩格式;参2:压缩质量(0-100);参3:输出流 bitmap.compress(CompressFormat.JPEG,100, newFileOutputStream(file)); }catch(Exceptione){ e.printStackTrace(); } } }
MD5Encoder
importjava.security.MessageDigest; publicclassMD5Encoder{ publicstaticStringencode(Stringstring)throwsException{ byte[]hash=MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8")); StringBuilderhex=newStringBuilder(hash.length*2); for(byteb:hash){ if((b&0xFF)<0x10){ hex.append("0"); } hex.append(Integer.toHexString(b&0xFF)); } returnhex.toString(); } }
最后新建一个工具类来使用我们上面的三个缓存工具类
/** *三级缓存工具类 * *@authorAce *@date2016-02-19 */ publicclassMyBitmapUtils{ //网络缓存工具类 privateNetCacheUtilsmNetUtils; //本地缓存工具类 privateLocalCacheUtilsmLocalUtils; //内存缓存工具类 privateMemoryCacheUtilsmMemoryUtils; publicMyBitmapUtils(){ mMemoryUtils=newMemoryCacheUtils(); mLocalUtils=newLocalCacheUtils(); mNetUtils=newNetCacheUtils(mLocalUtils,mMemoryUtils); } publicvoiddisplay(ImageViewimageView,Stringurl){ //设置默认加载图片 imageView.setImageResource(R.drawable.news_pic_default); //先从内存缓存加载 Bitmapbitmap=mMemoryUtils.getBitmapFromMemory(url); if(bitmap!=null){ imageView.setImageBitmap(bitmap); System.out.println("从内存读取图片啦..."); return; } //再从本地缓存加载 bitmap=mLocalUtils.getBitmapFromLocal(url); if(bitmap!=null){ imageView.setImageBitmap(bitmap); System.out.println("从本地读取图片啦..."); //给内存设置图片 mMemoryUtils.setBitmapToMemory(url,bitmap); return; } //从网络缓存加载 mNetUtils.getBitmapFromNet(imageView,url); } }
相关文章推荐
- Android Studio-SVN代码下载,提交,冲突解决方案
- androidstudio中androidannotations框架的配置
- Android Studio2.0 gradle同步问题
- Android_NDK简便开发流程
- android 成长日记 1.学习使用menu
- Android 开发时用到的10大权限
- Android开发规范
- Android Toast统一管理类
- Android 图片三级缓存之内存缓存(告别软引用(SoftRefrerence)和弱引用(WeakReference))
- Android 2016新技术
- Android的文件权限
- android raw与assets区别
- mac系统下android studio 查看SHA1值的方法
- Android小笔记(2)
- Android studio 加载百度地图
- android studio启动慢解决办法
- Android Studio中的快捷键介绍及设置快捷键的方法
- Android Studio 小技巧合集
- 新Android反编译教程
- andriod自定义view实现幸运抽奖盘