1. 程式人生 > >胖虎談ImageLoader框架(三)

胖虎談ImageLoader框架(三)

前言

從學校出來的這半年時間,發現很少有時間可以靜下來學習和寫博文了,為了保持著學習和分享的習慣,我準備每週抽出一部分時間為大家帶來一個優秀的Android框架原始碼閱讀後的理解系列博文。

期許:希望可以和大家一起學習好此框架,也希望大家看博文前最好是先了解下框架的基本使用場景和使用方法,有什麼問題可以留言給我,交流學習。
當然,再好的博文,也不如自己看一遍原始碼!


這周為大家帶來的是《胖虎談ImageLoader框架》系列,分析優秀的框架原始碼能讓我們更迅速地提升,大家共勉!!
原始碼包下載地址:http://download.csdn.net/detail/u011133213/9210765

希望我們尊重每個人的成果,轉載請註明出處。
轉載於:CSDN 胖虎 , http://blog.csdn.net/ljphhj


正文

繼上2篇博文《胖虎談ImageLoader框架(一)》 《胖虎談ImageLoader框架(二)》
帶來這篇《胖虎談ImageLoader框架(三)》,上篇我們提到的幾個涉及到的類,希望讀者可以自己去閱讀原始碼理解,此篇博文不希望在解釋原始碼,希望提到一些那些類中涉及到的知識(執行緒池,下載器實現,ReentrantLock和Synchronized,Collections.synchronizedMap等)

(ps:讀此博文前,希望網友已經閱讀並理解了

《胖虎談ImageLoader框架(一)》 《胖虎談ImageLoader框架(二)》再閱讀此博文。)

1、Java執行緒池
1.1 什麼情況下我們才使用執行緒池?
答:需要做大量的請求,並且每個請求的時間很短。

1.2 使用執行緒池的好處?
答:減少頻繁建立和銷燬執行緒物件所需時間和記憶體消耗,提升了效率。

1.3 開始學習Java中的執行緒池
(1) JAVA中執行緒池的類和繼承實現關係UML圖
這裡寫圖片描述

(2) ThreadPoolExecutor / ScheduledThreadPoolExecutor

2.1 ThreadPoolExecutor

public
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)

corePoolSize: 核心執行緒數量
maximumPoolSize: 執行緒池中最大可容納數量
keepAliveTime: 執行緒在佇列中等待被執行的超時時間
unit: 執行緒池允許超時時間的單位
workQueue: 執行緒池採用的緩衝佇列
threadFactory:建立執行緒的工廠類
handler: 拒絕執行緒任務的處理類

執行緒池的執行機制:
1. 呼叫execute(Runnable)方法,新增一個執行緒任務到執行緒池中。
2. ▲執行緒池判斷當前池中存線上程數 < corePoolSize, 立即建立一個新的執行緒,並執行該Runnable物件的run()方法。▲如果>=corePoolSize, 會將該任務新增到workQueue佇列中,等待執行。▲如果workQueue佇列中已經滿了,並且執行緒池中的執行緒數還 < maximumPoolSize, 那麼會建立一個新的執行緒來放入執行緒池中來處理此任務。▲如果如果workQueue佇列中已經滿了,並且執行緒池中的執行緒數>=maximumPoolSize , 那麼會呼叫handler的策略方法拒絕掉該任務。

學習這個類,主要還是學習一下這個類的引數的意義和執行的機制,當然咱們也要順帶把這個類裡面涉及到的周邊類也學習一下。這裡面提到了 ThreadFactory(可以自定義一個,用來修改執行緒的name, group, priority, daemon status等) , 佇列(SynchronousQueue、LinkedBlockingQueue、ArrayBlockingQueue),拒絕任務的策略種類(1. AbortPolicy[直接拋RejectedExecutionException異常]、2.CallerRunsPolicy[再給一次嘗試的機會]、3.DiscardPolicy[無作為]、4.DiscardOldestPolicy[捨棄隊頭任務,再嘗試添加當前任務])

