Android图片异步加载

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

  最近一段处理跟图片相关的问题,本来是自己写的图片加载,不过有些状态的控制还是比较烦人的,比如ListView滚动时ImageView的重用,所以本着偷懒与充分利用现有资源的态度去网上搜罗图片异步加载的代码,最终在GreenDroid UI库中找到一个,其中有个AsyncImageView的自定义View用于异步加载图片,不过也像网上的大多数图片异步加载方法一样,是跟图片的缓存关联在一起的,不过只是很简单的内存缓存,无文件缓存。图片的加载方法也如其他的一样是写死了的,这就限制了其使用范围,只可通过InputStream来decode图片,而像生成缩略图或其他一些图片处理的异步处理就无法用途。修改现有类库总比自己从头写来的简单,于是稍微修改了下AsyncImageView,使其可以自定义缓存与图片加载方法,对于AsyncImageView只有一点点的修改,大都是别人源码。

1. 核心类

  ImageLoader:图片加载核心类,内部使用线程池加载图片

  ImageRequest:表示一个图片加载的请求

  AsyncImageView:自定义的图片异步加载View

  LoadMethod:自定义图片加载方法的接口,可以通过实现此接口来自定义图片的加载方法

  CacheCallback:缓存接口,可以通过实现此接口实现对缓存的读写

AsyncImageView.OnImageViewLoadListener:图片加载状态监听(开始,失败,结束)

2。图片加载方法

复制代码
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);
                }
            }
        }
复制代码

  如果自定义了LoadMethod,会调用相应的方法加载图片,如果没有自定义,会使用默认的加载方法,可以加载本地图片,Asset图片与网络图片,GreenDroid的源码中加载网络图片是用的URL的,但我们以前在加载网络图片时遇到一个问题,有的机型通过URL类获得的ImputStream解析图片总是返回null,所以就改为了HttpClient。

3。使用方法

通过AsyncImageView的setPath方法来加载图片,setPath有3个重载方法:

public void setPath(String path)

public void setPath(String path, LoadMethod loadMethod)

public void setPath(String path, LoadMethod loadMethod, String cacheKey)

第一个参数指定要加载的图片的路径,第二个参数为自定义的图片加载方法,若不指定则用默认的。

至于加第三个参数,是做缓存用的,一般要加载的图片的路径都是唯一的,所以一般用第一个参数来做为缓存的Key就行了,但也有特殊情况,比如读取局域网中的图片,一般都是自动获取IP,所以根据图片路径做为缓存的Key可能是不合适的,所以就需要根据需要手动指定用来作为缓存的Key。

