手写Glide框架

    文章主要是仿照Glide方式来实现了图片加载,当然功能没有Glide那么全面,主要用来练习总结

    写一个Glide框架,首先需要我们来分析一下实现思路,主要分了六大步骤,接下来我们会一步一步来实现操作

  1.  资源封装 
  2. 活动缓存
  3. 内存缓存
  4. 磁盘缓存
  5. 生命周期
  6. 图片加载

 

  资源封装

          资源的封装主要是为了后面的缓存策略进行服务,方便查找放置,所以有了key,value,进行查找方便

    Key:即为键,因为图片资源链接中可能会存在特殊符号,所以我们需要特殊处理下来保证没有特殊符号,所以我这里我进行了加密操作

    Value:存放的图片,同时需要做一些记录操作,因为后面的缓存策略,我们需要做一些标记来进行应对

public class Key {
    private String key;

    /**
     * 需要对key进行加密操作,因为key是url,所以会存在特殊符号的可能
     * @param key
     */
    public Key(String key) {
        this.key = Utils.AES256Encode(key);
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }
}



public class Value {

    private static final String TAG = "Value";

    private Bitmap mBitmap;

    private ValueCallback callback;

    /**
     * 使用计数
     */
    private int count;

    private String key;

    private static Value value;

    public static Value getInstance() {
        if (value == null) {
            synchronized (Value.class) {
                if (value == null) {
                    value = new Value();
                }
            }
        }
        return value;
    }

    private Value() {

    }

    /**
     * 使用一次就进行 +1
     */
    public void useAction() {
        if (mBitmap == null) {
            Log.i(TAG, "图片资源为null");
            return;
        }
        if (mBitmap.isRecycled()) {
            Log.i(TAG, "图片资源已经被回收");
            return;
        }
        ++count;
    }

    /**
     * 使用一次后 -1
     */
    public void nonUseAction() {
        count--;
        if (count <= 0 && callback != null) {
            //不再使用了,需要告诉外界不再使用
            callback.valueNonUseListener(key, this);
        }
    }

    /**
     * 释放资源
     */
    public void recycleBitmap() {
        if (count > 0) {
            Log.i(TAG, "图片资源仍然在使用,不能将其释放");
            return;
        }
        //表示被回收了,不用再回收
        if (mBitmap.isRecycled()) {
            Log.i(TAG, "图片资源已经被释放");
            return;
        }
        mBitmap.recycle();
        value = null;
        System.gc();
    }

    public Bitmap getmBitmap() {
        return mBitmap;
    }

    public void setmBitmap(Bitmap mBitmap) {
        this.mBitmap = mBitmap;
    }

    public ValueCallback getCallback() {
        return callback;
    }

    public void setCallback(ValueCallback callback) {
        this.callback = callback;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }
}


public interface ValueCallback {

    void valueNonUseListener(String key, Value value);
}

资源的封装比较简单,只是做一些基本辅助工作

 

接下来主要是缓存策略的操作

  活动缓存-内存缓存-磁盘缓存,复用池这里不做过多说明,Glide里面还有复用池的应用,以此能解决内存抖动的问题

其中活动缓存和内存缓存本质上来说都是在内存中进行操作,在界面上时,加载的图像会进入活动缓存,加载图片时我们会先从活动缓存中查找,如果没有,则从内存缓存中查找,这里有找到和没找到的情况,找的话我们将其从内存缓存中移除,放入活动缓存中,没找到的话我们继续从磁盘缓存中查找,找到的话放入活动缓存中,如果还没找到则需要从网络或者sd卡中寻找

       当活动界面销毁时,活动缓存中的数据会进入内存缓存中,这里内存缓存和磁盘缓存我们都会进行LRU算法的操作

     缓存的处理策略大概是这样,接下来让我们来看代码,首先是活动缓存

 

/**
 * 活动缓存-正在被使用的资源
 * 因为是GC回收机制,所以需要处理成弱引用
 * 这里只有添加移除和获取三种操作
 */
public class ActiveCache {