2.2 ScheduledThreadPoolExecutor
這個類繼承於ThreadPoolExecutor,所以父類有的,它也一樣,它的使用方式是 :
schedule(Runnable command, long delay, TimeUnit unit) , 可以設定一個延時執行的任務。

2、下載器實現
1. ImageLoader中實現了三種的下載器:BaseImageDownloader、NetworkDeniedImageDownloader、SlowNetworkImageDownloader(都實現了介面ImageDownloader)
2. ImageDownloader介面中,實現了Scheme類,用於判斷傳入的一個Uri是什麼Schme和其他操作。
Scheme分為:HTTP(“http”), HTTPS(“https”), FILE(“file”), CONTENT(“content”), ASSETS(“assets”), DRAWABLE(“drawable”), UNKNOWN(“”)。
3.▲NetworkDeniedImageDownloader下載器,因為網路不能接觸,所以就不處理”http”和”https” 這兩種需要網路的Scheme。

private static class NetworkDeniedImageDownloader implements ImageDownloader {

        private final ImageDownloader wrappedDownloader;

        public NetworkDeniedImageDownloader(ImageDownloader wrappedDownloader) {
            this.wrappedDownloader = wrappedDownloader;
        }

        @Override
        public InputStream getStream(String imageUri, Object extra) throws IOException {
            switch (Scheme.ofUri(imageUri)) {
                case HTTP:
                case HTTPS:
                    throw new IllegalStateException();
                default:
                    return wrappedDownloader.getStream(imageUri, extra);
            }
        }
    }

▲SlowNetworkImageDownloader下載器, 考慮到網路情況是屬於比較差的,所以需要用到的InputStream來處理Scheme為”http”和”https”的Uri。

private static class SlowNetworkImageDownloader implements ImageDownloader {

        private final ImageDownloader wrappedDownloader;

        public SlowNetworkImageDownloader(ImageDownloader wrappedDownloader) {
            this.wrappedDownloader = wrappedDownloader;
        }

        @Override
        public InputStream getStream(String imageUri, Object extra) throws IOException {
            InputStream imageStream = wrappedDownloader.getStream(imageUri, extra);
            switch (Scheme.ofUri(imageUri)) {
                case HTTP:
                case HTTPS:
                    return new FlushedInputStream(imageStream);
                default:
                    return imageStream;
            }
        }
    }

BaseImageDownloader:此下載器最為重要,這個類裡面寫了ImageLoader處理各種資料來源時是如何下載圖片資源的。這邊分別對所有的Scheme將對應的處理函式貼出來,有興趣的網友再自行去看哈。這個其實可以抽出來作為自己的一個圖片下載類,因為所有不同來源的圖片下載方式都有。

    @Override
    public InputStream getStream(String imageUri, Object extra) throws IOException {
        switch (Scheme.ofUri(imageUri)) {
            case HTTP:
            case HTTPS:
                return getStreamFromNetwork(imageUri, extra);
            case FILE:
                return getStreamFromFile(imageUri, extra);
            case CONTENT:
                return getStreamFromContent(imageUri, extra);
            case ASSETS:
                return getStreamFromAssets(imageUri, extra);
            case DRAWABLE:
                return getStreamFromDrawable(imageUri, extra);
            case UNKNOWN:
            default:
                return getStreamFromOtherSource(imageUri, extra);
        }
    }

網路圖片,Http和Https的下載處理:

protected HttpURLConnection createConnection(String url, Object extra) throws IOException {
        String encodedUrl = Uri.encode(url, ALLOWED_URI_CHARS);
        HttpURLConnection conn = (HttpURLConnection) new URL(encodedUrl).openConnection();
        conn.setConnectTimeout(connectTimeout);
        conn.setReadTimeout(readTimeout);
        return conn;
    }

