手寫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);
        }
    }
}

 

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