Android图片异步加载

[来源] 达内    [编辑] 达内   [时间]2012-09-17

开发Android程序,一般情况下都会有两个操作,图片的异步加载与缓存,而图片的异步加载大都是从网络读取图片

开发Android程序,一般情况下都会有两个操作,图片的异步加载与缓存,而图片的 步加载大都是从网络读取图片(还有生成本地图片缩略图等操作),为了减少网络操作,加快图片加载速度就需要对图片进行缓存,所以网上的好多图片异步加载方法都是与图片的缓存紧密关联的。但也有可能用户已经有了缓存的相关类库,这样使用起来就会有点麻烦。

< p style="margin: 5px auto; padding: 0px; text-indent: 0px; color: rgb(57, 57, 57); font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 21px; orphans: 2; text-align: left; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(250, 247, 239); ">   最近一段处理跟图片相关的问题,本来是自己写的图片加载,不过有些状态的控制还是比 烦人的,比如ListView滚动时ImageView的重用,所以本着偷懒与充分利用现有资源的态度去网上搜罗图片异步加载的代码,最终在GreenDroid UI库中找到一个,其中有个AsyncImageView的自定义View用于异步加载图片,不过也像网上的大多数 片异步加载方法一样,是跟图片的缓存关联在一起的,不过只是很简单的内存缓存,无文件缓存。图片的加载方法也如其他的一样是写死了的,这就限制了其使用范围,只可通过InputStream来decode图片,而像生成缩略图或其他一些图片处理的异步处理就无法用途。修改现有类库总比自己从头写来的简单,于是稍微修改了下AsyncImageView,使其可以自定义缓存与图片加载方法,对于AsyncImageView只有一点点的修改,大都是别人源码。

< h3 style="margin: 15px auto 2px; padding: 6px 3px; font-size: 17px; font-weight: bold; color: rgb(57, 57, 57); font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; font-style: normal; font-variant: normal; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(182, 201, 231); background-position: initial initial; background-repeat: initial initial; ">1. 核心类 < p style="margin: 5px auto 5px 30px; padding: 0px; text-indent: 0px; color: rgb(57, 57, 57); font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 21px; orphans: 2; text-align: left; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(250, 247, 239); ">  ImageLoader:图片加载核心类,内部使用线程池加载图片

< p style="margin: 5px auto 5px 30px; padding: 0px; text-indent: 0px; color: rgb(57, 57, 57); font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 21px; orphans: 2; text-align: left; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(250, 247, 239); ">  ImageRequest:表示一个图片加载的请求

< p style="margin: 5px auto 5px 30px; padding: 0px; text-indent: 0px; color: rgb(57, 57, 57); font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 21px; orphans: 2; text-align: left; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(250, 247, 239); ">  AsyncImageView:自定义的图片异步加载View

< p style="margin: 5px auto 5px 30px; padding: 0px; text-indent: 0px; color: rgb(57, 57, 57); font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 21px; orphans: 2; text-align: left; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(250, 247, 239); ">   LoadMethod:自定义图片加载方法的接口,可以通过实现此接口来自定义图片的加载方法

< p style="margin: 5px auto 5px 30px; padding: 0px; text-indent: 0px; color: rgb(57, 57, 57); font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 21px; orphans: 2; text-align: left; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(250, 247, 239); ">  CacheCallback:缓存接口,可以通过实现此接口实现对缓存的读写

< p style="margin: 5px auto 5px 60px; padding: 0px; text-indent: 0px; color: rgb(57, 57, 57); font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 21px; orphans: 2; text-align: left; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(250, 247, 239); ">AsyncImageView.OnImageViewLoadListener:图片加载状态监听(开始,失败,结束)