protected InputStream getStreamFromNetwork(String imageUri, Object extra) throws IOException {
        HttpURLConnection conn = createConnection(imageUri, extra);

        int redirectCount = 0;
        while (conn.getResponseCode() / 100 == 3 && redirectCount < MAX_REDIRECT_COUNT) {
            conn = createConnection(conn.getHeaderField("Location"), extra);
            redirectCount++;
        }

        InputStream imageStream;
        try {
            imageStream = conn.getInputStream();
        } catch (IOException e) {
            // Read all data to allow reuse connection (http://bit.ly/1ad35PY)
            IoUtils.readAndCloseStream(conn.getErrorStream());
            throw e;
        }
        if (!shouldBeProcessed(conn)) {
            IoUtils.closeSilently(imageStream);
            throw new IOException("Image request failed with response code " + conn.getResponseCode());
        }

        return new ContentLengthInputStream(new BufferedInputStream(imageStream, BUFFER_SIZE), conn.getContentLength());
    }

來自本地檔案,Scheme為”file”的下載處理:

protected InputStream getStreamFromFile(String imageUri, Object extra) throws IOException {
        String filePath = Scheme.FILE.crop(imageUri);
        if (isVideoFileUri(imageUri)) {
            return getVideoThumbnailStream(filePath);
        } else {
            BufferedInputStream imageStream = new BufferedInputStream(new FileInputStream(filePath), BUFFER_SIZE);
            return new ContentLengthInputStream(imageStream, (int) new File(filePath).length());
        }
    }

    @TargetApi(Build.VERSION_CODES.FROYO)
    private InputStream getVideoThumbnailStream(String filePath) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {
            Bitmap bitmap = ThumbnailUtils.createVideoThumbnail(filePath, MediaStore.Images.Thumbnails.FULL_SCREEN_KIND);
            if (bitmap != null) {
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                bitmap.compress(CompressFormat.PNG, 0, bos);
                return new ByteArrayInputStream(bos.toByteArray());
            }
        }
        return null;
    }

來自ContentPorivder傳入的Scheme為”content”的下載處理:

protected InputStream getStreamFromContent(String imageUri, Object extra) throws FileNotFoundException {
        ContentResolver res = context.getContentResolver();

        Uri uri = Uri.parse(imageUri);
        if (isVideoContentUri(uri)) { // video thumbnail
            Long origId = Long.valueOf(uri.getLastPathSegment());
            Bitmap bitmap = MediaStore.Video.Thumbnails
                    .getThumbnail(res, origId, MediaStore.Images.Thumbnails.MINI_KIND, null);
            if (bitmap != null) {
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                bitmap.compress(CompressFormat.PNG, 0, bos);
                return new ByteArrayInputStream(bos.toByteArray());
            }
        } else if (imageUri.startsWith(CONTENT_CONTACTS_URI_PREFIX)) { // contacts photo
            return ContactsContract.Contacts.openContactPhotoInputStream(res, uri);
        }

        return res.openInputStream(uri);
    }

來自Assets和Drawable中傳入的Scheme為”assets”和”drawable”的下載處理:

    protected InputStream getStreamFromAssets(String imageUri, Object extra) throws IOException {
        String filePath = Scheme.ASSETS.crop(imageUri);
        return context.getAssets().open(filePath);
    }
    protected InputStream getStreamFromDrawable(String imageUri, Object extra) {
        String drawableIdString = Scheme.DRAWABLE.crop(imageUri);
        int drawableId = Integer.parseInt(drawableIdString);
        return context.getResources().openRawResource(drawableId);
    }

