Android custom image loaded three-level cache

Recently completed a project imitation micro letter, just retired and sit, write articles, to facilitate understanding of Android image loading mode.

Some people may ask: Why Customize, three library is not perfect it?

I said: Yes, there are many excellent packages Picasso, Glide, etc., can be completed.

But the point is:

1, by customizing understand the principles and essence is really important.

2, three libraries ranging from more than a dozen categories, as many as dozens or even hundreds of classes, methods, and more numerous, and if I am afraid at least to fully grasp a month, so a great impact on our learning efficiency, Imagine we are in the most limited of time to master the principles of good, or he kept drilling unlimited callback parameter stitching ... .. good.

3, is the reason to go to work, because my work unit rigorous review of external access, so a lot of technical point in development, many of which are own package complete, continuous improvement and explore during their preparation in order to be able to achieve a common goal She added.

Well, not long-winded gossip, we cut to the chase.

First, the principle: image loading caching mechanism, I believe we all know, but still a few nagging, for beginners who want to have a cognition.

APP is limited because the end user traffic, it is possible to reduce the maximum flow rate of consumption of the user, the user is a very important point, so it should some pictures, audio, video, and some die set of data is too large, cached, wait until the user logs on again, you can get from local, greatly saving resources.

Three-level cache:

Well, in a glance, begin now to achieve a specific code

II: Code

ImageLoader.java (image loading category)

ImageLoadListener.java (image is loaded callback interface)

/**
 * Created by jambestwick on 2018/1/10.
 * <p>
 * 三级缓存
 */

public class ImageLoader {

    private static final String TAG = "ImageLoader";

    // LRUCahce 池子
    private static LruCache<String, Bitmap> mCache;
    private static Handler mHandler;
    private static Map<ImageView, Future<?>> mTaskTags = new LinkedHashMap<ImageView, Future<?>>();
    private Context mContext;

    private static ImageLoader imageLoader;

    public ImageLoader(Context context) {
        mContext = context;
        if (mCache == null) {
            // 防止OOM,mCache存放
            int maxSize = (int) (Runtime.getRuntime().freeMemory() / 2);
            mCache = new LruCache<String, Bitmap>(maxSize) {
                @Override
                protected int sizeOf(String key, Bitmap value) {
                    return value.getRowBytes() * value.getHeight();
                }
            };
        }
        if (mHandler == null) {
            mHandler = new Handler();
        }

    }

    public static ImageLoader getImageLoader() {
        return null == imageLoader ? new ImageLoader(BaseApplication.getBaseApplication()) : imageLoader;
    }

    public LruCache<String, Bitmap> getmCache() {
        return mCache;
    }

    public void display(ImageView iv, String url, ImageLoadListener loadListener) {
        // 1.去内存中取
        Bitmap bitmap;
        bitmap = mCache.get(url);
        if (bitmap != null) {
            Log.e("1内存读取:", url);
            iv.setImageBitmap(bitmap);
            if (null != loadListener) {//单个朋友圈图片重置layoutParams
                invokeRelayoutBitmap(loadListener, bitmap, iv, url);
            }
            return;
        }

        // 2.去硬盘上取
        bitmap = loadBitmapFromLocal(url);
        if (bitmap != null) {
            Log.e("2文件读取:", url);
            iv.setImageBitmap(bitmap);
            if (null != loadListener) {//单个朋友圈图片重置layoutParams
                invokeRelayoutBitmap(loadListener, bitmap, iv, url);

            }
            return;
        }

        // 3. 去网络获取图片
        loadBitmapFromNet(iv, url, loadListener);


    }

    private void invokeRelayoutBitmap(ImageLoadListener loadListener, Bitmap bitmap, ImageView iv, String url) {
        mHandler.post(new RefreshBitmap(loadListener, bitmap, url, iv));

    }

    private void loadBitmapFromNet(ImageView iv, String url, ImageLoadListener loadListener) {
        // 判断是否有线程在为 imageView加载数据
        Future<?> futrue = mTaskTags.get(iv);
        if (futrue != null && !futrue.isCancelled() && !futrue.isDone()) {
            Log.d(TAG, "取消任务");
            // 线程正在执行
            futrue.cancel(true);
        }
        futrue = ThreadPool.getCachedInstance().submit(new ImageLoadTask(iv, url, loadListener));
        mTaskTags.put(iv, futrue);
    }

