Android实现异步加载图片
麦洛开通博客以来,有一段时间没有更新博文了.主要是麦洛这段时间因项目开发实在太忙了.今天周六还在公司加班,苦逼程序猿都是这样生活的.
今天在做项目的时候,有一个实现异步加载图片的功能,虽然比较简单但还是记录一下吧.因为麦洛之前实现异步加载图片都是使用了AsynTask这个API,继续这个类,实现起来非常简单也很方便.在doInBackground()方法里实现下载逻辑.具体实现如下
实现逻辑是:先从内存中读取,如果内存中有这张图片,则直接使用;如果内存没有再到sdcard上读取,如果有则显示;如果sdcard上还没有则到网络上读取.内存中开启缓存是参考了网上的实现.麦洛在这里非常感谢喜欢分享的程序猿们.
publicclassImageDownloaderextendsAsyncTask{ privatestaticfinalStringTAG="ImageDownloader"; //为了加快速度,在内存中开启缓存(主要应用于重复图片较多时,或者同一个图片要多次被访问,比如在ListView时来回滚动) privateMap >imageCache=newHashMap >(); /** *显示图片的控件 */ privateImageViewmImageView; publicImageDownloader(ImageViewimage){ mImageView=image; } @Override protectedvoidonPreExecute(){ super.onPreExecute(); } @Override protectedObjectdoInBackground(String...params){ //Log.i("ImageDownloader","loadingimage..."); Stringurl=params[0]; Drawabledrawable=null; try{ if(!"".equals(url)&&url!=null){ StringfileName=url.hashCode()+".jpg"; //如果缓存过就从缓存中取出数据 if(imageCache.containsKey(fileName)){ SoftReference softReference=imageCache.get(fileName); drawable=softReference.get(); if(drawable!=null){ returndrawable; } } Filedir=newFile(FileConstant.IMAGE_FILE_PATH); if(!dir.exists()){ booleanm=dir.mkdirs(); } Filefile=newFile(dir,fileName); if(file.exists()&&file.length()>0){ Log.i(TAG,"loadimagefromsdcard"); //如果文件存在则直接读取sdcard drawable=readFromSdcard(file); }else{ //file.createNewFile(); Log.i(TAG,"loadimagefromnetwork"); URLimageUrl=newURL(url); //写入sdcard if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ saveImageFile(imageUrl,file); drawable=Drawable.createFromStream(newFileInputStream(file),fileName); }else{ //直接从流读取 drawable=Drawable.createFromStream(imageUrl.openStream(),fileName); } } if(drawable!=null){ //保存在缓存中 imageCache.put(fileName,newSoftReference (drawable)); } } }catch(Exceptione){ e.printStackTrace(); } returndrawable; } /** *saveimage */ privatevoidsaveImageFile(URLurl,Filefile){ FileOutputStreamout=null; InputStreamin=null; try{ file.deleteOnExit(); out=newFileOutputStream(file); in=url.openStream(); byte[]buf=newbyte[1024]; intlen=-1; while((len=in.read(buf))!=-1){ out.write(buf,0,len); out.flush(); } }catch(Exceptione){ e.printStackTrace(); }finally{ if(out!=null){ try{ out.close(); }catch(IOExceptione){ e.printStackTrace(); } } if(in!=null){ try{ in.close(); }catch(IOExceptione){ e.printStackTrace(); } } } } /** *从sdcard中获取图片 */ privateDrawablereadFromSdcard(Filefile)throwsException{ FileInputStreamin=newFileInputStream(file); returnDrawable.createFromStream(in,file.getName()); } @Override protectedvoidonPostExecute(Objectresult){ super.onPostExecute(result); Drawabledrawable=(Drawable)result; if(mImageView!=null&&drawable!=null){ mImageView.setBackgroundDrawable(drawable); } } @Override protectedvoidonProgressUpdate(Integer...values){ super.onProgressUpdate(values); } @Override protectedvoidonCancelled(){ super.onCancelled(); } }
使用时:
ImageDownloaderloader=newImageDownloader(imageView); loader.execute(url);
其实这样的话,还有一些隐患的,就是说这个类实现还是有些问题的.比如每次都在imageView中设置网络上的图片时,其实是没有使用到这个类里面的内存缓存的,就是imageCache
Map
因为每次设置imageView的时候,都是new了一个ImageDownloader的对象.所以每个ImageDownloader对象里面都是独立的一个imageCache.
另外,AsynTask也是一个线程.而每次使用都开一个线程来load图片,对线程个数没有进行显示,毕竟线程数目还是有限制的.
所以麦洛今天发现了这个问题,于是参考了别人的实现,使用了线程池,实现逻辑也上面的代码一样,先从内存读取,如果没有到sdcard读取,如果还是没有,则是网络读取;实现没有使用AsynTask,具体代码如下:
/** *异步加载图片,并将图片设置到ImageView控件中 */ publicclassImageDownloaderextendsAsyncTask{ privatestaticfinalStringTAG="ImageDownloader"; //为了加快速度,在内存中开启缓存(主要应用于重复图片较多时,或者同一个图片要多次被访问,比如在ListView时来回滚动) privateMap >imageCache=newHashMap >(); /** *显示图片的控件 */ privateImageViewmImageView; publicImageDownloader(ImageViewimage){ mImageView=image; } @Override protectedvoidonPreExecute(){ super.onPreExecute(); } @Override protectedObjectdoInBackground(String...params){ //Log.i("ImageDownloader","loadingimage..."); Stringurl=params[0]; Drawabledrawable=null; try{ if(!"".equals(url)&&url!=null){ StringfileName=url.hashCode()+".jpg"; //如果缓存过就从缓存中取出数据 if(imageCache.containsKey(fileName)){ SoftReference softReference=imageCache.get(fileName); drawable=softReference.get(); if(drawable!=null){ returndrawable; } } Filedir=newFile(FileConstant.IMAGE_FILE_PATH); if(!dir.exists()){ booleanm=dir.mkdirs(); } Filefile=newFile(dir,fileName); if(file.exists()&&file.length()>0){ Log.i(TAG,"loadimagefromsdcard"); //如果文件存在则直接读取sdcard drawable=readFromSdcard(file); }else{ //file.createNewFile(); Log.i(TAG,"loadimagefromnetwork"); URLimageUrl=newURL(url); //写入sdcard if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ saveImageFile(imageUrl,file); drawable=Drawable.createFromStream(newFileInputStream(file),fileName); }else{ //直接从流读取 drawable=Drawable.createFromStream(imageUrl.openStream(),fileName); } } if(drawable!=null){ //保存在缓存中 imageCache.put(fileName,newSoftReference (drawable)); } } }catch(Exceptione){ e.printStackTrace(); } returndrawable; } /** *saveimage */ privatevoidsaveImageFile(URLurl,Filefile){ FileOutputStreamout=null; InputStreamin=null; try{ file.deleteOnExit(); out=newFileOutputStream(file); in=url.openStream(); byte[]buf=newbyte[1024]; intlen=-1; while((len=in.read(buf))!=-1){ out.write(buf,0,len); out.flush(); } }catch(Exceptione){ e.printStackTrace(); }finally{ if(out!=null){ try{ out.close(); }catch(IOExceptione){ e.printStackTrace(); } } if(in!=null){ try{ in.close(); }catch(IOExceptione){ e.printStackTrace(); } } } } /** *从sdcard中获取图片 */ privateDrawablereadFromSdcard(Filefile)throwsException{ FileInputStreamin=newFileInputStream(file); returnDrawable.createFromStream(in,file.getName()); } @Override protectedvoidonPostExecute(Objectresult){ super.onPostExecute(result); Drawabledrawable=(Drawable)result; if(mImageView!=null&&drawable!=null){ mImageView.setBackgroundDrawable(drawable); } } @Override protectedvoidonProgressUpdate(Integer...values){ super.onProgressUpdate(values); } @Override protectedvoidonCancelled(){ super.onCancelled(); } }
这个ImageDownloader2的使用也很简单
publicclassImageUtil{ /** *imageloader */ staticImageDownloader2loader=null; /** *loadimage */ publicstaticvoidloadImage(Stringurl,finalImageViewimageView){ if(loader==null){ loader=newImageDownloader2(); } loader.loadDrawable(url,newImageCallback(){ @Override publicvoidimageLoaded(DrawableimageDrawable){ if(imageDrawable!=null){ imageView.setBackgroundDrawable(imageDrawable); } } }); } }
每次在使用是需要调用ImageUtil.loadImage(url,imageView)将图片url已经需要显示图片的控件ImageView的引用传入就可以了.
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。