3、ReentrantLock和Synchronized
這兩個的比較,我查閱了一下網上的文章,本想自己做一些理解和總結,看了一篇文章覺得剖析得非常好,偷懶直接貼上鍊接地址,如果您對此知識還不是完全熟悉,那麼推薦您也可以看看。(http://www.ibm.com/developerworks/cn/java/j-jtp10264/index.html

4、Collections.synchronizedMap類(執行緒安全)
此Map用於可能多執行緒共享使用的情況。原始碼如下,無非就是將自身作為一個互斥量,進行Map的各種操作時用synchronized關鍵字將其鎖住,保證執行緒安全。

public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
        return new SynchronizedMap<>(m);
    }

    /**
     * @serial include
     */
    private static class SynchronizedMap<K,V>
        implements Map<K,V>, Serializable {
        private static final long serialVersionUID = 1978198479659022715L;

        private final Map<K,V> m;     // Backing Map
        final Object      mutex;        // Object on which to synchronize

        SynchronizedMap(Map<K,V> m) {
            this.m = Objects.requireNonNull(m);
            mutex = this;
        }

        SynchronizedMap(Map<K,V> m, Object mutex) {
            this.m = m;
            this.mutex = mutex;
        }

        public int size() {
            synchronized (mutex) {return m.size();}
        }
        public boolean isEmpty() {
            synchronized (mutex) {return m.isEmpty();}
        }
        public boolean containsKey(Object key) {
            synchronized (mutex) {return m.containsKey(key);}
        }
        public boolean containsValue(Object value) {
            synchronized (mutex) {return m.containsValue(value);}
        }
        public V get(Object key) {
            synchronized (mutex) {return m.get(key);}
        }

        public V put(K key, V value) {
            synchronized (mutex) {return m.put(key, value);}
        }
        public V remove(Object key) {
            synchronized (mutex) {return m.remove(key);}
        }
        public void putAll(Map<? extends K, ? extends V> map) {
            synchronized (mutex) {m.putAll(map);}
        }
        public void clear() {
            synchronized (mutex) {m.clear();}
        }

        private transient Set<K> keySet;
        private transient Set<Map.Entry<K,V>> entrySet;
        private transient Collection<V> values;

        public Set<K> keySet() {
            synchronized (mutex) {
                if (keySet==null)
                    keySet = new SynchronizedSet<>(m.keySet(), mutex);
                return keySet;
            }
        }

        public Set<Map.Entry<K,V>> entrySet() {
            synchronized (mutex) {
                if (entrySet==null)
                    entrySet = new SynchronizedSet<>(m.entrySet(), mutex);
                return entrySet;
            }
        }

        public Collection<V> values() {
            synchronized (mutex) {
                if (values==null)
                    values = new SynchronizedCollection<>(m.values(), mutex);
                return values;
            }
        }

        public boolean equals(Object o) {
            if (this == o)
                return true;
            synchronized (mutex) {return m.equals(o);}
        }
        public int hashCode() {
            synchronized (mutex) {return m.hashCode();}
        }
        public String toString() {
            synchronized (mutex) {return m.toString();}
        }

        // Override default methods in Map
        @Override
        public V getOrDefault(Object k, V defaultValue) {
            synchronized (mutex) {return m.getOrDefault(k, defaultValue);}
        }
        @Override
        public void forEach(BiConsumer<? super K, ? super V> action) {
            synchronized (mutex) {m.forEach(action);}
        }
        @Override
        public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
            synchronized (mutex) {m.replaceAll(function);}
        }
        @Override
        public V putIfAbsent(K key, V value) {
            synchronized (mutex) {return m.putIfAbsent(key, value);}
        }
        @Override
        public boolean remove(Object key, Object value) {
            synchronized (mutex) {return m.remove(key, value);}
        }
        @Override
        public boolean replace(K key, V oldValue, V newValue) {
            synchronized (mutex) {return m.replace(key, oldValue, newValue);}
        }
        @Override
        public V replace(K key, V value) {
            synchronized (mutex) {return m.replace(key, value);}
        }
        @Override
        public V computeIfAbsent(K key,
                Function<? super K, ? extends V> mappingFunction) {
            synchronized (mutex) {return m.computeIfAbsent(key, mappingFunction);}
        }
        @Override
        public V computeIfPresent(K key,
                BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
            synchronized (mutex) {return m.computeIfPresent(key, remappingFunction);}
        }
        @Override
        public V compute(K key,
                BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
            synchronized (mutex) {return m.compute(key, remappingFunction);}
        }
        @Override
        public V merge(K key, V value,
                BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
            synchronized (mutex) {return m.merge(key, value, remappingFunction);}
        }

        private void writeObject(ObjectOutputStream s) throws IOException {
            synchronized (mutex) {s.defaultWriteObject();}
        }
    }

總結

其實這篇博文旨在將一些框架中引用到的知識點,可是平時可能被我忽視的或者少用到的,進行記錄,方便我自身充電,與其說是在寫博文交流,不如說是自身的補缺補漏,大牛們可以無視O(∩_∩)O!