在Android的应用中实现网络图片异步加载的方法
前言
其实很幸运,入职一周之后就能跟着两个师兄做android开发,师兄都是大神,身为小白的我只能多多学习,多多努力。最近一段时间都忙的没机会总结,今天刚完成了android客户端图片异步加载的类,这里记录一下(ps:其实我这里都是参考网上开源实现)
原理
在ListView或者GridView中加载图片的原理基本都是一样的:
先从内存缓存中获取,取到则返回,取不到进行下一步
从文件缓存中获取,取到则返回并更新到内存缓存,取不到则进行进行下一步
从网络上下载图片,并更新内存缓存和文件缓存
流程图如下:
同时,要注意线程的数量。一般在listview中加载图片,大家都是开启新的线程去加载,但是当快速滑动时,很容易造成OOM,因此需要控制线程数量。我们可以通过线程池控制线程的数量,具体线程池的大小还需要根据处理器的情况和业务情况自行判断
建立线程池的方法如下:
ExecutorServiceexecutorService=Executors.newFixedThreadPool(5);//5是可变的
文件缓存类
importjava.io.File; importandroid.content.Context; publicclassFileCache{ privatestaticfinalStringDIR_NAME="your_dir"; privateFilecacheDir; publicFileCache(Contextcontext){ //Findthedirectorytosavecachedimages if(android.os.Environment.getExternalStorageState().equals( android.os.Environment.MEDIA_MOUNTED)){ cacheDir=newFile( android.os.Environment.getExternalStorageDirectory(), DIR_NAME); }else{ cacheDir=context.getCacheDir(); } if(!cacheDir.exists()){ cacheDir.mkdirs(); } } publicFilegetFile(Stringurl){ //Identifyimagesbyurl'shashcode Stringfilename=String.valueOf(url.hashCode()); Filef=newFile(cacheDir,filename); returnf; } publicvoidclear(){ File[]files=cacheDir.listFiles(); if(files==null){ return; }else{ for(Filef:files){ f.delete(); } } } }
内存缓存类
这里使用了软引用,Map<String,SoftReference<Bitmap>>cache,可以google一下软引用的机制,简单的说:实现了map,同时当内存紧张时可以被回收,不会造成内存泄露
importjava.lang.ref.SoftReference; importjava.util.Collections; importjava.util.LinkedHashMap; importjava.util.Map; importandroid.graphics.Bitmap; publicclassMemoryCache{ privateMap<String,SoftReference<Bitmap>>cache=Collections .synchronizedMap(newLinkedHashMap<String,SoftReference<Bitmap>>( 10,1.5f,true)); 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(); } }
图片加载类
importjava.io.File; importjava.io.FileInputStream; importjava.io.FileNotFoundException; importjava.io.FileOutputStream; importjava.io.InputStream; importjava.io.OutputStream; importjava.net.HttpURLConnection; importjava.net.URL; importjava.util.Collections; importjava.util.Map; importjava.util.WeakHashMap; importjava.util.concurrent.ExecutorService; importjava.util.concurrent.Executors; importandroid.content.Context; importandroid.graphics.Bitmap; importandroid.graphics.BitmapFactory; importandroid.os.Handler; importandroid.widget.ImageView; publicclassImageLoader{ /** *Networktimeout */ privatestaticfinalintTIME_OUT=30000; /** *Defaultpictureresource */ privatestaticfinalintDEFAULT_BG=R.drawable.plate_list_head_bg; /** *Threadpoolnumber */ privatestaticfinalintTHREAD_NUM=5; /** *Memoryimagecache */ MemoryCachememoryCache=newMemoryCache(); /** *Fileimagecache */ FileCachefileCache; /** *Judgeimageviewifitisreuse */ privateMap<ImageView,String>imageViews=Collections .synchronizedMap(newWeakHashMap<ImageView,String>()); /** *Threadpool */ ExecutorServiceexecutorService; /** *HandlertodisplayimagesinUIthread */ Handlerhandler=newHandler(); publicImageLoader(Contextcontext){ fileCache=newFileCache(context); executorService=Executors.newFixedThreadPool(THREAD_NUM); } publicvoiddisPlayImage(Stringurl,ImageViewimageView){ imageViews.put(imageView,url); Bitmapbitmap=memoryCache.get(url); if(bitmap!=null){ //DisplayimagefromMemorycache imageView.setImageBitmap(bitmap); }else{ //DisplayimagefromFilecacheorNetwork queuePhoto(url,imageView); } } privatevoidqueuePhoto(Stringurl,ImageViewimageView){ PhotoToLoadphotoToLoad=newPhotoToLoad(url,imageView); executorService.submit(newPhotosLoader(photoToLoad)); } privateBitmapgetBitmap(Stringurl){ Filef=fileCache.getFile(url); //FromFilecache Bitmapbmp=decodeFile(f); if(bmp!=null){ returnbmp; } //FromNetwork try{ Bitmapbitmap=null; URLimageUrl=newURL(url); HttpURLConnectionconn=(HttpURLConnection)imageUrl .openConnection(); conn.setConnectTimeout(TIME_OUT); conn.setReadTimeout(TIME_OUT); conn.setInstanceFollowRedirects(true); InputStreamis=conn.getInputStream(); OutputStreamos=newFileOutputStream(f); copyStream(is,os); os.close(); conn.disconnect(); bitmap=decodeFile(f); returnbitmap; }catch(Throwableex){ if(exinstanceofOutOfMemoryError){ clearCache(); } returnnull; } } privatevoidcopyStream(InputStreamis,OutputStreamos){ intbuffer_size=1024; try{ byte[]bytes=newbyte[buffer_size]; while(true){ intcount=is.read(bytes,0,buffer_size); if(count==-1){ break; } os.write(bytes,0,count); } }catch(Exceptione){ } } privateBitmapdecodeFile(Filef){ try{ //TODO:Compressimagesize FileInputStreamfileInputStream=newFileInputStream(f); Bitmapbitmap=BitmapFactory.decodeStream(fileInputStream); returnbitmap; }catch(FileNotFoundExceptione){ returnnull; } } privatevoidclearCache(){ memoryCache.clear(); fileCache.clear(); } /** *Taskforthequeue * *@authorzhengyi.wzy * */ privateclassPhotoToLoad{ publicStringurl; publicImageViewimageView; publicPhotoToLoad(Stringurl,ImageViewimageView){ this.url=url; this.imageView=imageView; } } /** *Asynchronoustoloadpicture * *@authorzhengyi.wzy * */ classPhotosLoaderimplementsRunnable{ PhotoToLoadphotoToLoad; publicPhotosLoader(PhotoToLoadphotoToLoad){ this.photoToLoad=photoToLoad; } privatebooleanimageViewReused(PhotoToLoadphotoToLoad){ Stringtag=imageViews.get(photoToLoad.imageView); if(tag==null||!tag.equals(photoToLoad.url)){ returntrue; } returnfalse; } @Override publicvoidrun(){ //AbortcurrentthreadifImageViewreused if(imageViewReused(photoToLoad)){ return; } Bitmapbitmap=getBitmap(photoToLoad.url); //UpdateMemory memoryCache.put(photoToLoad.url,bitmap); if(imageViewReused(photoToLoad)){ return; } //Don'tchangeUIinchildrenthread BitmapDisplayerbd=newBitmapDisplayer(bitmap,photoToLoad); handler.post(bd); } classBitmapDisplayerimplementsRunnable{ Bitmapbitmap; PhotoToLoadphotoToLoad; publicBitmapDisplayer(Bitmapbitmap,PhotoToLoadphotoToLoad){ this.bitmap=bitmap; this.photoToLoad=photoToLoad; } @Override publicvoidrun(){ if(imageViewReused(photoToLoad)){ return; } if(bitmap!=null){ photoToLoad.imageView.setImageBitmap(bitmap); }else{ photoToLoad.imageView.setImageResource(DEFAULT_BG); } } } } }
调用方法
ImageLoaderimageLoader=newImageLoader(context); imageLoader.disPlayImage(imageUrl,imageView);