有的人在努力,有的人卻在拼命,不負當下
本篇博客主要是指引着寫一個簡單實用的線程切換工具,如果有更好的想法或者意見可以留言
我們需要帶着問題去學,看到題目不知道你是否有一下幾個問題:
- 如何切換線程
- 如何通知下一個需要進行的任務
- 如何去管理這些任務
- 如何能更好的更直觀的去看去使用
一、如何切換線程
首先我們要知道如何進行基礎的線程切換方法,如下:
主線程
原理:比較通用的方法是獲取主線程的handler
,然後使用post(runnable r)
方法,從寫run()
方法,run()
方法內的代碼就是在主線程內運行,即使你運行這段代碼在其他線程。代碼:
//初始化基礎數據 Looper looper=Looper.getMainLooper(); //構造主線程handler關鍵 Handler handler=new Handler(looper); //將主線程的looper放入 //運行在主線程的方法 public static void runInUiThread(Runnable r) { if (handler != null) { handler.post(r); } }
子線程
原理:正常情況下可以直接new Thread,然後調用start()方法,此處考慮到線程的重用,採用線程池。代碼:
//初始化基礎數據 private static ExecutorService mCacheExcutor = Executors.newCachedThreadPool();//初始化線程池,用於切換到線程 //運行在子線程的方法 public static void runInBackGroundThread(Runnable r) { if (mCacheExcutor != null) { mCacheExcutor.execute(r); } }
通過如上的步奏就已經知道如何將線程切換至主線程和子線程了,下面我們將如何將任務聯繫起來。
二、如何通知下一個任務?
我們要做的不能是沒有什麼用途的工具,所以在此我們考慮一下什麼時候需要進行線程切換?舉個例子,如果你需要從數據庫或者網絡獲取數據,然後刷新ui,這時候你可以採用原始的線程方式,然後內部多次嵌套,代碼如下:
嵌套執行
handler.post(new Runnable() { @Override public void run() { //顯示進度條 new Thread() { @Override public void run() { super.run(); //下載或者獲取內容 handler.post(new Runnable() { @Override public void run() { //關閉進度條 } }); } }.start(); } });
看到這樣的代碼只有絕望。。。估計沒有多少人願意讀,更不會想修改,當然我們也可以用google給封裝好的,其代碼如下:
AsyncTask方式
class DownloadTask extends AsyncTask<Void, Integer, Boolean> { @Override protected void onPreExecute() { //主線程 } @Override protected Boolean doInBackground(Void... params) { //子線程 } @Override protected void onProgressUpdate(Integer... values) { //主線程 } @Override protected void onPostExecute(Boolean result) { //主線程 } }
- 這個看起來確實挺直觀的,代碼邏輯也簡單了不少,可是如果需要多次切換呢,比如:主線程->子線程->主線程->子線程。。。這時候AsyncTask 就無法勝任了,當然目前還有個更加優秀的開源庫,本庫的思想也借鑑於此,這個庫更加酷炫(Rxjava),有興趣的可以看一下,我們開講我們的庫能幹什麼。
我們來完成這個主線程->子線程->主線程->子線程變態任務,如果我們的庫去寫如何實現呢?代碼如下:
- 這個看起來確實挺直觀的,代碼邏輯也簡單了不少,可是如果需要多次切換呢,比如:主線程->子線程->主線程->子線程。。。這時候AsyncTask 就無法勝任了,當然目前還有個更加優秀的開源庫,本庫的思想也借鑑於此,這個庫更加酷炫(Rxjava),有興趣的可以看一下,我們開講我們的庫能幹什麼。
當前庫的寫法
TaskManager.getNewInstance().addTask(new Task() { @Override public void runInTask() { //主線程 } }).addTask(new Task(true) { @Override public void runInTask() { //子線程 } }).addTask(new Task() { @Override public void runInTask() { //主線程 } }).addTask(new Task(true) { @Override public void runInTask() { //子線程 } }).start();
- 現在我們開始解剖Task 的設計,代碼如下:
public abstract class Task implements Runnable {
//下一個任務
private Task afterTask;
//標誌爲是否在線程中執行
private boolean isBackGround = false;
/**
* 構造函數
*
* @param isBackGround 標誌:是不是子線程執行
*/
public Task(boolean isBackGround) {
this.isBackGround = isBackGround;
}
/**
* 默認是ui線程
*/
public Task() {
this.isBackGround = false;
}
public Task getAfterTask() {
return afterTask;
}
public boolean isBackGround() {
return isBackGround;
}
@Override
public void run() {
if (isBackGround) {
ThreadUtils.runInBackGroundThread(new Runnable() {
@Override
public void run() {
runInTask();
runAfterTask();
}
});
} else {
ThreadUtils.runInUiThread(new Runnable() {
@Override
public void run() {
runInTask();
runAfterTask();
}
});
}
}
protected void runAfterTask() {
if (afterTask != null) {
afterTask.run();
}
}
public abstract void runInTask();
/**
* 添加下一個task
*
* @param task 需要添加的task
* @return 返回當前添加的task用於下次添加
*/
public Task addNextTask(Task task) {
setAfterTask(task);
return task;
}
private void setAfterTask(Task afterTask) {
this.afterTask = afterTask;
}
}
主要方法在run()方法內,根據自身的線程狀態位來判斷當前需要在什麼環境下執行,然後回調runInTask(),也就是暴露到外部的方法,然後執行下一個任務的run()方法,這樣就完成了任務之間的關聯,而且通過剛纔講過的線程工具方法,進行線程的切換。
三、如何取管理這些任務?
- 從上面使用的時候的代碼,通過TaskManager進行任務的添加並且使用start()方法開啓任務。
TaskManager代碼如下: - TaskManager代碼
public class TaskManager {
//第一個任務
private Task firstTask = null;
//上一個任務
private Task lastTask = null;
private TaskManager() {
}
public static TaskManager getNewInstance() {
return new TaskManager();
}
public void clearTask() {
firstTask = null;
lastTask = null;
}
public TaskManager addTask(Task task) {
if (firstTask == null) {
lastTask = firstTask = task;
} else {
lastTask.addNextTask(task);
lastTask = task;
}
return this;
}
public void start() {
if (firstTask != null) {
firstTask.run();
} else {
throw new NullPointerException("任務不能爲空!");
}
}
}
主要是兩個關鍵的地方,一個是記錄第一個,用於開啓整個任務,另一個是記錄上一個,用於添加下一個任務
四、如何讓邏輯更加簡練?
我們的庫採用的是鏈式調用,既保證了線程的切換,也保證了邏輯的連貫性,邏輯更容易理解和易懂。