安卓(Android)中如何優雅的 創建/執行 異步任務/回調(AsyncTask 、Thread、Job、CallBack、Handler) ?線程池(Executor)


        在安卓的開發過程中,我們時常需要寫一些異步任務,比如網絡請求,文件壓縮,數據庫訪問等等,這些操作一般都需要放到子線程進行執行。但是,如果每一個異步操作都 new 一個thread去執行,會造成大量的資源消耗,而且不易管理,同時容易造成 內存泄露,而 Android 本身提供的AsyncTask同樣難用,並且 不同版本的 Android API 實現也是 不同的,所以為了更好的管理我們的異步任務,必須找到更佳的解決方案!
1、我們到底需要一個什么樣的異步框架? 理想的情況是這樣的,我有一個異步任務需求,然后我只需要 Add一下,然后框架去管理執行這個異步任務,完事之后把數據回調(callback)給我,我拿數據去更新UI,其他的事一律不管。並且這個框架要足夠簡單,靈活,內存占用低,沒有任務時不消耗或者是很少消耗資源。
2網上有沒有現成的框架可以用,為什么自己寫? 有很多,我上github搜了一下,有好多還不錯的框架,比如  https://github.com/path/android-priority-jobqueue   和   https://github.com/yigit/android-priority-jobqueue 都不錯,功能很強大,但是都相對比較大,不夠簡潔,所以我決定自己寫。
3、寫的時候具體思路是怎樣的? 就如上面提到的那樣,要足夠易用,代碼足夠少,足夠靈活,顯然Thread不合適,AsyncTask 也不合適,那么只有用線程池(Executor )了。
4、是如何實現的?定下了初步方針,現在就需要實現了,廢話不多說,直接上我的項目地址: https://github.com/RockySteveJobs/TaskManager 
使用步驟是直接從github上復制過來的。

How to use?

1、first step:

copy the file "ExcuteTaskManager" and "ExcuteTask"  to your project 

2、second step:

init the library in your application or activity

ExcuteTaskManager.getInstance().init();

3、third step:

write a file extends "ExcuteTask" (eg: JsonExcuteTask)

4、fourth step:

ExcuteTaskManager.getInstance().newExcuteTask(new JsonExcuteTask());

but if you want callback , please call the method getData()

ExcuteTaskManager.getInstance().getData(new JsonExcuteTask(),
new ExcuteTaskManager.GetExcuteTaskCallback()
{
public void onDataLoaded(ExcuteTask task)
{
//data has int the task, so you can update your ui;
}
});

核心代碼(Core code)