    class ImageLoadTask implements Runnable {

        private String mUrl;
        private ImageView iv;
        private ImageLoadListener loadListener;

        public ImageLoadTask(ImageView iv, String url, ImageLoadListener loadListener) {
            this.mUrl = url;
            this.iv = iv;
            this.loadListener = loadListener;
        }

        @Override
        public void run() {
            try {

                Log.e("网络异步", "图片" + mUrl);
                // 获取连接

                //HttpsURLConnection conn = HttpsFormUtil.getInstance().getHttpURLConnection(new URL(mUrl));
                HttpURLConnection conn = (HttpURLConnection) new URL(mUrl).openConnection();

                conn.setConnectTimeout(30 * 1000);// 设置连接服务器超时时间
                conn.setReadTimeout(30 * 1000);// 设置读取响应超时时间

                // 连接网络
                conn.connect();

                // 获取响应码
                int code = conn.getResponseCode();

                if (200 == code) {
                    InputStream is = conn.getInputStream();

                    // 将流转换为bitmap
                    final Bitmap bitmap = BitmapFactory.decodeStream(is);

                    // 存储到本地
                    write2Local(mUrl, bitmap);

                    // 存储到内存
                    mCache.put(mUrl, bitmap);

                    mHandler.post(new Runnable() {

                        @Override
                        public void run() {
                            if (null != loadListener) {
                                loadListener.onLoadingComplete(mUrl, iv, bitmap);
                            }
                            display(iv, mUrl, loadListener);
                        }
                    });
                }
            } catch (Exception e) {
                Log.e(TAG, "download failed:" + e.toString());
            }
        }
    }


    /**
     * 本地找图片
     *
     * @param url
     */
    private Bitmap loadBitmapFromLocal(String url) {
        // 去找文件,将文件转换为bitmap
        String name;
        try {
            name = MD5Encode.encode(url);

            File file = new File(getCacheDir(), name);
            if (file.exists()) {

                Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());

                // 存储到内存
                mCache.put(url, bitmap);
                return bitmap;
            }

        } catch (Exception e) {
            // TODO Auto-generated catch block
            Log.e(TAG, "load LocalImage Failed:" + e.toString());
        }

        return null;
    }

    private void write2Local(String url, Bitmap bitmap) {
        String name;
        FileOutputStream fos = null;
        try {
            name = MD5Encode.encode(url);
            File file = new File(getCacheDir(), name);
            fos = new FileOutputStream(file);

            // 将图像写到流中,放缩略,减下存储
            bitmap.compress(Bitmap.CompressFormat.JPEG, 50, fos);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    Log.e(TAG, "io exception:" + e.toString());
                }
            }
        }
    }

    private String getCacheDir() {

        String state = Environment.getExternalStorageState();//外置sd卡路径
        File dir;
        if (Environment.MEDIA_MOUNTED.equals(state)) {
            // 有sd卡
            dir = new File(Environment.getExternalStorageDirectory(), "/zhanglong/data/" + mContext.getPackageName()
                    + "/icon");
        } else {
            // 没有sd卡
            dir = new File(mContext.getCacheDir(), "/icon");

        }
        if (!dir.exists()) {
            dir.mkdirs();
        }

        return dir.getAbsolutePath();
    }

    static class RefreshBitmap implements Runnable {
        private ImageLoadListener loadListener;
        private Bitmap bitmap;
        private String url;
        private ImageView iv;

        public RefreshBitmap(ImageLoadListener loadListener, Bitmap bitmap, String url, ImageView iv) {
            this.loadListener = loadListener;
            this.bitmap = bitmap;
            this.url = url;
            this.iv = iv;
        }

        @Override
        public void run() {
            loadListener.onLoadingComplete(url, iv, bitmap);
        }
    }

}
The core approach is a display,

/**
 * Created by jambestwick on 2018/1/10.
 * 下载完成监听
 */

public interface ImageLoadListener {
   void onLoadingComplete(String imageUri, View view, Bitmap bitmap);
}
It's that simple


Published 19 original articles · won praise 19 · views 30000 +

Guess you like

Origin blog.csdn.net/baidu_30084597/article/details/79089162