复制代码
/**
     * 设置要加载的图片的路径, 可为网络路径, 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();
                }
            }
        }
    }
复制代码
复制代码
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);
            }
        }
复制代码

  readCache()用于读取缓存,代码如下:

    private Bitmap readCache() {
        if (mCache != null)
            return mCache.readCache(TextUtils.isEmpty(mCacheKey) ? mUrl : mCacheKey);
        return null;
    }

  其中的mCache由用户能过setCacheCallback(CacheCallback callback)设置用户自定义的缓存方法,由此将图片的加载与缓存分离开,使用户可以使用现有的缓存实现。如要用户指定了缓存Key就使用用户指定的Key,否则就用图片的路径作Key。

4.AsyncImageView中的其他重要方法

reload([boolean force]):重新加载

stopLoading():停止加载,如果当前正在加载则没有效果,如果加载任务在加载线程池队列中则取消。

setDefaultImage...()类方法:设置默认图片。

setPause(boolean pause):是否加载图处

 

源码下载:AsyncImage

相关文章
|
5月前
|
XML Android开发 数据格式
Android利用selector(选择器)实现图片动态点击效果
本文介绍了Android中ImageView的`src`与`background`属性的区别及应用,重点讲解如何通过设置背景选择器实现图片点击动态效果。`src`用于显示原图大小,不拉伸;`background`可随组件尺寸拉伸。通过创建`selector_setting.xml`,结合`setting_press.xml`和`setting_normal.xml`定义按下和正常状态的背景样式,提升用户体验。示例代码展示了具体实现步骤,包括XML配置和形状定义。
231 3
Android利用selector(选择器)实现图片动态点击效果
|
5月前
|
Java Android开发
Android图片的手动放大缩小
本文介绍了通过缩放因子实现图片放大缩小的功能,效果如动图所示。关键步骤包括:1) 在布局文件中设置 `android:scaleType="matrix"`;2) 实例化控件并用 `ScaleGestureDetector` 处理缩放手势;3) 使用 `Matrix` 对图片进行缩放处理。为避免内存崩溃,可在全局配置添加 `android:largeHeap="true"`。代码中定义了 `beforeScale` 和 `nowScale` 变量控制缩放范围,确保流畅体验。
165 8
|
5月前
|
缓存 编解码 Android开发
Android内存优化之图片优化
本文主要探讨Android开发中的图片优化问题,包括图片优化的重要性、OOM错误的成因及解决方法、Android支持的图片格式及其特点。同时介绍了图片储存优化的三种方式:尺寸优化、质量压缩和内存重用,并详细讲解了相关的实现方法与属性。此外,还分析了图片加载优化策略,如异步加载、缓存机制、懒加载等,并结合多级缓存流程提升性能。最后对比了几大主流图片加载框架(Universal ImageLoader、Picasso、Glide、Fresco)的特点与适用场景,重点推荐Fresco在处理大图、动图时的优异表现。这些内容为开发者提供了全面的图片优化解决方案。
181 1
|
Android开发
Android通过手势(多点)缩放和拖拽图片
Android通过手势(多点)缩放和拖拽图片
170 4
|
Java Android开发
android 下载图片的问题
android 下载图片的问题
116 3
|
存储 缓存 编解码
Android经典面试题之图片Bitmap怎么做优化
本文介绍了图片相关的内存优化方法,包括分辨率适配、图片压缩与缓存。文中详细讲解了如何根据不同分辨率放置图片资源,避免图片拉伸变形;并通过示例代码展示了使用`BitmapFactory.Options`进行图片压缩的具体步骤。此外,还介绍了Glide等第三方库如何利用LRU算法实现高效图片缓存。
171 20
Android经典面试题之图片Bitmap怎么做优化
|
XML JSON Java
Android App开发即时通信中通过SocketIO在客户端与服务端间传输文本和图片的讲解及实战(超详细 附源码)
Android App开发即时通信中通过SocketIO在客户端与服务端间传输文本和图片的讲解及实战(超详细 附源码)
761 0
|
数据处理 开发工具 数据安全/隐私保护
Android平台RTMP推送|轻量级RTSP服务|GB28181接入之文字、png图片水印的精进之路
本文探讨了Android平台上推流模块中添加文字与PNG水印的技术演进。自2015年起,为了满足应急指挥及安防领域的需求,逐步发展出三代水印技术:第一代为静态文字与图像水印;第二代实现了动态更新水印内容的能力,例如实时位置与时间信息;至第三代,则优化了数据传输效率,直接使用Bitmap对象传递水印数据至JNI层,减少了内存拷贝次数。这些迭代不仅提升了用户体验和技术效率,也体现了开发者追求极致与不断创新的精神。
157 7
|
自然语言处理 定位技术 API
Android经典实战之如何获取图片的经纬度以及如何根据经纬度获取对应的地点名称
本文介绍如何在Android中从图片提取地理位置信息并转换为地址。首先利用`ExifInterface`获取图片内的经纬度,然后通过`Geocoder`将经纬度转为地址。注意操作需在子线程进行且考虑多语言支持。
901 4
|
XML 前端开发 Android开发
Android经典实战之Kotlin中实现圆角图片和圆形图片
本文介绍两种实现圆角图像视图的方法。第一种是通过自定义Kotlin `AppCompatImageView`,重写`onDraw`方法使用`Canvas`和`Path`进行圆角剪裁。第二种利用Android Material库中的`ShapeableImageView`,简单配置即可实现圆角效果。两种方法均易于实现且提供动态调整圆角半径的功能。
262 0

热门文章

最新文章