package com.rocky.eagle.task;import android.os.Handler;import android.os.Looper;import android.os.Message;import com.rocky.eagle.utils.LogUtils;import java.lang.ref.SoftReference;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.ConcurrentLinkedQueue;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;/** * Start * <p/> * User:Rocky(email:1247106107@qq.com) * Created by Rocky on 2016/6/15  21:51 * PACKAGE_NAME com.rocky.eagle.activity * PROJECT_NAME TaskManager * TODO: * Description: * * 使用的時候務必請先調用init()方法,否則不能使用 * * 用最少的代碼做盡可能多的事情 * * Done */public class ExcuteTaskManager implements Runnable {    /**     * 線程執行完事兒后默認的回調類型     */    private static final int COMMON_EXCUTE_TASK_TYPE = 0;    /**     * 線程開關     */    public volatile boolean isRunning = false;    /**     * 是否初始化完成的開關     */    private boolean isHasInit = false;    /**     * 默認線程池的線程數量     */    private static final int DEFAULT_THREAD_NUM = 5;    /**     * 初始化時的線程數量     */    private int threadNum = DEFAULT_THREAD_NUM;    /**     * 定義一個單線程的線程池,專門用來執行耗時且不需要回調的操作     */    private static ScheduledExecutorService singlePool = null/*Executors.newSingleThreadScheduledExecutor()*/;    /**     * 定義一個大小為5的線程池(這個我們比較適合多個圖片下載時使用)     */    private static ExecutorService threadPool = null/*Executors.newFixedThreadPool(threadNum)*/;    /**     * 任務執行隊列     */    private static ConcurrentLinkedQueue<ExcuteTask> allExcuteTask = null/*new ConcurrentLinkedQueue<ExcuteTask>()*/;    /**     * 回調接口列表     */    private static ConcurrentHashMap<Integer, Object> uniqueListenerList = null/*new ConcurrentHashMap<String, Object>()*/;    public Handler getHandler() {        return handler;    }    public int getThreadNum() {        return threadNum;    }    public boolean isHasInit() {        return isHasInit;    }    public boolean isRunning() {        return isRunning;    }    /**     * @author Rocky     * @desc 得到普通的 ExcuteTask 對象,     * 對外界開放的回調接口     */    public interface GetExcuteTaskCallback {        void onDataLoaded(ExcuteTask task);    }    /**     * 直接把數據發送到主線程     */    private static Handler handler = new Handler(Looper.getMainLooper()) {        @Override        public void handleMessage(Message msg) {            long start = System.currentTimeMillis();            if (msg != null && msg.obj != null                    && msg.obj instanceof ExcuteTask) {                switch (msg.what) {                    case COMMON_EXCUTE_TASK_TYPE:                        ExcuteTaskManager.getInstance().doCommonHandler((ExcuteTask) msg.obj);                        break;                    /** 如果想要添加其他類型的回調,可以在此加入代碼*/                }            }            long end = System.currentTimeMillis();            LogUtils.i(" handleMessage 總共消耗時間為:" + (end - start));        }    };    private static ExcuteTaskManager instance = null;    private ExcuteTaskManager() {        LogUtils.i("private ExcuteTaskManager() { 當前的線程Id為:" + Thread.currentThread().getId());    }    public static ExcuteTaskManager getInstance() {        if (instance == null) {            synchronized (ExcuteTaskManager.class) {                if (instance == null) {                    instance = new ExcuteTaskManager();                }            }        }        return instance;    }    /**     * 初始化操作,這個主要是初始化需要執行異步     * 回調任務的線程池,默認開啟5個線程     */    public void init() {        init(threadNum);    }    /**     * 初始化操作,這個主要是初始化需要執行異步     * 回調任務的線程池,可以傳入線程的個數     */    public synchronized void init(int initNum) {        if (!isHasInit) {            /**             * 初始化之后就相當於開始了線程次的運行             * 只不過如果沒有任務處於等待狀態             */            isRunning = true;            if (initNum > 0) {                threadNum = initNum;            }            threadPool = Executors.newFixedThreadPool(threadNum);            singlePool = Executors.newSingleThreadScheduledExecutor();            allExcuteTask = new ConcurrentLinkedQueue<>();            uniqueListenerList = new ConcurrentHashMap<>();            /**             * 初始化需要用到的線程             */            for (int i = 0; i < threadNum; i++) {                threadPool.execute(this);            }            isHasInit = true;        } else {            LogUtils.d("ExcuteTaskManager 已經初始化完成,不需要重復初始化");        }    }    /**     * 當應用被銷毀時,執行清理操作     */    public void doDestory() {        /**         * 關閉線程開關         */        isRunning = false;        isHasInit = false;        if (allExcuteTask != null) {            allExcuteTask.clear();            allExcuteTask = null;        }        if (uniqueListenerList != null) {            uniqueListenerList.clear();            uniqueListenerList = null;        }        if (threadPool != null) {            threadPool.shutdown();            threadPool = null;        }        if (singlePool != null) {            singlePool.shutdown();            singlePool = null;        }    }    /**     * 向任務隊列中添加任務對象,添加成功后,     * 任務會自動執行,執行完事兒后,不進行任何回調操作     *     * @param task 可執行的任務對象     */    public void newExcuteTask(ExcuteTask task) {        if (task != null) {            allExcuteTask.offer(task);            LogUtils.i("ExcuteTaskManager 添加任務成功之后" + "allExcuteTask.size()=" + allExcuteTask.size());            long timeOne = System.currentTimeMillis();            synchronized (allExcuteTask) {                allExcuteTask.notifyAll();                LogUtils.i("ExcuteTaskManager =====>處於喚醒狀態");            }            long timeTwo = System.currentTimeMillis();            LogUtils.i("ExcuteTaskManager喚醒線程所消耗的時間為:" + (timeTwo - timeOne));        } else {            LogUtils.w("ExcuteTaskManager====您添加的ExcuteTask為空,請重新添加");        }    }    /**     * 這個方法主要是獲取普通的回調數據,     * 獲取成功后會把加入的 ExcuteTask 對象回調到用戶界面     *     * @param task     加入的任務Task     * @param callback 任務的回調接口GetDataCallback     */    public void getData(ExcuteTask task, GetExcuteTaskCallback callback) {        /**         *  把CallBack 接口加入列表中,用完之后移除         */        try {            if (task != null && callback != null) {                LogUtils.i("callback的hashcode為:" + callback.hashCode() + "task的hashcode為:" + task.hashCode());                if (task.getUniqueID() == 0) {                    task.setUniqueID(task.hashCode());                }                uniqueListenerList.put(task.getUniqueID(), callback);                /**                 * 開始加入任務,執行任務                 */                newExcuteTask(task);            } else {                LogUtils.w("Task 或者是 GetDataCallback 為空了,請檢查你添加的參數!");            }        } catch (Exception e) {            // TODO: handle exception            /**             * 其實,這個地方的數據應該寫到一個文件中             */            LogUtils.e("ExcuteTaskManager========getData=====" + e.toString() + " thread id 為:" + Thread.currentThread().getId());            e.printStackTrace();        }    }    /**     * 從任務隊列中移除任務對象,使其不再執行(如果任務已經執行,則此方法無效)     *     * @param task 添加的任務對象     */    public void removeExcuteTask(ExcuteTask task) {        if (task != null) {            uniqueListenerList.remove(task.getUniqueID());            allExcuteTask.remove(task);        } else {            LogUtils.w("ExcuteTaskManager====您所要移除的任務為null,移除失敗");        }    }    /**     * 清除所有的任務     */    public void removeAllExcuteTask() {        allExcuteTask.clear();        uniqueListenerList.clear();    }    /**=================================任務執行、回調、分發start============================================*/    /**     * 所有的異步任務都在此執行     */    @Override    public void run() {        // TODO Auto-generated method stub        while (isRunning) {            LogUtils.i("ExcuteTaskManager====准備開始執行任務 總任務個數為  allExcuteTask.size()=" + allExcuteTask.size());            /**             * 從allExcuteTask取任務             */            ExcuteTask lastExcuteTask = allExcuteTask.poll();            LogUtils.i("ExcuteTaskManager====從allExcuteTask取出了一個任務  allExcuteTask.size()=" + allExcuteTask.size());            if (lastExcuteTask != null) {                try {                    LogUtils.i("ExcuteTaskManager任務ID" + lastExcuteTask.getUniqueID());                    /**                     * 真正開始執行任務,                     * 所有的耗時任務都是在子線程中執行                     */                    doExcuteTask(lastExcuteTask);                } catch (Exception e) {                    // TODO: handle exception                    LogUtils.e("ExcuteTaskManager=====>執行任務發生了異常,信息為:" + e.getMessage());                    e.printStackTrace();                }                LogUtils.i("任務仍在執行,ExcuteTaskManager線程處於運行狀態,當前的線程的ID為:" + Thread.currentThread().getId());            } else {                LogUtils.i("任務執行完畢,ExcuteTaskManager線程處於等待狀態,當前的線程的ID為:" + Thread.currentThread().getId());                try {                    synchronized (allExcuteTask) {                        allExcuteTask.wait();                    }                } catch (InterruptedException e) {                    // TODO Auto-generated catch block                    LogUtils.e("ExcuteTaskManager=====>  線程等待時發生了錯誤,信息為:" + e.getMessage());                    e.printStackTrace();                }            }        }    }    /**     * 根據不同的ExcuteTask,執行相應的任務     *     * 這個是真正開始執行異步任務的地方,     * 即調用需要在子線程執行的代碼==>task.doTask()     *     * @param task ExcuteTask對象     */    private void doExcuteTask(ExcuteTask task) {        ExcuteTask result = task.doTask();        /**         *         * 開始執行的Task和最后得到的Task是同一個的時候,才會進行回調,         * 否則不進行回調(保證在回調得到數據的時候知道是哪一個Task,以便進行強轉)         *         *         * 沒有UniqueID相當於不需要回調         *         */        if (result != null && task == result && result.getUniqueID() != 0) {            /**             *  發送當前消息,更新UI(把數據回調到界面),             *  下面不用做任何的發送消息,             *  只在這一個地方發送就行,否者會發生錯誤!             */            Message msg = Message.obtain();            msg.what = COMMON_EXCUTE_TASK_TYPE;            msg.obj = result;            handler.sendMessage(msg);        } else {            uniqueListenerList.remove(task.getUniqueID());        }    }    /**     * 真正的回調操作,所有的任務在這里     * 把數據回調到主界面     *     * @param task ExcuteTask對象     */    private void doCommonHandler(ExcuteTask task) {        long start = System.currentTimeMillis();        LogUtils.i("已經進入了private void doCommonHandler(Message msg) {");        if (task != null && uniqueListenerList.containsKey(task.getUniqueID())) {            try {                /**                 * 回調整個Task數據                 * 然后可以回調方法中去直接更新UI                 */                ((GetExcuteTaskCallback) uniqueListenerList.get(task.getUniqueID())).onDataLoaded(task);                /**                 * 回調完成移除CallBack對象                 */                uniqueListenerList.remove(task.getUniqueID());                LogUtils.i("TaskManager========doCommonHandler=====回調成功====task 為:" + task.toString());            } catch (Exception e) {                // TODO: handle exception                LogUtils.e("TaskManager========doCommonHandler=====if (uniqueListenerList.containsKey(task.getUniqueID())) {" + e.toString());                e.printStackTrace();            }        } else {            LogUtils.i("TaskManager========doCommonHandler=====已經移除了回調監聽");        }        long end = System.currentTimeMillis();        LogUtils.i("執行回調doCommonHandler 耗時:" + (end - start));    }    /**=================================任務執行、回調、分發end============================================*/    /**=================================單線程池,可以順序,延遲執行一些任務start============================================*/    /**     * 順序執行耗時的操作     *     * @param runnable 對象     */    public void excute(Runnable runnable) {        singlePool.execute(runnable);    }    /**     * 順序執行耗時的操作     *     * @param runnable 對象     * @param delay    延遲執行的時間,單位毫秒     */    public void excute(Runnable runnable, long delay) {        singlePool.schedule(runnable, delay, TimeUnit.MILLISECONDS);    }    /**     * 順序執行耗時的操作     *     * @param runnable 對象     * @param delay    延遲執行的時間     * @param timeUnit 時間單位     */    public void excute(Runnable runnable, long delay, TimeUnit timeUnit) {        singlePool.schedule(runnable, delay, timeUnit);    }    public void scheduleAtFixedRate(Runnable runnable, long delay, long period, TimeUnit timeUnit) {        singlePool.scheduleAtFixedRate(runnable, delay, period, timeUnit);    }    public void scheduleAtFixedRate(Runnable runnable, long delay, long period) {        singlePool.scheduleAtFixedRate(runnable, delay, period, TimeUnit.MILLISECONDS);    }    public void scheduleAtFixedRate(Runnable runnable, long period) {        singlePool.scheduleAtFixedRate(runnable, 0, period, TimeUnit.MILLISECONDS);    }    /**=================================單線程池,可以順序,延遲執行一些任務end============================================*/}


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com