Xutils 中BitmapUtils工具类的源码分析

项目中经常使用xutils框架的bitmaputils工具类来加载网络图片,用法极为方便,内置线程池开启多个线程来加载图片,内置lru算法防止内存溢出,也可以缓存本地,等等功能。

现在看看其中内部如何实现的。

bitmaputils类中大量的构造方法,配置缓存路径,缓存大小,加载时显示图片等等,不再赘述,直接从设置图片的方法入手。


[java]  view plain copy
  1. public <T extends View> void display(T container, String uri, BitmapDisplayConfig displayConfig, BitmapLoadCallBack<T> callBack) {  
  2.         if (container == null) {  
  3.             return;  
  4.         }  
  5.   
  6.         container.clearAnimation();  
  7.         //如果回调是空则创建默认  
  8.         if (callBack == null) {  
  9.             callBack = new SimpleBitmapLoadCallBack<T>();  
  10.         }  
  11.         //同上  
  12.         if (displayConfig == null || displayConfig == defaultDisplayConfig) {  
  13.             displayConfig = defaultDisplayConfig.cloneNew();  
  14.         }  
  15.   
  16.         // Optimize Max Size,确定设置图片的大小  
  17.         BitmapSize size = displayConfig.getBitmapMaxSize();  
  18.         displayConfig.setBitmapMaxSize(BitmapCommonUtils.optimizeMaxSizeByView(container, size.getWidth(), size.getHeight()));  
  19.   
  20.         callBack.onPreLoad(container, uri, displayConfig);  
  21.         //如果uri是空,回调加载失败方法  
  22.         if (TextUtils.isEmpty(uri)) {  
  23.             callBack.onLoadFailed(container, uri, displayConfig.getLoadFailedDrawable());  
  24.             return;  
  25.         }  
  26.         //从内存缓存中抓取该这个bitmap  
  27.         Bitmap bitmap = globalConfig.getBitmapCache().getBitmapFromMemCache(uri, displayConfig);  
  28.           
  29.         if (bitmap != null) {  
  30.             //显示图片,并且回调  
  31.             callBack.onLoadStarted(container, uri, displayConfig);  
  32.             callBack.onLoadCompleted(  
  33.                     container,  
  34.                     uri,  
  35.                     bitmap,  
  36.                     displayConfig,  
  37.                     BitmapLoadFrom.MEMORY_CACHE);  
  38.         } else if (!bitmapLoadTaskExist(container, uri, callBack)) {  
  39.   
  40.             final BitmapLoadTask<T> loadTask = new BitmapLoadTask<T>(container, uri, displayConfig, callBack);  
  41.             // set loading image  
  42.             final AsyncDrawable<T> asyncDrawable = new AsyncDrawable<T>(  
  43.                     displayConfig.getLoadingDrawable(),  
  44.                     loadTask);  
  45.             callBack.setDrawable(container, asyncDrawable);  
  46.   
  47.             // load bitmap from uri or diskCache  
  48.             loadTask.executeOnExecutor(globalConfig.getBitmapLoadExecutor());  
  49.         }  
  50.     }  
  51.  

display方法很多个重载,几经辗转最终调用这个方法。

17行开始,获取需要设置图片的大小,跟踪这个方法。

[java]  view plain copy
  1. public BitmapSize getBitmapMaxSize() {  
  2.         return bitmapMaxSize == null ? BitmapSize.ZERO : bitmapMaxSize;  
  3.     }
首先获得一个默认的bitmapsize对象

