1. 程式人生 > >安卓(Android)中如何優雅的 建立/執行 非同步任務/回撥(AsyncTask 、Thread、Job、CallBack、Handler) ?執行緒池(Executor)

安卓(Android)中如何優雅的 建立/執行 非同步任務/回撥(AsyncTask 、Thread、Job、CallBack、Handler) ?執行緒池(Executor)

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:
[email protected]
) * 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============================================*/ }

相關推薦

(Android)如何優雅建立/執行 非同步任務/(AsyncTask ThreadJobCallBackHandler) ?執行(Executor)

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

EasyDarwin直播之EasyPusher NDK開發:JNI函式的實現

最近在做EasyDarwin的EasyPusher手機直播專案開發時涉及到JNI回撥,今日便研究了一下,跟native呼叫Java層的程式碼不同,此文說的是直接通過setCallback的方式去實現回撥: 先看一下載入so庫的程式碼,我就直接在Activity

AndroidService與Activity的通訊---介面方式

最近在技術交流群中有人問到:要實現service與activity的高強度通訊用什麼方法? 群友回答的是用handler,但面試官好像不太滿意,後來本人查找了下資料,得到個人的結論:service與activity之前的通訊方式有很多,回撥介面方式、觀察者模式、廣播、還有h

開發-尺寸單位+Logcat的使用+Android單元測試+系統資料的讀寫

知識檢視: 《一:尺寸單位》 1.px:電腦 電視上的解析度的尺寸單位。畫素是構成數碼影像的基本單元。例如300x300解析度,即表示水平方向與垂直方向上每英寸長度上的像 素數都是300,也

AndroidWebApp 全屏和普通模式下 webview軟鍵盤彈出擋住輸入框 解決方案

最近公司開發的WEBAPP遇到了一個比較噁心的問題。就是在webview中當需要輸入內容時,軟鍵盤彈起後,擋住了輸入框,試了很多辦法都不太滿意。(這裡要吐槽下,人家IOS端,蘋果系統都給直接封裝好了,

手機pdf閱讀器怎麽安裝?各款pdf閱讀器哪個好

輕快PDF閱讀器現在的人們基本人手一部手機,還是各種品牌、各種款式的。今天我們就詳細說說安卓系統的手機怎麽去安裝pdf閱讀器?所有pdf閱讀器中哪種最好。 輕快PDF閱讀器是一款體積小、啟動速度快、閱讀效果最好的PDF閱讀軟件。適用於安卓設備,支持隨時隨地查看、註釋和保護PDF文件。其閱讀模式也頗具個性

怎麽恢復手機被清空的照片? 手機照片誤刪怎麽恢復

在安卓手機卡頓或下載不了軟件顯示內存不夠的時候,我們大多數人的做法就是將安卓手機還原成出廠設置。這就意味著手機中的照片被全部清空了,那被清空的手機照片還能恢復嗎?我們該怎麽恢復手機中被清空的照片? 借助互盾照片恢復軟件免費版,能有效的恢復安卓手機中被誤清理的照片。操作簡單,電

win10 大文件 android studio修改 漢化

分布 ini con 自己 重復 install 復用 問題 下載 韓夢飛沙 韓亞飛 [email protected]/* */ yue31313 han_meng_fei_sha ========== win10 大文件 gradle-3.

開發Theme.AppCompat.Light的解決方法

col san 文件 輸入 安卓 light style match ont styles.xml中<style name="AppBaseTheme" parent="Theme.AppCompat.Light">提示如下錯誤,這是版本問題。 error: E

計算機畢業設計源碼分享-801雙魚林Android美食評論愛食app

andro l數據庫 源碼分享 eight 自己 nbsp 單個 對象信息 課程 開發環境: Myclipse(服務器端) + Eclipse(手機客戶端) + mysql數據庫 學生: 學號,登錄密碼,姓名,性別,出生日期,聯系電話,家庭地址食堂: 食堂id,食堂名稱菜品

開發使用ZXing生成解析二維碼

roi contents arc bar white png 解析 tex span 編碼示例 package com.wolf_pan.qrcodesample; import android.graphics.Bitmap; import android.graph

解析觀察者模式在程序的應用——如何實現跨界面Handler通訊

設計之下 管理 alt cto mov png opera 規範 bubuko 這裏特使用了github中的一個項目作為例子進行解析,地址為:https://github.com/LiqiNew/HandlerFrame/tree/master/handler

開發判斷APP在前端還是在後臺

1.在自己的APPlication中定義一個int 型別的變數 private int activityCount; 2.在APPlication建立一個內部類 實現 ActivityLifecycleCallbacks private class CCActivityLifecyc

開發傳送簡訊的方法 APP內單發簡訊 群發簡訊 APP跳轉到簡訊頁 單條簡訊 多條簡訊Intent跳轉

1.APP內部直接傳送短息 包括髮送單條簡訊 傳送多條簡訊 APP內部發送簡訊首先要獲取一個傳送簡訊的許可權 <uses-permission android:name="android.permission.SEND_SMS"/> 安卓6.0以上的手機要動態獲取到這個許可

手機除錯網站

嘗試在手機上 去進行專案的預覽和測試 要保證自己的手機可以正常執行; 要保證 手機 和 開發專案的電腦 處於同一個 WIFI 環境中,也就是說手機可以訪問到電腦的 IP 開啟自己的 專案中 package.json 檔案,在 dev 指令碼中,新增一個 --hos

Message引數的含義

使用者定義這個訊息用於區分這個訊息到底是屬於誰來處理的 arg1和arg2相對於setData方法來講,當你只是想攜帶int型別的資料時 我們的使用成本更加低廉 obj可以傳遞任意型別的資料,但在程序間傳遞序列化的框架類時必須保證非空,並建議傳遞其他資料時還是以setDat

setOnItemClickListener關於Cursor的一個小問題

  在之前擬定的業務需求中,涉及到獲取呼叫listview中點選項資料的問題。先前的解決方法為:從頭開始遍歷獲取,如果存在很多資料,會將每一條都處理一遍。 while(!cursor.isAfterLast()){ Cursor cursor = select(); cursor.mo

關於在apk增加官方簽名校驗的說明

眾所周知,安卓apk的釋出,是需要經過簽名這一道程式的。 另外,要破解一個APK,必然需要重新對APK進行簽名。而這個簽名,一般情況無法再與APK原先的簽名保持一致。(除非APK原作者的私鑰洩漏,那已經是另一個層次的軟體安全問題了。)簽名機制標明瞭APK的發行機構。因此,站在軟

關於專案統計資料achartengine類(包括餅狀圖,柱狀圖,折線圖等)的使用

       最近由於專案中需要統計資料,通過比較直觀的方式展示給使用者,所以就抽時間學習了一個統計圖的類庫achartengine,裡面包含統計中所需的餅狀圖,柱狀圖,折線圖等等,所以就拿出來給大家分享一下,希望大家相互學習,如有不足之處

Android基礎第五天

使用HttpUrlConnection方式提交到伺服器2 Get方式:組拼url地址把資料組拼到url上,有大小限制1kb(瀏覽器)或4kb(http協議) Post方式:post方式提交安全,沒有大小限制 Post方式通過請求體的方式把資料寫給伺服器