在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);