[java]  view plain copy
  1. public static BitmapSize optimizeMaxSizeByView(View view, int maxImageWidth, int maxImageHeight) {  
  2.         int width = maxImageWidth;  
  3.         int height = maxImageHeight;  
  4.   
  5.         if (width > 0 && height > 0) {  
  6.             return new BitmapSize(width, height);  
  7.         }  
  8.         //若params参数有值,即view的大小是固定,不是warp,match。  
  9.         final ViewGroup.LayoutParams params = view.getLayoutParams();  
  10.         if (params != null) {  
  11.             if (params.width > 0) {  
  12.                 width = params.width;  
  13.             } else if (params.width != ViewGroup.LayoutParams.WRAP_CONTENT) {  
  14.                 width = view.getWidth();  
  15.             }  
  16.   
  17.             if (params.height > 0) {  
  18.                 height = params.height;  
  19.             } else if (params.height != ViewGroup.LayoutParams.WRAP_CONTENT) {  
  20.                 height = view.getHeight();  
  21.             }  
  22.         }  
  23.        // 如果要设置图片的view是imageview,通过反射取到大小  
  24.         if (width <= 0) width = getImageViewFieldValue(view, "mMaxWidth");  
  25.         if (height <= 0) height = getImageViewFieldValue(view, "mMaxHeight");  
  26.         //若都没有,则屏幕大小  
  27.         BitmapSize screenSize = getScreenSize(view.getContext());  
  28.         if (width <= 0) width = screenSize.getWidth();  
  29.         if (height <= 0) height = screenSize.getHeight();  
  30.   
  31.         return new BitmapSize(width, height);  
  32.     }

根据这个bitmapsize大小和view的大小得出最终设置图片的大小并且设置到配置对象中

[java]  view plain copy
  1. callBack.onPreLoad(container, uri, displayConfig);  
然后回调准备加载方法,这里是代码中是空实现。

[java]  view plain copy
  1. Bitmap bitmap = globalConfig.getBitmapCache().getBitmapFromMemCache(uri, displayConfig);  

这句很重要,取内存缓存中的bitmap对象,若有,则直接显示即可,如无,则需要加载。追踪这句代码

[java]  view plain copy
  1. public Bitmap getBitmapFromMemCache(String uri, BitmapDisplayConfig config) {  
  2.         if (mMemoryCache != null && globalConfig.isMemoryCacheEnabled()) {  
  3.             MemoryCacheKey key = new MemoryCacheKey(uri, config == null ? null : config.toString());  
  4.             return mMemoryCache.get(key);  
  5.         }  
  6.         return null;  
  7.     }

这里根据uri生成key,从lru队列中取出这个bitmap对象