< h3 style="margin: 15px auto 2px; padding: 6px 3px; font-size: 17px; font-weight: bold; color: rgb(57, 57, 57); font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; font-style: normal; font-variant: normal; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(182, 201, 231); background-position: initial initial; background-repeat: initial initial; ">2。图片加载方法 < div style="margin: 5px 0px; padding: 5px; background-color: rgb(245, 245, 245); font-family: 'Courier New'; font-size: 12px; border: 1px solid rgb(204, 204, 204); overflow: auto; color: rgb(57, 57, 57); font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; " class="cnblogs_code">
public
 void
 run() {              Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);              final
 Handler h = mHandler;             Bitmap bitmap 
= null

;             Throwable throwable = 
null

;              h.sendMessage(Message.obtain(h, ON_START));              
try

 {                 if
 (TextUtils.isEmpty(mUrl)) {                     
throw

 new
 Exception("The given URL cannot be null or empty"

);                 }                  //
 如果自定义了加载方法,则用自定义的方法

                if (mLoadMethod != 
null) {                     bitmap 
=

 mLoadMethod.load(mUrl);                 } else
 {                      InputStream inputStream 
= null

;                      //
 Asset

                    if
 (mUrl.startsWith("file:///android_asset/")) {                         inputStream 
= sAssetManager.open(mUrl.replaceFirst(                                 

"file:///android_asset/", ""));                     }                     
// File

                    else if
 (mUrl.startsWith("file:///") || mUrl.startsWith("/")) {                         
if
 (mUrl.startsWith("file:///"))                             mUrl 

= mUrl.replaceFirst("file:///", "/");                         inputStream 
= new
 FileInputStream(mUrl);                     }                     

// NetWork

                    else
 {                         //
 在用URL类加载图片时,发现有的机型上面通过URL类获得的InputStream解析获得的图片总是null,故使用HttpClient

                        HttpGet httpRequest = new
 HttpGet(mUrl);                         HttpClient httpclient 
= new

 DefaultHttpClient();                         HttpParams httpParams 
= new

 BasicHttpParams();                         HttpConnectionParams.setConnectionTimeout(httpParams, 
5000);                         HttpConnectionParams.setSoTimeout(httpParams, 
5000

);                         httpRequest.setParams(httpParams);                         HttpResponse response =
 (HttpResponse)httpclient.execute(httpRequest);                         HttpEntity entity 

= response.getEntity();                         BufferedHttpEntity bufHttpEntity 
= new
 BufferedHttpEntity(entity);                         InputStream instream 

= bufHttpEntity.getContent();                         BufferedInputStream bi 
= new

 BufferedInputStream(instream);                         inputStream 
= bi;                     }                      
//


 虽然AsyncImageView中有设置BitmapFactory.Options的方法,但一般情况下都未知图片的大小,也就无法计算相应的inSampleSize,                     

//
 也就无法设置相应的BitmapFactory.Options,所以一般情况下还是根据自己的需要自定义LoadMethod为好

                    bitmap = BitmapFactory.decodeStream(inputStream, null
,                             (mOptions 
== null

) ? sDefaultOptions : mOptions);                     inputStream.close();                 }                 


if (mBitmapProcessor != null
 && bitmap != null
) {                     

final Bitmap processedBitmap =
 mBitmapProcessor.processImage(bitmap);                     

if (processedBitmap != null
) {                         bitmap.recycle();                         bitmap 
=

 processedBitmap;                     }                 }              } catch
 (Exception e) {                 Log.e(LOG_TAG, 
"Error while fetching image", e);                 throwable 

= e;             }              
if

 (bitmap == null
) {                 

if (throwable == null
) {                     throwable = 
new

 Exception("Skia image decoding failed");                 }                 h.sendMessage(Message.obtain(h, ON_FAIL, throwable));             } 