    private Map<String, WeakReference<Value>> mMap = new HashMap<>();
    /**
     * 为了监听弱引用是否被回收,CustomWeakReference中需要ReferenceQueue,所以我们需要获取这个queue
     */
    private ReferenceQueue<Value> mQueue;
    private boolean isCloseThread;
    private Thread mThread;
    private boolean isShutDownRemove;
    private ValueCallback valueCallback;

    public ActiveCache(ValueCallback valueCallback) {
        this.valueCallback = valueCallback;
    }

    public void put(String key, Value value) {
        if (TextUtils.isEmpty(key) || value == null) {
            throw new IllegalStateException("传递的数据为空");
        }
        //绑定value的监听
        value.setCallback(valueCallback);
        mMap.put(key, new CustomWeakReference(value, getQueue(), key));
    }

    /**
     * 获取资源
     * @param key
     * @return
     */
    public Value get(String key) {
        WeakReference<Value> valueWeakReference = mMap.get(key);
        if (valueWeakReference != null) {
            return valueWeakReference.get();
        }
        return null;
    }

    /**
     * 手动移除
     * @param key
     * @return
     */
    public Value remove(String key) {
        isShutDownRemove = true;
        WeakReference<Value> valueWeakReference = mMap.remove(key);
        isShutDownRemove = false;
        if (valueWeakReference != null) {
            return valueWeakReference.get();
        }
        return null;
    }

