• Xutils 中BitmapUtils工具类的源码分析


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

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

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


    [java] view plaincopy
    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 plaincopy
    1. public BitmapSize getBitmapMaxSize() {  
    2.         return bitmapMaxSize == null ? BitmapSize.ZERO : bitmapMaxSize;  
    3.     }
    首先获得一个默认的bitmapsize对象

    [java] view plaincopy
    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 plaincopy
    1. callBack.onPreLoad(container, uri, displayConfig);  
    然后回调准备加载方法,这里是代码中是空实现。

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

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

    [java] view plaincopy
    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 plaincopy
    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 plaincopy
    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 plaincopy
    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 plaincopy
    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 plaincopy
    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 plaincopy
    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 plaincopy
    1. loadTask.executeOnExecutor(globalConfig.getBitmapLoadExecutor());

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

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

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

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

    [java] view plaincopy
    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 plaincopy
    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 plaincopy
    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 plaincopy
    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 plaincopy
    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

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


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


    处女作,轻喷~














  • 相关阅读:
    ASP.NET(4):多语言的解决方案
    无题
    SIP 计时器的总结(转)
    一个Sip协议栈的实现方案
    通过拦截WCF消息进行身份栈传播
    从WPF控件拖放到Winform控件的注意事项
    一个用C#操作OpenLDAP的例子
    通过定制行为拦截WCF消息
    一个基于Prism的方案的介绍
    MVVM模式下附加属性的使用
  • 原文地址:https://www.cnblogs.com/xingyun1992/p/5021089.html
Copyright © 2020-2023  润新知