else
 {                 h.sendMessage(Message.obtain(h, ON_END, bitmap));                 if
 (mCache != null
) {                     mCache.writeCache(TextUtils.isEmpty(mCacheKey) 

? mUrl : mCacheKey, bitmap);                 }             }         }
< p style="margin: 5px auto; padding: 0px; text-indent: 0px; color: rgb(57, 57, 57); font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 21px; orphans: 2; text-align: left; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(250, 247, 239); ">   如果自定义了LoadMethod,会调用相应的方法加载图片,如果没有自定义,会使用默认的加载方法,可以加载本地图片,Asset图片与网络图片,GreenDroid的源码中加载网络图片是用的URL的,但我们以前在加载网络图片时遇到一个问题,有的机型通过URL类获得的ImputStream解析图片总是返回null,所以就改为了HttpClient。

< h3 style="margin: 15px auto 2px; padding: 6px 3px; font-size: 17px; font-weight: bold; color: rgb(57, 57, 57); font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; font-style: normal; font-variant: normal; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(182, 201, 231); background-position: initial initial; background-repeat: initial initial; ">3。使用方法 < p style="margin: 5px auto; padding: 0px; text-indent: 0px; color: rgb(57, 57, 57); font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 21px; orphans: 2; text-align: left; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(250, 247, 239); ">通过AsyncImageView的setPath方法来加载图片,setPath有3个重载方法:

< p style="margin: 5px auto 5px 30px; padding: 0px; text-indent: 0px; color: rgb(57, 57, 57); font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 21px; orphans: 2; text-align: left; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(250, 247, 239); ">public void setPath(String path)

< p style="margin: 5px auto 5px 30px; padding: 0px; text-indent: 0px; color: rgb(57, 57, 57); font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 21px; orphans: 2; text-align: left; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(250, 247, 239); ">public void setPath(String path, LoadMethod loadMethod)

< p style="margin: 5px auto 5px 30px; padding: 0px; text-indent: 0px; color: rgb(57, 57, 57); font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 21px; orphans: 2; text-align: left; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(250, 247, 239); ">public void setPath(String path, LoadMethod loadMethod, String cacheKey)

< p style="margin: 5px auto; padding: 0px; text-indent: 0px; color: rgb(57, 57, 57); font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 21px; orphans: 2; text-align: left; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(250, 247, 239); "> 第一个参数指定要加载的图片的路径,第二个参数为自定义的图片加载方法,若不指定则用默认的。

< p style="margin: 5px auto; padding: 0px; text-indent: 0px; color: rgb(57, 57, 57); font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 21px; orphans: 2; text-align: left; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(250, 247, 239); "> 至于加第三个参数,是做缓存用的,一般要加载的图片的路径都是唯一的,所以一般用第一个参数来做为缓存的Key就行了,但也有特殊情况,比如读取局域网中的图片,一般都是自动获取IP,所以根据图片路径做为缓存的Key可能是不合适的,所以就需要根据需要手动指定用来作为缓存的Key。

< div style="margin: 5px 0px; padding: 5px; background-color: rgb(245, 245, 245); font-family: 'Courier New'; font-size: 12px; border: 1px solid rgb(204, 204, 204); overflow: auto; color: rgb(57, 57, 57); font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; " class="cnblogs_code">
/**
      * 设置要加载的图片的路径, 可为网络路径, Asset文件路径(file:///android_asset), 本地图片路径(file:///或/)      *       * 
@param
 path 要加载的图片的路径, 若为null则加载默认图片      * @param


 loadMethod 自定义的图片加载的方法, 可以null, 使用默认的加载方法      * @param
 cacheKey 缓存key      */

    public void
 setPath(String path, LoadMethod loadMethod, String cacheKey) {          
// Check the url has changed

        if (mBitmap != null
 && path != null
 && path.equals(mUrl)) { //
 TODO mBitmap != null necessary?


            return
;         }          stopLoading();         mUrl = path;         mCacheKey 
= cacheKey;         mLoadMethod =
 loadMethod;          

//
 Setting the url to an empty string force the displayed image to the         

// default image

        if
 (TextUtils.isEmpty(mUrl)) {             mBitmap = null


;             setDefaultImage();         } else
 {             if
 (!

mPaused) {                 reload();             } else
 {                 //
 We're paused: let's look in a synchronous and efficient cache                 
//

 prior using the default image.

                mBitmap = readCache(); //
 TODO 可能会耗时间

                if (mBitmap != 
null) {                     setImageBitmap(mBitmap);                 } 
else
 {                     setDefaultImage();                 }             }         }     }
