失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > Android之ListView异步加载网络图片(优化缓存机制)

Android之ListView异步加载网络图片(优化缓存机制)

时间:2021-09-19 08:46:41

相关推荐

Android之ListView异步加载网络图片(优化缓存机制)

网上关于这个方面的文章也不少,基本的思路是线程+缓存来解决。下面提出一些优化:

1、采用线程池

2、内存缓存+文件缓存

3、内存缓存中网上很多是采用SoftReference来防止堆溢出,这儿严格限制只能使用最大JVM内存的1/4

4、对下载的图片进行按比例缩放,以减少内存的消耗

具体的代码里面说明。先放上内存缓存类的代码MemoryCache.java:

[java]view plaincopy publicclassMemoryCache{privatestaticfinalStringTAG="MemoryCache";//放入缓存时是个同步操作//LinkedHashMap构造方法的最后一个参数true代表这个map里的元素将按照最近使用次数由少到多排列,即LRU//这样的好处是如果要将缓存中的元素替换,则先遍历出最近最少使用的元素来替换以提高效率privateMap<String,Bitmap>cache=Collections.synchronizedMap(newLinkedHashMap<String,Bitmap>(10,1.5f,true));//缓存中图片所占用的字节,初始0,将通过此变量严格控制缓存所占用的堆内存privatelongsize=0;//currentallocatedsize//缓存只能占用的最大堆内存privatelonglimit=1000000;//maxmemoryinbytespublicMemoryCache(){//use25%ofavailableheapsizesetLimit(Runtime.getRuntime().maxMemory()/4);}publicvoidsetLimit(longnew_limit){limit=new_limit;Log.i(TAG,"MemoryCachewilluseupto"+limit/1024./1024.+"MB");}publicBitmapget(Stringid){try{if(!cache.containsKey(id))returnnull;returncache.get(id);}catch(NullPointerExceptionex){returnnull;}}publicvoidput(Stringid,Bitmapbitmap){try{if(cache.containsKey(id))size-=getSizeInBytes(cache.get(id));cache.put(id,bitmap);size+=getSizeInBytes(bitmap);checkSize();}catch(Throwableth){th.printStackTrace();}}/***严格控制堆内存,如果超过将首先替换最近最少使用的那个图片缓存**/privatevoidcheckSize(){Log.i(TAG,"cachesize="+size+"length="+cache.size());if(size>limit){//先遍历最近最少使用的元素Iterator<Entry<String,Bitmap>>iter=cache.entrySet().iterator();while(iter.hasNext()){Entry<String,Bitmap>entry=iter.next();size-=getSizeInBytes(entry.getValue());iter.remove();if(size<=limit)break;}Log.i(TAG,"Cleancache.Newsize"+cache.size());}}publicvoidclear(){cache.clear();}/***图片占用的内存**@parambitmap*@return*/longgetSizeInBytes(Bitmapbitmap){if(bitmap==null)return0;returnbitmap.getRowBytes()*bitmap.getHeight();}}

也可以使用SoftReference,代码会简单很多,但是我推荐上面的方法。

[java]view plaincopy publicclassMemoryCache{privateMap<String,SoftReference<Bitmap>>cache=Collections.synchronizedMap(newHashMap<String,SoftReference<Bitmap>>());publicBitmapget(Stringid){if(!cache.containsKey(id))returnnull;SoftReference<Bitmap>ref=cache.get(id);returnref.get();}publicvoidput(Stringid,Bitmapbitmap){cache.put(id,newSoftReference<Bitmap>(bitmap));}publicvoidclear(){cache.clear();}}

下面是文件缓存类的代码FileCache.java:

[java]view plaincopy publicclassFileCache{privateFilecacheDir;publicFileCache(Contextcontext){//如果有SD卡则在SD卡中建一个LazyList的目录存放缓存的图片//没有SD卡就放在系统的缓存目录中if(android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))cacheDir=newFile(android.os.Environment.getExternalStorageDirectory(),"LazyList");elsecacheDir=context.getCacheDir();if(!cacheDir.exists())cacheDir.mkdirs();}publicFilegetFile(Stringurl){//将url的hashCode作为缓存的文件名Stringfilename=String.valueOf(url.hashCode());//Anotherpossiblesolution//Stringfilename=URLEncoder.encode(url);Filef=newFile(cacheDir,filename);returnf;}publicvoidclear(){File[]files=cacheDir.listFiles();if(files==null)return;for(Filef:files)f.delete();}}

最后最重要的加载图片的类,ImageLoader.java:

[java]view plaincopy publicclassImageLoader{MemoryCachememoryCache=newMemoryCache();FileCachefileCache;privateMap<ImageView,String>imageViews=Collections.synchronizedMap(newWeakHashMap<ImageView,String>());//线程池ExecutorServiceexecutorService;publicImageLoader(Contextcontext){fileCache=newFileCache(context);executorService=Executors.newFixedThreadPool(5);}//当进入listview时默认的图片,可换成你自己的默认图片finalintstub_id=R.drawable.stub;//最主要的方法publicvoidDisplayImage(Stringurl,ImageViewimageView){imageViews.put(imageView,url);//先从内存缓存中查找Bitmapbitmap=memoryCache.get(url);if(bitmap!=null)imageView.setImageBitmap(bitmap);else{//若没有的话则开启新线程加载图片queuePhoto(url,imageView);imageView.setImageResource(stub_id);}}privatevoidqueuePhoto(Stringurl,ImageViewimageView){PhotoToLoadp=newPhotoToLoad(url,imageView);executorService.submit(newPhotosLoader(p));}privateBitmapgetBitmap(Stringurl){Filef=fileCache.getFile(url);//先从文件缓存中查找是否有Bitmapb=decodeFile(f);if(b!=null)returnb;//最后从指定的url中下载图片try{Bitmapbitmap=null;URLimageUrl=newURL(url);HttpURLConnectionconn=(HttpURLConnection)imageUrl.openConnection();conn.setConnectTimeout(30000);conn.setReadTimeout(30000);conn.setInstanceFollowRedirects(true);InputStreamis=conn.getInputStream();OutputStreamos=newFileOutputStream(f);CopyStream(is,os);os.close();bitmap=decodeFile(f);returnbitmap;}catch(Exceptionex){ex.printStackTrace();returnnull;}}//decode这个图片并且按比例缩放以减少内存消耗,虚拟机对每张图片的缓存大小也是有限制的privateBitmapdecodeFile(Filef){try{//decodeimagesizeBitmapFactory.Optionso=newBitmapFactory.Options();o.inJustDecodeBounds=true;BitmapFactory.decodeStream(newFileInputStream(f),null,o);//Findthecorrectscalevalue.Itshouldbethepowerof2.finalintREQUIRED_SIZE=70;intwidth_tmp=o.outWidth,height_tmp=o.outHeight;intscale=1;while(true){if(width_tmp/2<REQUIRED_SIZE||height_tmp/2<REQUIRED_SIZE)break;width_tmp/=2;height_tmp/=2;scale*=2;}//decodewithinSampleSizeBitmapFactory.Optionso2=newBitmapFactory.Options();o2.inSampleSize=scale;returnBitmapFactory.decodeStream(newFileInputStream(f),null,o2);}catch(FileNotFoundExceptione){}returnnull;}//TaskforthequeueprivateclassPhotoToLoad{publicStringurl;publicImageViewimageView;publicPhotoToLoad(Stringu,ImageViewi){url=u;imageView=i;}}classPhotosLoaderimplementsRunnable{PhotoToLoadphotoToLoad;PhotosLoader(PhotoToLoadphotoToLoad){this.photoToLoad=photoToLoad;}@Overridepublicvoidrun(){if(imageViewReused(photoToLoad))return;Bitmapbmp=getBitmap(photoToLoad.url);memoryCache.put(photoToLoad.url,bmp);if(imageViewReused(photoToLoad))return;BitmapDisplayerbd=newBitmapDisplayer(bmp,photoToLoad);//更新的操作放在UI线程中Activitya=(Activity)photoToLoad.imageView.getContext();a.runOnUiThread(bd);}}/***防止图片错位**@paramphotoToLoad*@return*/booleanimageViewReused(PhotoToLoadphotoToLoad){Stringtag=imageViews.get(photoToLoad.imageView);if(tag==null||!tag.equals(photoToLoad.url))returntrue;returnfalse;}//用于在UI线程中更新界面classBitmapDisplayerimplementsRunnable{Bitmapbitmap;PhotoToLoadphotoToLoad;publicBitmapDisplayer(Bitmapb,PhotoToLoadp){bitmap=b;photoToLoad=p;}publicvoidrun(){if(imageViewReused(photoToLoad))return;if(bitmap!=null)photoToLoad.imageView.setImageBitmap(bitmap);elsephotoToLoad.imageView.setImageResource(stub_id);}}publicvoidclearCache(){memoryCache.clear();fileCache.clear();}publicstaticvoidCopyStream(InputStreamis,OutputStreamos){finalintbuffer_size=1024;try{byte[]bytes=newbyte[buffer_size];for(;;){intcount=is.read(bytes,0,buffer_size);if(count==-1)break;os.write(bytes,0,count);}}catch(Exceptionex){}}}

主要流程是先从内存缓存中查找,若没有再开线程,从文件缓存中查找都没有则从指定的url中查找,并对bitmap进行处理,最后通过下面方法对UI进行更新操作。

[java]view plaincopy a.runOnUiThread(...);

在你的程序中的基本用法:

[java]view plaincopy ImageLoaderimageLoader=newImageLoader(context);...imageLoader.DisplayImage(url,imageView);

比如你的放在你的ListView的adapter的getView()方法中,当然也适用于GridView。

OK,先到这。

如果觉得《Android之ListView异步加载网络图片(优化缓存机制)》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。