[java]  view plain copy
  1. mMemoryCache = new LruMemoryCache<MemoryCacheKey, Bitmap>(globalConfig.getMemoryCacheSize()) {  
  2.             /** 
  3.              * Measure item size in bytes rather than units which is more practical 
  4.              * for a bitmap cache 
  5.              * 测量一个bitmap对象所占的内存大小 
  6.              */  
  7.             @Override  
  8.             protected int sizeOf(MemoryCacheKey key, Bitmap bitmap) {  
  9.                 if (bitmap == nullreturn 0;  
  10.                 return bitmap.getRowBytes() * bitmap.getHeight();  
  11.             }  
  12.         }; 
这里是lru对象的创建,构造方法传入最大缓存数值,重写单个对象占用字节大小方法,所以这里有一个很重要的地方,bitmapuitls尽可能实现单例,每创建一个bitmapuitls就会创建一个lru队列,而只是对与一小部分进行缓存管理,并没有对全局,所以该内存溢出还是会溢出的。   而MemoryCacheKey是一个实体类,并且重写了equals()方法

[java]  view plain copy
  1. public class MemoryCacheKey {  
  2.         private String uri;  
  3.         private String subKey;  
  4.   
  5.         private MemoryCacheKey(String uri, String subKey) {  
  6.             this.uri = uri;  
  7.             this.subKey = subKey;  
  8.         }  
  9.   
  10.         @Override  
  11.         public boolean equals(Object o) {  
  12.             if (this == o) return true;  
  13.             if (!(o instanceof MemoryCacheKey)) return false;  
  14.   
  15.             MemoryCacheKey that = (MemoryCacheKey) o;  
  16.   
  17.             if (!uri.equals(that.uri)) return false;  
  18.   
  19.             if (subKey != null && that.subKey != null) {  
  20.                 return subKey.equals(that.subKey);  
  21.             }  
  22.   
  23.             return true;  
  24.         }  
  25.   
  26.         @Override  
  27.         public int hashCode() {  
  28.             return uri.hashCode();  
  29.         }  
  30.     }
这里的equals方法比较特殊,其实就是比较两个键值是否相同,第15行开始,将传进来的object对象强转成该对象,比较uri和subkey是否相等。


接下去看,读缓存图片就不说了,看bitmap为空该如何。

[java]  view plain copy
  1. else if (!bitmapLoadTaskExist(container, uri, callBack)) {  
  2.   
  3.             final BitmapLoadTask<T> loadTask = new BitmapLoadTask<T>(container, uri, displayConfig, callBack);  
  4.             // set loading image  
  5.             final AsyncDrawable<T> asyncDrawable = new AsyncDrawable<T>(  
  6.                     displayConfig.getLoadingDrawable(),  
  7.                     loadTask);  
  8.             callBack.setDrawable(container, asyncDrawable);  
  9.   
  10.             // load bitmap from uri or diskCache  
  11.             loadTask.executeOnExecutor(globalConfig.getBitmapLoadExecutor());  
  12.         }
if中判断当前bitmap加载任务是否存在,若不存在则给这个图片设置加载任务,追踪这句代码

[java]  view plain copy
  1. private static <T extends View> boolean bitmapLoadTaskExist(T container, String uri, BitmapLoadCallBack<T> callBack) {  
  2.         final BitmapLoadTask<T> oldLoadTask = getBitmapTaskFromContainer(container, callBack);  
  3.   
  4.         if (oldLoadTask != null) {  
  5.             final String oldUrl = oldLoadTask.uri;  
  6.             if (TextUtils.isEmpty(oldUrl) || !oldUrl.equals(uri)) {  
  7.                 oldLoadTask.cancel(true);  
  8.             } else {  
  9.                 return true;  
  10.             }  
  11.         }  
  12.         return false;  
  13.     } 
这里先获取这个view的加载任务,若为空或者uri不相等,则说明任务不存在,那现在看看如何获取oldLoadTask的

[java]  view plain copy
  1.  private static <T extends View> BitmapLoadTask<T> getBitmapTaskFromContainer(T container, BitmapLoadCallBack<T> callBack) {  
  2.         if (container != null) {  
  3.             final Drawable drawable = callBack.getDrawable(container);  
  4.             if (drawable instanceof AsyncDrawable) {  
  5.                 final AsyncDrawable<T> asyncDrawable = (AsyncDrawable<T>) drawable;  
  6.                 return asyncDrawable.getBitmapWorkerTask();  
  7.             }  
  8.         }  
  9.         return null;  
  10.     }
根据传进来的view来抓取他的drawable,判断该drawable对象是否属于AsyncDrawable,如果是,则从AsyncDrawable中取出加载任务,

在这里很好奇的是,AsyncDrawable是个什么东西,看一下代码

[java]  view plain copy
  1. public class AsyncDrawable<T extends View> extends Drawable {  
  2.   
  3.     private final WeakReference<BitmapUtils.BitmapLoadTask<T>> bitmapLoadTaskReference;  
  4.   
  5.     private final Drawable baseDrawable;  
  6.   
  7.     public AsyncDrawable(Drawable drawable, BitmapUtils.BitmapLoadTask<T> bitmapWorkerTask) {  
  8.         if (drawable == null) {  
  9.             throw new IllegalArgumentException("drawable may not be null");  
  10.         }  
  11.         if (bitmapWorkerTask == null) {  
  12.             throw new IllegalArgumentException("bitmapWorkerTask may not be null");  
  13.         }  
  14.         baseDrawable = drawable;  
  15.         bitmapLoadTaskReference = new WeakReference<BitmapUtils.BitmapLoadTask<T>>(bitmapWorkerTask);  
  16.     }  
  17.   
  18.     public BitmapUtils.BitmapLoadTask<T> getBitmapWorkerTask() {  
  19.         return bitmapLoadTaskReference.get();  
  20.     }  
  21. //省略大量代码,都是和drawable绘制有关

AsyncDrawable继承了Drawable,在里面使用弱引用bitmapLoadTaskReference把加载任务装进去,构造方法传入加载任务,提供get方法取出加载任务


好,回到主线,若加载任务为空,则创建 BitmapLoadTask,创建AsyncDrawable对象,构造方法传入加载时显示的图片和加载任务,给view设置上drawable。

调用终极方法

[java]  view plain copy
  1. loadTask.executeOnExecutor(globalConfig.getBitmapLoadExecutor());

不过调用方法和往常不大一样,平时用异步任务异步都是调用execute(),而这个却是executeOnExecutor,还要传入一个奇怪的东西。

(再往后面涉及大量Java多线程知识,我这方面也很弱,如有讲错,轻喷、)

不了解系统异步任务的可以看看Android AsyncTask源码解析,洋洋写的很牛逼,大致说明了Android3.0之后系统AsyncTask的execute采用单线程队列形式处理并发请求,同时一百个异步任务开启,也只会按照队列的形式一个接一个执行。

现在使用executeOnExecutor(),使用线程池的方式来并发执行任务,来看看传进什么去了

[java]  view plain copy
  1. public ExecutorService getBitmapLoadExecutor() {  
  2.         if (_dirty_params_bitmapLoadExecutor || bitmapLoadExecutor == null) {  
  3.             bitmapLoadExecutor = Executors.newFixedThreadPool(getThreadPoolSize(), sThreadFactory);  
  4.             _dirty_params_bitmapLoadExecutor = false;  
  5.         }  
  6.         return bitmapLoadExecutor;  
  7.     }

创建一个带固定数量的线程池。构造方法传入一个线程工厂sThreadFactory,看看这个什么做什么用的

[java]  view plain copy
  1. private static final ThreadFactory sThreadFactory = new ThreadFactory() {  
  2.         private final AtomicInteger mCount = new AtomicInteger(1);  
  3.   
  4.         @Override  
  5.         public Thread newThread(Runnable r) {  
  6.             Thread thread = new Thread(r, "BitmapUtils #" + mCount.getAndIncrement());  
  7.             thread.setPriority(Thread.NORM_PRIORITY - 1);  
  8.             return thread;  
  9.         }  
  10.     };
AtomicInteger是一个线程安全的自加器,因为在Java中i++是线程不安全的,若要使用需要加上synchronized,使用这个方便些。

接着重写newThread方法,定义用此线程池创建线程的规范,如线程名,线程优先级。

[java]  view plain copy
  1. public class BitmapLoadTask<T extends View> extends CompatibleAsyncTask<Object, Object, Bitmap> {  
  2.        private final String uri;  
  3.        //这个是弱引用  
  4.        private final WeakReference<T> containerReference;  
  5.        private final BitmapLoadCallBack<T> callBack;  
  6.        private final BitmapDisplayConfig displayConfig;  
  7.   
  8.        private BitmapLoadFrom from = BitmapLoadFrom.DISK_CACHE;  
  9.   
  10.        public BitmapLoadTask(T container, String uri, BitmapDisplayConfig config, BitmapLoadCallBack<T> callBack) {  
  11.            if (container == null || uri == null || config == null || callBack == null) {  
  12.                throw new IllegalArgumentException("args may not be null");  
  13.            }  
  14.   
  15.            this.containerReference = new WeakReference<T>(container);  
  16.            this.callBack = callBack;  
  17.            this.uri = uri;  
  18.            this.displayConfig = config;  
  19.        }  
发现,图片加载任务并非继承系统的AsyncTask,而是使用兼容性的AsyncTask,这玩意是根据系统的改造过来的,详细也很复杂的,构造方法不说,看看接下来的doInBackground方法

[java]  view plain copy
  1. protected Bitmap doInBackground(Object... params) {  
  2.   
  3.            synchronized (pauseTaskLock) {  
  4.                while (pauseTask && !this.isCancelled()) {  
  5.                    try {  
  6.                        pauseTaskLock.wait();  
  7.                    } catch (Throwable e) {  
  8.                    }  
  9.                }  
  10.            }  
  11.   
  12.            Bitmap bitmap = null;  
  13.   
  14.            // get cache from disk cache  
  15.            if (!this.isCancelled() && this.getTargetContainer() != null) {  
  16.                this.publishProgress(PROGRESS_LOAD_STARTED);  
  17.                bitmap = globalConfig.getBitmapCache().getBitmapFromDiskCache(uri, displayConfig);  
  18.            }  
  19.   
  20.            // download image  
  21.            if (bitmap == null && !this.isCancelled() && this.getTargetContainer() != null) {  
  22.                bitmap = globalConfig.getBitmapCache().downloadBitmap(uri, displayConfig, this);  
  23.                from = BitmapLoadFrom.URI;  
  24.            }  
  25.   
  26.            return bitmap;  
  27.        }  

前几行是线程挂起的常用手段,定义一个object对象来锁线程,当需要线程暂停时,改变pauseTask的值,调用wait方法,线程休眠并且释放对象锁让外部线程访问,知道外面线程调用notify方法,将唤醒线程。在listview滑动暂停加载时,就是调用了这里方法。

接着从本地缓存中读取图片

[java]  view plain copy
  1. public Bitmap getBitmapFromDiskCache(String uri, BitmapDisplayConfig config) {  
  2.         if (uri == null || !globalConfig.isDiskCacheEnabled()) return null;  
  3.         synchronized (mDiskCacheLock) {  
  4.             while (!isDiskCacheReadied) {  
  5.                 try {  
  6.                     mDiskCacheLock.wait();  
  7.                 } catch (Throwable e) {  
  8.                 }  
  9.             }  
  10.             if (mDiskLruCache != null) {  
  11.                 LruDiskCache.Snapshot snapshot = null;  
  12.                 try {  
  13.                     snapshot = mDiskLruCache.get(uri);  
  14.                     if (snapshot != null) {  
  15.                         Bitmap bitmap = null;  
  16.                         if (config == null || config.isShowOriginal()) {  
  17.                             bitmap = BitmapDecoder.decodeFileDescriptor(  
  18.                                     snapshot.getInputStream(DISK_CACHE_INDEX).getFD());  
  19.                         } else {  
  20.                             bitmap = BitmapDecoder.decodeSampledBitmapFromDescriptor(  
  21.                                     snapshot.getInputStream(DISK_CACHE_INDEX).getFD(),  
  22.                                     config.getBitmapMaxSize(),  
  23.                                     config.getBitmapConfig());  
  24.                         }  
  25.   
  26.                         bitmap = rotateBitmapIfNeeded(uri, config, bitmap);  
  27.                         addBitmapToMemoryCache(uri, config, bitmap, mDiskLruCache.getExpiryTimestamp(uri));  

  28.                         return bitmap;  
  29.                     }  
  30.                 } catch (Throwable e) {  
  31.                     LogUtils.e(e.getMessage(), e);  
  32.                 } finally {  
  33.                     IOUtils.closeQuietly(snapshot);  
  34.                 }  
  35.             }  
  36.             return null;  
  37.         }  
  38.     }  

这里涉及了disklrucache,这块也挺复杂的,依旧推荐洋洋的DiskLruCache源码解析

这块基本操作就是从本地缓存中取出图片,值得注意的

 bitmap = rotateBitmapIfNeeded(uri, config, bitmap);
 addBitmapToMemoryCache(uri, config, bitmap, mDiskLruCache.getExpiryTimestamp(uri));

第一句是旋转bitmap图片,第二句是将这个bitmap添加到内存缓存中。

private Bitmap rotateBitmapIfNeeded(String uri, BitmapDisplayConfig config, Bitmap bitmap) {
        Bitmap result = bitmap;
        if (config != null && config.isAutoRotation()) {
            File bitmapFile = this.getBitmapFileFromDiskCache(uri);
            if (bitmapFile != null && bitmapFile.exists()) {
                ExifInterface exif = null;
                try {
                    exif = new ExifInterface(bitmapFile.getPath());
                } catch (Throwable e) {
                    return result;
                }
                int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
                int angle = 0;
                switch (orientation) {
                    case ExifInterface.ORIENTATION_ROTATE_90:
                        angle = 90;
                        break;
                    case ExifInterface.ORIENTATION_ROTATE_180:
                        angle = 180;
                        break;
                    case ExifInterface.ORIENTATION_ROTATE_270:
                        angle = 270;
                        break;
                    default:
                        angle = 0;
                        break;
                }
                if (angle != 0) {
                    Matrix m = new Matrix();
                    m.postRotate(angle);
                    result = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m, true);
                    bitmap.recycle();
                    bitmap = null;
                }
            }
        }
        return result;
    }
方法开始读取图片,关键点从这里开始

 exif = new ExifInterface(bitmapFile.getPath());
ExifInterface是个拍照api, 这个接口提供了图片文件的旋转,gps,时间等信息。Exifinterface详解

这里读取出图片的旋转信息,因为有时候拍照会横屏拍,这时候可能会出现图片是横着的情况,这里把图片旋转回去了。

private void addBitmapToMemoryCache(String uri, BitmapDisplayConfig config, Bitmap bitmap, long expiryTimestamp) throws IOException {
        if (uri != null && bitmap != null && globalConfig.isMemoryCacheEnabled() && mMemoryCache != null) {
            MemoryCacheKey key = new MemoryCacheKey(uri, config == null ? null : config.toString());
            mMemoryCache.put(key, bitmap, expiryTimestamp);
        }
    }
这里创建key,把这个bitmap缓存到内存缓存中。

回到主线,如果本地缓存没有,那么就需要从网络上面获取下来。download方法挺大的,大致逻辑:先判断是否启用磁盘缓存,若有,启用disklrucache缓存图片,若无,直接下载到内存中。最后自动转图片+添加到内存缓存中。

 public Bitmap downloadBitmap(String uri, BitmapDisplayConfig config, final BitmapUtils.BitmapLoadTask<?> task) {

        BitmapMeta bitmapMeta = new BitmapMeta();

        OutputStream outputStream = null;
        LruDiskCache.Snapshot snapshot = null;

        try {

            Bitmap bitmap = null;
            // try download to disk
            if (globalConfig.isDiskCacheEnabled()) {
                synchronized (mDiskCacheLock) {
                    // Wait for disk cache to initialize
                    while (!isDiskCacheReadied) {
                        try {
                            mDiskCacheLock.wait();
                        } catch (Throwable e) {
                        }
                    }

                    if (mDiskLruCache != null) {
                        try {
                            snapshot = mDiskLruCache.get(uri);
                            if (snapshot == null) {
                                LruDiskCache.Editor editor = mDiskLruCache.edit(uri);
                                if (editor != null) {
                                    outputStream = editor.newOutputStream(DISK_CACHE_INDEX);
                                    bitmapMeta.expiryTimestamp = globalConfig.getDownloader().downloadToStream(uri, outputStream, task);
                                    if (bitmapMeta.expiryTimestamp < 0) {
                                        editor.abort();
                                        return null;
                                    } else {
                                        editor.setEntryExpiryTimestamp(bitmapMeta.expiryTimestamp);
                                        editor.commit();
                                    }
                                    snapshot = mDiskLruCache.get(uri);
                                }
                            }
                            if (snapshot != null) {
                                bitmapMeta.inputStream = snapshot.getInputStream(DISK_CACHE_INDEX);
                                bitmap = decodeBitmapMeta(bitmapMeta, config);
                                if (bitmap == null) {
                                    bitmapMeta.inputStream = null;
                                    mDiskLruCache.remove(uri);
                                }
                            }
                        } catch (Throwable e) {
                            LogUtils.e(e.getMessage(), e);
                        }
                    }
                }
            }

            // try download to memory stream,说明手机sd卡不能用,下载到内存中
            if (bitmap == null) {
                outputStream = new ByteArrayOutputStream();
                bitmapMeta.expiryTimestamp = globalConfig.getDownloader().downloadToStream(uri, outputStream, task);
                if (bitmapMeta.expiryTimestamp < 0) {
                    return null;
                } else {
                    bitmapMeta.data = ((ByteArrayOutputStream) outputStream).toByteArray();
                    bitmap = decodeBitmapMeta(bitmapMeta, config);
                }
            }

            if (bitmap != null) {//你懂得
                bitmap = rotateBitmapIfNeeded(uri, config, bitmap);
                addBitmapToMemoryCache(uri, config, bitmap, bitmapMeta.expiryTimestamp);
            }
            return bitmap;
        } catch (Throwable e) {
            LogUtils.e(e.getMessage(), e);
        } finally {
            IOUtils.closeQuietly(outputStream);
            IOUtils.closeQuietly(snapshot);
        }

        return null;
    }

24行开始,是disklrucache存图片的标准用法,需要一个edit对象,利用这个对象创建输出流,传进downloadToStream方法。跟踪这个方法,Downloader类是一个抽象类,实现在SimpleDownloader。

public long downloadToStream(String uri, OutputStream outputStream, final BitmapUtils.BitmapLoadTask<?> task) {

        if (task == null || task.isCancelled() || task.getTargetContainer() == null) return -1;

        URLConnection urlConnection = null;
        BufferedInputStream bis = null;

        OtherUtils.trustAllSSLForHttpsURLConnection();

        long result = -1;
        long fileLen = 0;
        long currCount = 0;
        try {
            if (uri.startsWith("/")) {
                FileInputStream fileInputStream = new FileInputStream(uri);
                fileLen = fileInputStream.available();
                bis = new BufferedInputStream(fileInputStream);
                result = System.currentTimeMillis() + this.getDefaultExpiry();
            } else if (uri.startsWith("assets/")) {
                InputStream inputStream = this.getContext().getAssets().open(uri.substring(7, uri.length()));
                fileLen = inputStream.available();
                bis = new BufferedInputStream(inputStream);
                result = Long.MAX_VALUE;
            } else {
                final URL url = new URL(uri);
                urlConnection = url.openConnection();
                urlConnection.setConnectTimeout(this.getDefaultConnectTimeout());
                urlConnection.setReadTimeout(this.getDefaultReadTimeout());
                bis = new BufferedInputStream(urlConnection.getInputStream());
                result = urlConnection.getExpiration();
                result = result < System.currentTimeMillis() ? System.currentTimeMillis() + this.getDefaultExpiry() : result;
                fileLen = urlConnection.getContentLength();
            }

            if (task.isCancelled() || task.getTargetContainer() == null) return -1;

            byte[] buffer = new byte[4096];
            int len = 0;
            while ((len = bis.read(buffer)) != -1) {
                outputStream.write(buffer, 0, len);
                currCount += len;
                if (task.isCancelled() || task.getTargetContainer() == null) return -1;
                task.updateProgress(fileLen, currCount);
            }
            outputStream.flush();
        } catch (Throwable e) {
            result = -1;
            LogUtils.e(e.getMessage(), e);
        } finally {
            IOUtils.closeQuietly(bis);
        }
        return result;
    }

这里终于看到了,他是如何识别要加载的是本地图片还是网络图片。本地图片好办,传进路径建立起输入流,这里网络图片用的是urlConnection里建立http连接,一般安卓里面都是用httpclient,这是原声Java的api,这里可以了解一下: urlconnection

拿到输入流就好办,边读边写,把流写到本地缓存文件中。


到此,基本结束。(虽然里面还要很多很多方法,但确实能力有限)


处女作,轻喷~














  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值