< div style="margin: 5px 0px; padding: 5px; background-color: rgb(245, 245, 245); font-family: 'Courier New'; font-size: 12px; border: 1px solid rgb(204, 204, 204); overflow: auto; color: rgb(57, 57, 57); font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; " class="cnblogs_code">
public
 void reload(boolean
 force) {         if
 (mRequest == null
 && mUrl != null

) {              //
 Prior downloading the image ... let's look in a cache !

            mBitmap = null
;             if (!
force) {                 //
 This may take a long time.

                mBitmap = readCache();             }              
if (mBitmap != null
) {                 setImageBitmap(mBitmap);                 
return

;             }              setDefaultImage();             mRequest 
= new

 ImageRequest(mUrl, this
, mImageProcessor, mOptions, mCacheKey);             mRequest.load(getContext(), mLoadMethod);             if
 (ImageLoader.getInstance() != null
 && ImageLoader.getInstance().getCache() == null

) {                 ImageLoader.getInstance().setCache(mCache);             }         }
< p style="margin: 5px auto; padding: 0px; text-indent: 0px; color: rgb(57, 57, 57); font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 21px; orphans: 2; text-align: left; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(250, 247, 239); ">  readCache()用于读取缓存,代码如下:

< div style="margin: 5px 0px; padding: 5px; background-color: rgb(245, 245, 245); font-family: 'Courier New'; font-size: 12px; border: 1px solid rgb(204, 204, 204); overflow: auto; color: rgb(57, 57, 57); font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; " class="cnblogs_code">
    private
 Bitmap readCache() {         if
 (mCache != null

)             return
 mCache.readCache(TextUtils.isEmpty(mCacheKey) ? mUrl : mCacheKey);         
return null
;     }
< p style="margin: 5px auto; padding: 0px; text-indent: 0px; color: rgb(57, 57, 57); font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 21px; orphans: 2; text-align: left; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(250, 247, 239); ">   其中的mCache由用户能过setCacheCallback(CacheCallback callback)设置用户自定义的缓存方法,由此将图片的加载与缓存分离开,使用户可以使用现有的缓存实现。如要用户指定了缓存Key就使用用户指定的Key,否则就用图片的路径作Key。

< h3 style="margin: 15px auto 2px; padding: 6px 3px; font-size: 17px; font-weight: bold; color: rgb(57, 57, 57); font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; font-style: normal; font-variant: normal; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(182, 201, 231); background-position: initial initial; background-repeat: initial initial; ">4.AsyncImageView中的其他重要方法 < p style="margin: 5px auto 5px 60px; padding: 0px; text-indent: 0px; color: rgb(57, 57, 57); font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 21px; orphans: 2; text-align: left; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(250, 247, 239); ">reload([boolean force]):重新加载

< p style="margin: 5px auto 5px 60px; padding: 0px; text-indent: 0px; color: rgb(57, 57, 57); font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 21px; orphans: 2; text-align: left; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(250, 247, 239); "> stopLoading():停止加载,如果当前正在加载则没有效果,如果加载任务在加载线程池队列中则取消。

< p style="margin: 5px auto 5px 60px; padding: 0px; text-indent: 0px; color: rgb(57, 57, 57); font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 21px; orphans: 2; text-align: left; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(250, 247, 239); ">setDefaultImage...()类方法:设置默认图片。

< p style="margin: 5px auto 5px 60px; padding: 0px; text-indent: 0px; color: rgb(57, 57, 57); font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; font-size: 14px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 21px; orphans: 2; text-align: left; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(250, 247, 239); ">setPause(boolean pause):是否加载图处

资源下载