即日起在codingBlog上分享您的技术经验即可获得积分,积分可兑换现金哦。

ImageLoader

编程语言 lawei2012 5℃ 0评论
public class ImageLoader {
    public static final int UNCONSTRAINED = -1;

    private static final String TAG = "ImageLoader";
    private static final boolean DEBUG = false;

    // Queue of work to do in the worker thread. The work is done in order.
    private final ArrayList mQueue = new ArrayList();

    // the worker thread and a done flag so we know when to exit
    private boolean mDone;
    private Thread mDecodeThread;
    private ContentResolver mCr;

    private LruCache memoryCache;

    private static Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            Map data = (Map) msg.obj;
            WorkItem workItem = (WorkItem) data.get("workItem");

            Bitmap bitmap = (Bitmap) data.get("bitmap");
            workItem.mOnLoadedRunnable.onLoadingComplete(bitmap);
            if (DEBUG) Log.i(TAG, String.valueOf(workItem.mImage.getId()) + workItem.mIsThumb + " called listener");
            return false;
        }
    });

    public void getBitmap(ImageData image,
                          OnLoaderCompleteListener imageLoadedRunnable,
                          boolean isThumb) {
        if (mDecodeThread == null) {
            start();
        }
        synchronized (mQueue) {
            WorkItem w = new WorkItem(image, imageLoadedRunnable, isThumb);
            mQueue.add(w);
            mQueue.notifyAll();
        }
    }

    /**
     * @param filePath 文件路径
     * @return 文件数据
     */
    public Bitmap getBitmapFromPath(int minSideLength, int maxNumOfPixels, String filePath) {
        try {
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(filePath, options);
            if (options.mCancel || options.outWidth == -1
                    || options.outHeight == -1) {
                return null;
            }
            options.inSampleSize = computeSampleSize(
                    options, minSideLength, maxNumOfPixels);
            options.inJustDecodeBounds = false;

            options.inDither = false;
            options.inPreferredConfig = Bitmap.Config.ARGB_8888;
            return BitmapFactory.decodeFile(filePath, options);
        } catch (OutOfMemoryError ex) {
            Log.e(TAG, "Got oom exception ", ex);
            return null;
        }
    }

    public Bitmap makeBitmap(int minSideLength, int maxNumOfPixels,
                             Uri uri, ContentResolver cr, ParcelFileDescriptor pfd,
                             BitmapFactory.Options options) {
        try {
            if (pfd == null) pfd = makeInputStream(uri, cr);
            if (pfd == null) return null;
            if (options == null) options = new BitmapFactory.Options();

            FileDescriptor fd = pfd.getFileDescriptor();
            options.inJustDecodeBounds = true;
//            BitmapManager.instance().decodeFileDescriptor(fd, options);
            BitmapFactory.decodeFileDescriptor(fd, null, options);
            if (options.mCancel || options.outWidth == -1
                    || options.outHeight == -1) {
                return null;
            }
            options.inSampleSize = computeSampleSize(
                    options, minSideLength, maxNumOfPixels);
            options.inJustDecodeBounds = false;

            options.inDither = false;
            options.inPreferredConfig = Bitmap.Config.ARGB_8888;
            return BitmapFactory.decodeFileDescriptor(fd, null, options);
        } catch (OutOfMemoryError ex) {
            Log.e(TAG, "Got oom exception ", ex);
            return null;
        } finally {
            closeSilently(pfd);
        }
    }

    public static int computeSampleSize(BitmapFactory.Options options,
                                        int minSideLength, int maxNumOfPixels) {
        int initialSize = computeInitialSampleSize(options, minSideLength,
                maxNumOfPixels);

        int roundedSize;
        if (initialSize <= 8) {
            roundedSize = 1;
            while (roundedSize < initialSize) {
                roundedSize <<= 1;
            }
        } else {
            roundedSize = (initialSize + 7) / 8 * 8;
        }

        return roundedSize;
    }

    private static int computeInitialSampleSize(BitmapFactory.Options options,
                                                int minSideLength, int maxNumOfPixels) {
        double w = options.outWidth;
        double h = options.outHeight;

        int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 :
                (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
        int upperBound = (minSideLength == UNCONSTRAINED) ? 128 :
                (int) Math.min(Math.floor(w / minSideLength),
                        Math.floor(h / minSideLength));

        if (upperBound < lowerBound) {
            // return the larger one when there is no overlapping zone.
            return lowerBound;
        }

        if ((maxNumOfPixels == UNCONSTRAINED) &&
                (minSideLength == UNCONSTRAINED)) {
            return 1;
        } else if (minSideLength == UNCONSTRAINED) {
            return lowerBound;
        } else {
            return upperBound;
        }
    }

    private ParcelFileDescriptor makeInputStream(
            Uri uri, ContentResolver cr) {
        try {
            return cr.openFileDescriptor(uri, "r");
        } catch (IOException ex) {
            return null;
        }
    }

    public void closeSilently(ParcelFileDescriptor c) {
        if (c == null) return;
        try {
            c.close();
        } catch (Throwable t) {
            // do nothing
        }
    }

    public boolean cancel(final ImageData image) {
        synchronized (mQueue) {
            int index = findItem(image);
            if (index >= 0) {
                mQueue.remove(index);
                return true;
            } else {
                return false;
            }
        }
    }

    // The caller should hold mQueue lock.
    private int findItem(ImageData image) {
        for (int i = 0; i < mQueue.size(); i++) {
            if (mQueue.get(i).mImage == image) {
                return i;
            }
        }
        return -1;
    }

    // Clear the queue. Returns an array of tags that were in the queue.
    public void clearQueue() {
        synchronized (mQueue) {
            for (WorkItem workItem : mQueue) {
                workItem.mImage = null;
                workItem.mOnLoadedRunnable = null;
            }
            mQueue.clear();
        }
    }

    private static class WorkItem {
        ImageData mImage;
        OnLoaderCompleteListener mOnLoadedRunnable;
        boolean mIsThumb;

        WorkItem(ImageData image, OnLoaderCompleteListener onLoadedRunnable, boolean isThumb) {
            mImage = image;
            mOnLoadedRunnable = onLoadedRunnable;
            mIsThumb = isThumb;
        }
    }

    public ImageLoader(ContentResolver cr) {
        mCr = cr;

        //获取系统分配给每个应用程序的最大内存
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        int mCacheSize = maxMemory / 2;
        //给LruCache分配1/2最大内存
        memoryCache = new LruCache(mCacheSize) {

            //必须重写此方法,来测量Bitmap的大小
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getRowBytes() * value.getHeight();
            }

            /**
             * 当bitmap被清出缓存的时候,回收所占内存
             */
            @Override
            protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
                if (key.contains("false")) {
                    oldValue.recycle();
                    oldValue = null;
                }
                if (DEBUG) Log.i(TAG, key + " is removed from cache");
            }

        };

        start();
    }

    private void addBitmapToMemoryCache(String key, Bitmap bitmap) {
        if (getBitmapFromMemCache(key) == null && bitmap != null) {
            memoryCache.put(key, bitmap);
        }
    }

    private Bitmap getBitmapFromMemCache(String key) {
        return memoryCache.get(key);
    }

    private class WorkerThread implements Runnable {

        // Pick off items on the queue, one by one, and compute their bitmap.
        // Place the resulting bitmap in the cache, then call back by executing
        // the given runnable so things can get updated appropriately.
        public void run() {
            while (true) {
                WorkItem workItem = null;
                synchronized (mQueue) {
                    if (mDone) {
                        break;
                    }
                    if (!mQueue.isEmpty()) {
                        workItem = mQueue.remove(0);
                    } else {
                        try {
                            mQueue.wait();
                        } catch (InterruptedException ex) {
                            // ignore the exception
                        }
                        continue;
                    }
                }

                Message msg = new Message();
                Map msgMap = new HashMap<>();
                msgMap.put("workItem", workItem);
                Bitmap bitmap = getBitmapFromMemCache(String.valueOf(workItem.mImage.getId()) + workItem.mIsThumb);
                if (bitmap == null) {
                    if (workItem.mImage.getType() == MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO) {
                        if (workItem.mIsThumb) {
                            long longId = workItem.mImage.getId();
                            BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
                            bitmap = MediaStore.Video.Thumbnails.getThumbnail(mCr, longId,
                                    MediaStore.Images.Thumbnails.MINI_KIND, bitmapOptions);
                        } else {
                            try {
                                MediaMetadataRetriever media = new MediaMetadataRetriever();
                                media.setDataSource(workItem.mImage.getPath());
                                bitmap = media.getFrameAtTime();
                            } catch (Exception e) {
                                Log.e(TAG, "get video bitmap error.");
                                e.printStackTrace();
                                bitmap = null;
                            }
                        }
                    } else if (workItem.mImage.getType() == MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE) {
                        if (workItem.mIsThumb) {
                            long longId = workItem.mImage.getId();
                            BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
                            bitmap = MediaStore.Images.Thumbnails.getThumbnail(mCr, longId,
                                    MediaStore.Images.Thumbnails.MINI_KIND, bitmapOptions);
                        } else {
                            bitmap = makeBitmap(UNCONSTRAINED, 3 * 1024 * 1024, workItem.mImage.getUri(), mCr, null, null);
                        }
                    }
                    if (bitmap != null) {
                        addBitmapToMemoryCache(String.valueOf(workItem.mImage.getId()) + workItem.mIsThumb, bitmap);
                        if (DEBUG) Log.i(TAG, String.valueOf(workItem.mImage.getId()) + workItem.mIsThumb + " is put in cache");
                    }
                }
                msgMap.put("bitmap", bitmap);
                msg.obj = msgMap;
                handler.sendMessage(msg);
            }
        }
    }

    private void start() {
        if (mDecodeThread != null) {
            return;
        }

        mDone = false;
        Thread t = new Thread(new WorkerThread());
        t.setName("image-loader");
        mDecodeThread = t;
        t.start();
    }

    public void stop() {
        synchronized (mQueue) {
            mDone = true;
            mQueue.notifyAll();
        }
        if (mDecodeThread != null) {
            try {
                Thread t = mDecodeThread;
                MediaStore.Images.Thumbnails.cancelThumbnailRequest(mCr, -1, t.getId());
                MediaStore.Video.Thumbnails.cancelThumbnailRequest(mCr, -1, t.getId());
                t.join();
                mDecodeThread = null;
            } catch (InterruptedException ex) {
                // so now what?
            }
        }
    }

    public interface OnLoaderCompleteListener {
        void onLoadingComplete(Bitmap bitmap);
    }
}


转载请注明:CodingBlog » ImageLoader

喜欢 (0)or分享 (0)
发表我的评论
取消评论

*

表情