    /**
     * 释放关闭线程
     */
    public void closeThread() {
        isCloseThread = true;
        if (mThread != null) {
            mThread.interrupt();
            try {
                //线程稳定安全的停止
                mThread.join(TimeUnit.SECONDS.toMillis(5));
                //如果还是活跃状态
                if (mThread.isAlive()) {
                    throw new IllegalStateException("线程关闭失败了");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 为了监听弱引用被回收,被动移除的
     */
    private ReferenceQueue<Value> getQueue() {
        if (mQueue == null) {
            mQueue = new ReferenceQueue<>();
            //监听这个弱引用,是否被回收了
            mThread = new Thread() {
                @Override
                public void run() {
                    super.run();
                    while (!isCloseThread) {
                        try {
                            //如果被回收了,就会执行到这个方法
                            // mQueue.remove() 是阻塞式的方法,如果没有移除不会往下执行,如果移除了,下面的才会执行
                            Reference<? extends Value> remove = mQueue.remove();
                            CustomWeakReference weakReference = (CustomWeakReference) remove;
                            //移除容器中存储的,需要区分是主动移除还是被动移除的
                            if (!mMap.isEmpty() && !isShutDownRemove) {
                                mMap.remove(weakReference.key);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }

                }
            };
            mThread.start();
        }
        return mQueue;
    }

    /**
     * 需要监听弱引用
     */
    public static class CustomWeakReference extends WeakReference<Value> {

        private String key;

        /**
         * 该方法能知道是否被回收掉
         */
        public CustomWeakReference(Value referent, ReferenceQueue<? super Value> q, String key) {
            super(referent, q);
            this.key = key;
        }
    }

}

 接下来是内存缓存

public class MemoryCache extends LruCache<String, Value> {

    private boolean isShutDownRemove;

    /**
     * @param maxSize
     *         for caches that do not override {@link #sizeOf}, this is
     *         the maximum number of entries in the cache. For all other caches,
     *         this is the maximum sum of the sizes of the entries in this cache.
     */
    public MemoryCache(int maxSize) {
        super(maxSize);
    }

    private MemoryCacheCallback memoryCacheCallback;

    public void setMemoryCacheCallback(MemoryCacheCallback memoryCacheCallback) {
        this.memoryCacheCallback = memoryCacheCallback;
    }


    @Override
    protected int sizeOf(String key, Value value) {
        Bitmap bitmap = value.getmBitmap();
        return bitmap.getAllocationByteCount();
    }


    /**
     * 1.最少使用的元素会被移除
     * 2.重复的key
     */
    @Override
    protected void entryRemoved(boolean evicted, String key, Value oldValue, Value newValue) {
        super.entryRemoved(evicted, key, oldValue, newValue);
        //被动的移除
        if (memoryCacheCallback != null && !isShutDownRemove) {
            memoryCacheCallback.entryRemovedMemoryCache(key, oldValue);
        }
    }

    /**
     * 当进入到活动缓存中时需要手动移除
     */
    public Value shutDownRemove(String key) {
        isShutDownRemove = true;
        Value remove = remove(key);
        isShutDownRemove = false;
        return remove;
    }

}






public interface MemoryCacheCallback {
    /**
     * 移除内存缓存中的Key
     * @param key
     * @param value
     */
    void entryRemovedMemoryCache(String key, Value value);

}

磁盘缓存

public class DiskLruCacheImpl {
    private static final String TAG = "DiskLruCacheImpl";
    private static final String DISK_LRU_CACHE_DIR = "disk_lru_cache_dir";
    private DiskLruCache diskLruCache;
    /**
     * 做标记,版本号变更的话之前的缓存失效
     */
    private static final int APP_VERSION = 999;
    private static final int VALUE_COUNT = 1;
    private static final long MAX_SIZE = 1024 * 1024 * 10;

    public DiskLruCacheImpl() {
        File file = new File(Environment.getExternalStorageDirectory() + File.separator + DISK_LRU_CACHE_DIR);
        try {
            diskLruCache = DiskLruCache.open(file, APP_VERSION, VALUE_COUNT, MAX_SIZE);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 添加
     */
    public void put(String key, Value value) {
        DiskLruCache.Editor edit = null;
        OutputStream outputStream = null;
        try {
            edit = diskLruCache.edit(key);
            //index 不能大于VALUE_COUNT
            outputStream = edit.newOutputStream(0);
            Bitmap bitmap = value.getmBitmap();
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
            outputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
            try {
                edit.abort();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        } finally {
            try {
                edit.commit();
                diskLruCache.flush();
                if (outputStream != null) {
                    outputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 获取
     */
    public Value get(String key) {
        if (TextUtils.isEmpty(key)) {
            throw new IllegalStateException(TAG + " : key不能为空");
        }
        InputStream inputStream = null;
        Value value = null;
        try {
            DiskLruCache.Snapshot snapshot = diskLruCache.get(key);
            if (snapshot != null) {
                value = Value.getInstance();
                inputStream = snapshot.getInputStream(0);
                Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                value.setmBitmap(bitmap);
                value.setKey(key);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return value;
    }
}

然后是网络请求或者Sd卡读取

public class LoadDataManager implements ILoadData, Runnable {

    private String path;
    private ResponseListener responseListener;
    private Context context;

    @Override
    public Value loadResource(Context context, String path, ResponseListener listener) {
        this.path = path;
        this.responseListener = listener;
        this.context = context;

        //加载网络图片/SD卡本地图片
        Uri uri = Uri.parse(path);
        if ("HTTP".equalsIgnoreCase(uri.getScheme()) || "HTTPS".equalsIgnoreCase(uri.getScheme())) {
            new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10)).execute(this);
        }
        //Sd本地图片 找到图片返回

        return null;
    }


    @Override
    public void run() {
        InputStream inputStream = null;
        HttpURLConnection httpURLConnection = null;
        try {
            URL url = new URL(path);
            httpURLConnection = (HttpURLConnection) url.openConnection();
            httpURLConnection.setConnectTimeout(5000);
            final int responseCode = httpURLConnection.getResponseCode();
            if (responseCode == 200) {
                inputStream = httpURLConnection.getInputStream();
                final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                //需要切换回主线程
                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        Value value = Value.getInstance();
                        value.setmBitmap(bitmap);
                        //回调成功
                        responseListener.responseSuccess(value);
                    }
                });

            } else {
                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        responseListener.responseFail(new Exception("请求失败,请求码" + responseCode));
                    }
                });
            }
        } catch (final Exception e) {
            e.printStackTrace();
            new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    responseListener.responseFail(e);
                }
            });

        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (httpURLConnection != null) {
                httpURLConnection.disconnect();
            }
        }
    }
}

生命周期

生命周期的绑定我们主要是通过添加fragment,然后监听fragment的方式来达到监听生命周期的目的

public class RequestManager {
    private static final String TAG = "RequestManager";
    public static final String FRAGMENT_ACTIVITY_NAME = "fragment_activity_name";
    public static final String ACTIVITY_NAME = "activity_name";
    private Context requestManagerContext;
    private static final int SEND_FRAGMENT_ACTIVITY_MSG = 999;
    private static final int SEND_ACTIVITY_MSG = 888;
    private RequestTargetEngine requestTargetEngine;
    private FragmentActivityFragmentManager fragmentActivityByTag;
    private ActivityFragmentManager activityByTag;

    /**
     * 可以管理生命周期 --fragment -- FragmentActivityFragmentManager
     * @param fragmentActivity
     */
    public RequestManager(FragmentActivity fragmentActivity) {
        requestManagerContext = fragmentActivity;
        FragmentManager supportFragmentManager = fragmentActivity.getSupportFragmentManager();
        fragmentActivityByTag = (FragmentActivityFragmentManager) supportFragmentManager.findFragmentByTag(FRAGMENT_ACTIVITY_NAME);
        if (fragmentActivityByTag == null) {
            //关联生命周期
            fragmentActivityByTag = new FragmentActivityFragmentManager(requestTargetEngine);
            supportFragmentManager.beginTransaction()
                    .add(fragmentActivityByTag, FRAGMENT_ACTIVITY_NAME)
                    .commitAllowingStateLoss();
        }
        //发送一个handler,因为add,这种操作会存在等待情况,需要确保能拿到
        mHandler.sendEmptyMessage(SEND_FRAGMENT_ACTIVITY_MSG);
    }

    {
        //构造代码块中初始化,避免每个都需要初始化
        requestTargetEngine = new RequestTargetEngine();
    }

    /**
     * 可以管理生命周期 --activity -- ActivityFragmentManager
     * @param activity
     */
    public RequestManager(Activity activity) {
        requestManagerContext = activity;

        android.app.FragmentManager fragmentManager = activity.getFragmentManager();
        activityByTag = (ActivityFragmentManager) fragmentManager.findFragmentByTag(ACTIVITY_NAME);
        if (activityByTag == null) {
            //关联生命周期
            activityByTag = new ActivityFragmentManager(requestTargetEngine);
            fragmentManager.beginTransaction().add(activityByTag, ACTIVITY_NAME).commitAllowingStateLoss();
        }

        //发送一个handler,因为add,这种操作会存在等待情况,需要确保能拿到
        mHandler.sendEmptyMessage(SEND_ACTIVITY_MSG);
    }

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            Log.i(TAG, "收到消息了" + msg.what);
            if (msg.what == SEND_ACTIVITY_MSG) {
                activityByTag.setRequestManager(RequestManager.this);
                mHandler.removeMessages(SEND_ACTIVITY_MSG);
            } else {
                fragmentActivityByTag.setRequestManager(RequestManager.this);
                mHandler.removeMessages(SEND_FRAGMENT_ACTIVITY_MSG);
            }

        }
    };

    /**
     * 无法管理生命周期--application
     * @param context
     */
    public RequestManager(Context context) {
        requestManagerContext = context;
    }

    /**
     * 拿到图片路径
     * @param path
     * @return
     */
    public RequestTargetEngine load(String path) {
        requestTargetEngine.loadValueInitAction(requestManagerContext, path);
        return requestTargetEngine;
    }
}

真正处理的地方

public class RequestTargetEngine implements LifecyclerCallback, ValueCallback, MemoryCacheCallback, ResponseListener {

    private static final String TAG = "RequestTargetEngine";
    private static final int MEMORY_MAX_SIZE = 1024 * 1024 * 60;

    @Override
    public void glideInitAction() {
        Log.i(TAG, "glideInitAction");
    }

    @Override
    public void glideStopAction() {
        Log.i(TAG, "glideStopAction");
    }

    @Override
    public void glideRecycleAction() {
        Log.i(TAG, "glideRecycleAction");
        if (activeCache != null) {
            //释放掉活动缓存
            activeCache.closeThread();
        }
    }


    private ActiveCache activeCache;
    private MemoryCache memoryCache;
    private DiskLruCacheImpl diskLruCache;
    private Context context;
    private String path;
    private String key;
    private ImageView imageView;

    public void into(ImageView imageView) {
        if (imageView == null) {
            throw new IllegalStateException("对象不能为空");
        }
        if (Looper.myLooper() != Looper.getMainLooper()) {
            throw new IllegalStateException("需要在主线程中操作");
        }
        this.imageView = imageView;
        //开始去加载资源 缓存-》网络 加载后放入缓存中
        Value value = cacheAction();
        if (value != null) {
            imageView.setImageBitmap(value.getmBitmap());
            //使用后-1
            value.nonUseAction();
        }
    }

    /**
     * 活动缓存-内存缓存-磁盘缓存-网络
     */
    private Value cacheAction() {
        //1判断活动缓存中是否有对象资源,有就返回,没有就继续查找
        Value value = activeCache.get(key);
        if (value != null) {
            Log.i(TAG, "在活动缓存中获取到数据");
            //使用了一次,需要+1
            value.useAction();
            return value;
        }
        //2内存缓存中查找
        value = memoryCache.get(key);
        if (value != null) {
            Log.i(TAG, "在内存缓存中获取到数据,需要将其移动到活动缓存中,并删除内存缓存中的数据");
            //移动到活动缓存中
            memoryCache.remove(key);
            activeCache.put(key, value);
            value.useAction();
            return value;
        }
        //3从磁盘缓存中查找,找到就放到活动和内存缓存中,
        value = diskLruCache.get(key);
        if (value != null) {
            activeCache.put(key, value);
            value.useAction();
            //            memoryCache.put(key, value);
            return value;
        }
        //4 加载网络/SD卡图片
        value = new LoadDataManager().loadResource(context, path, this);
        if (value != null) {
            return value;
        }

        return null;

    }

    /**
     * RequestManager传递的值
     */
    public void loadValueInitAction(Context context, String path) {
        this.context = context;
        this.path = path;
        key = new Key(path).getKey();

    }

    public RequestTargetEngine() {
        if (activeCache == null) {
            activeCache = new ActiveCache(this);
        }
        if (memoryCache == null) {
            memoryCache = new MemoryCache(MEMORY_MAX_SIZE);
            memoryCache.setMemoryCacheCallback(this);
        }
        if (diskLruCache == null) {
            diskLruCache = new DiskLruCacheImpl();
        }
    }

    /**
     * 活动缓存中数据不再使用时触发
     * @param key
     * @param value
     */
    @Override
    public void valueNonUseListener(String key, Value value) {
        //说明活动缓存中不再使用了,需要放到内存缓存中
        if (!TextUtils.isEmpty(key) && value != null) {
            memoryCache.put(key, value);
        }
    }

    /**
     * 内存缓存中被移除会触发这里
     * @param key
     * @param value
     */
    @Override
    public void entryRemovedMemoryCache(String key, Value value) {
        //
    }

    /**
     * 外部资源的成功
     * @param value
     */
    @Override
    public void responseSuccess(Value value) {
        if (value != null) {
            imageView.setImageBitmap(value.getmBitmap());
            saveCache(key, value);
        }
    }

    /**
     * 外部资源的失败
     * @param e
     */
    @Override
    public void responseFail(Exception e) {
        Log.i(TAG, "加载外部资源发生了异常" + e.getMessage());
    }

    /**
     * 加载外部资源后保存到缓存中
     * @param key
     * @param value
     */
    private void saveCache(String key, Value value) {
        value.setKey(key);
        if (diskLruCache != null) {
            //保存到磁盘缓存中
            diskLruCache.put(key, value);
        }
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章