Universal-Image-Loader源碼解析之線程管理

UIL中包括一下封裝的任務:

  • DisplayImageTask
  • LoadAndDisplayImageTask
  • ProcessAndDisplayImageTask
    它們都在ImageLoadEngine中得到統一調度執行。

線程需要關注的幾個方面:

線程池類型

創建線程池的構造方法:

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }
幾種常見線程池.png
  • 對于需要保證所有提交的任務都要被執行的情況,或者任務屬于耗時長但是數量少的,使用FixedThreadPool
  • 如果限定只能使用一個線程進行任務處理,使用SingleThreadExecutor
  • 如果希望提交的任務盡快分配線程執行,或者說任務量大且每個任務耗時比較少的,使用CachedThreadPool
  • 如果業務上允許任務執行失敗,或者任務執行過程可能出現執行時間過長進而影響其他業務的應用場景,可以通過使用限定線程數量的線程池以及限定長度的隊列進行容錯處理。
UIL中的三個線程池.png
UIL中線程池的配置.png

UIL線程執行

ImageLoader

            ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
                    options, listener, progressListener, engine.getLockForUri(uri));
            LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo,
                    defineHandler(options));
            if (options.isSyncLoading()) {
                displayTask.run();
            } else {
                engine.submit(displayTask);
            }

ImageLoadEngine

    void submit(final LoadAndDisplayImageTask task) {
        taskDistributor.execute(new Runnable() {
            @Override
            public void run() {
                File image = configuration.diskCache.get(task.getLoadingUri());
                boolean isImageCachedOnDisk = image != null && image.exists();
                initExecutorsIfNeed();
                if (isImageCachedOnDisk) {
                    taskExecutorForCachedImages.execute(task);
                } else {
                    taskExecutor.execute(task);
                }
            }
        });
    }
  • taskDistributor:主要任務就是根據磁盤是否有緩存,再來分配任務。由于在每創建一個新的線程的時候都需要讀取一下磁盤,屬于IO操作。需要圖片緩存的應用一般在需要加載圖片的時候,同時創建很多(>5)線程,這些線程一般來得猛去的也快,存活時間不必太長。
  • taskExecutorForCachedImages:主要任務就是將磁盤上緩存的圖片解碼出來,并作后續處理,再顯示
  • taskExecutor:主要任務就是負責一個圖片完整的從網絡加載到磁盤緩存、內存緩存、預處理、后續處理、展示等。

它有如下幾個主要方法:

    private Executor createTaskExecutor() {
        return DefaultConfigurationFactory
                .createExecutor(configuration.threadPoolSize, configuration.threadPriority,
                configuration.tasksProcessingType);
    }
    void pause() {
        paused.set(true);
    }

    void resume() {
        paused.set(false);
        synchronized (pauseLock) {
            pauseLock.notifyAll();
        }
    }

    void stop() {
        if (!configuration.customExecutor) {
            ((ExecutorService) taskExecutor).shutdownNow();
        }
        if (!configuration.customExecutorForCachedImages) {
            ((ExecutorService) taskExecutorForCachedImages).shutdownNow();
        }

        cacheKeysForImageAwares.clear();
        uriLocks.clear();
    }

其中shutdownNow()方法會終止線程池中的執行

而這個pause()resume()方法,在多圖列表滑動時,可以通過調用此方法,暫停線程池中任務的執行以減輕cpu的負擔,達到流暢滑動的目的。

LoadAndDisplayImageTask中,run()方法執行之前,都會檢查一下 waitIfPaused()方法看此任務是否暫停,如果是,則先把線程鎖鎖上,然后進入等待。

    private boolean waitIfPaused() {
        AtomicBoolean pause = engine.getPause();
        if (pause.get()) {
            synchronized (engine.getPauseLock()) {
                if (pause.get()) {
                    L.d(LOG_WAITING_FOR_RESUME, memoryCacheKey);
                    try {
                        engine.getPauseLock().wait();
                    } catch (InterruptedException e) {
                        L.e(LOG_TASK_INTERRUPTED, memoryCacheKey);
                        return true;
                    }
                    L.d(LOG_RESUME_AFTER_PAUSE, memoryCacheKey);
                }
            }
        }
        return isTaskNotActual();
    }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容