[Android開源]EasyExecutor: 讓執行緒任務的使用變得高效、安全、方便、靈活

Haoge發表於2018-07-06

什麼是EasyExecutor

EasyExecutor是開源基礎元件整合庫EasyAndroid中的基礎元件之一。

其作用是:對執行緒池進行二次封裝,為上層提供最簡單、最方便的執行緒操作體驗

EasyAndroid作為一款整合元件庫,此庫中所整合的元件,均包含以下特點,你可以放心使用~~

1. 設計獨立

元件間獨立存在,不相互依賴,且若只需要整合庫中的部分元件。也可以很方便的只copy對應的元件檔案進行使用

2. 設計輕巧

因為是元件整合庫,所以要求每個元件的設計儘量精練、輕巧。避免因為一個小功能而引入大量無用程式碼.

每個元件的方法數均不超過100. 大部分元件甚至不超過50

得益於編碼時的高內聚性,若你只需要使用EasyExecutor. 那麼可以直接去拷貝EasyExecutor原始碼檔案到你的專案中,直接進行使用,也是沒問題的。

EasyAndroid開源庫地址:

https://github.com/yjfnypeu/EasyAndroid

EasyExecutor元件地址:

https://github.com/yjfnypeu/EasyAndroid/blob/master/utils/src/main/java/com/haoge/easyandroid/easy/EasyExecutor.kt

特性

  • 安全: 直接catch住任務執行期間出現的異常。並通知給使用者,避免出現crash
  • 回撥通知: 執行任務期間,有分別的生命週期作為通知。
  • 配置靈活: 可方便、靈活的對每次所啟動的任務,配置執行緒名、回撥等。
  • 任務延遲: 支援在每次啟動任務前。指定延遲時間
  • 非同步任務: 支援直接啟動非同步任務並回撥傳遞資料
  • 執行緒切換: 支援指定回撥方法所在的執行緒。預設為執行於UI執行緒中
  • 進度通知: 支援方便地進行任務處理進度通知

用法

建立配置EasyExecutor例項

EasyExecutor是對執行緒池進行的封裝,所以在建立時,我們需要指定需要建立的執行緒池的大小

val builder = EasyExecutor.newBuilder(size)
... // 其他配置
val executor = builder.build()// 配置完成後再建立EasyExecutor提供使用
複製程式碼

引數size為Int型別,即為指定的執行緒池大小,size與建立的執行緒池的關係為:

val executor = when {
	// size小於1, 建立可快取大小的執行緒池提供使用
	size <= 0 -> Executors.newCachedThreadPool(createFactory())
	// size大於0, 建立指定大小的執行緒池。
	else -> Executors.newFixedThreadPool(size, createFactory())
}
複製程式碼

執行緒優先順序配置

可以通過以下方式,指定執行緒池建立出來的執行緒優先順序

builder.setPriority(priority)
複製程式碼

執行緒任務名配置

所謂任務名即是建立出來的執行緒的執行緒名, 而指定任務名的方式有以下兩種:

1.指定預設任務名:在建立時進行指定

builder.setName("default name")
複製程式碼

2.指定臨時任務名:在使用前進行指定

executor.setName("temp name")
複製程式碼

臨時預設任務名的關係是:在啟動一次後臺任務時:

  • 有配置臨時任務名時:使用臨時任務名作為此次的執行緒任務名,並將此臨時任務名進行重置
  • 沒配置臨時任務名時:使用預設任務名作為此次的執行緒任務名。

啟動非同步任務

普通非同步任務,直接建立任務進行啟動即可:

executor.execute { // TODO } 
複製程式碼

很多時候,在java原生中的使用習慣是:普通任務都是通過Runnable介面進行定義,所以EasyExecutor也提供了直接使用Runnable任務的過載方法:

val runnable:Runnable = createTask()
executor.execute(runnable)
複製程式碼

啟動非同步回撥任務

非同步回撥任務:用於在需要接收非同步任務返回值時使用:

executor.async(
    task:(Notifier) -> T, // Notifier將在後面的進度通知小節進行介紹
    result:(T)->Unit)// 接收task的返回值並通知到此
複製程式碼

比如說。在子執行緒進行Bitmap建立:

executor.async(
	{ // 非同步任務
		// 子執行緒中,進行Bitmap建立,
		return@Callable bitmap
	},
	{ bitmap -> // 非同步回撥,預設執行於UI執行緒
		// TODO 使用建立好的bitmap進行操作
		// 關於回撥執行緒的派發,後面 派發器 小節會進行詳細說明
	}
)
複製程式碼

啟動延遲任務

如果需要指定此次任務需要被延遲執行時。使用setDelay直接指定延遲時間即可:

executor.setDelay(delayTime)
複製程式碼

請注意: delayTime型別為Long, 單位為毫秒。且此延遲時間的有效作用域是此次被啟動的任務。一旦有任務被啟動之後。延遲時間將被重置。也就是:

executor.setDelay(3000)
executor.execute(task1)// task1任務將被延遲3秒執行
executor.execute(task2)// task2任務將被直接執行
複製程式碼

安全的進行回撥派發

EasyExecutor提供三個回撥方法:

型別 lambda 說明
onStart (String) -> Unit 當執行緒任務被執行時被觸發,引數為任務名
onError (String, Throwable) -> Unit 當執行緒任務執行出現異常時被觸發, 引數為任務名出現的異常
onSuccess (String) -> Unit 當執行緒任務執行完成時被觸發,引數為任務名

此三種回撥,也有預設回撥配置臨時回撥配置的區別

指定預設回撥:作用域為所有的執行緒任務

builder.onStart {threadName -> } // 所有任務啟動時的回撥
    .onSuccess {threadName -> }// 所有任務執行完畢後的回撥
    .onError {threadName, throwable -> }// 所有任務執行出現異常時的回撥
複製程式碼

指定臨時回撥:作用域為此次啟動的執行緒任務

executor.onStart {threadName -> } // 此次任務啟動時的回撥
    .onSuccess {threadName -> }// 此次任務執行完畢後的回撥
    .onError {threadName, throwable -> }// 此次任務執行出現異常時的回撥
複製程式碼

所以,與任務名配置不同。臨時回撥不會覆蓋掉預設回撥,而他們被觸發的先後順序是:先觸發預設回撥。再觸發臨時回撥

而所謂安全,即是在整個任務的執行過程中。會將執行過程中出現的異常進行捕獲。防止任務執行出錯導致crash。被捕獲的異常將會通過onError回撥。通知到指定執行緒進行處理:

Thread.currentThread().setUncaughtExceptionHandler {
    name, e ->
    // deliver:派發器。將訊息通知到指定執行緒。
    deliver.execute {
        builder.error?.invoke(name, e)// 預設回撥異常通知
        error?.invoke(name, e)// 臨時回撥異常通知
    }
}
複製程式碼

配置派發器

就以上面的三個回撥方法為例:我們在說的時候。只說了它們被觸發的時機,但是沒說它們具體執行在哪個執行緒中。而這,就是派發器乾的事:

派發器的作用: 就是將訊息派發到指定執行緒中去之後再進行使用者通知!

派發器的本質,是一個Executor介面的實現類:

internal var deliver:Executor = UIDeliver
複製程式碼

而預設使用的UIDeliver,就是專門針對Android執行時環境建立的:將訊息派發到UI執行緒進行通知

internal val UIDeliver:Executor = Executor { runnable ->
    if (Looper.myLooper() == Looper.getMainLooper()) {
        runnable.run()
    } else {
        mainHandler.post { runnable.run() }
    }
}
複製程式碼

而對於派發器來說。也存在預設配置臨時配置

預設配置:當不存在臨時派發器配置時,使用此預設派發器

builder.setDeliver(Executor {})
複製程式碼

臨時配置:只對此次啟動任務生效

executor.setDeliver(Executor {})
複製程式碼

需要注意的是:派發器也對非同步回撥任務生效。所以在預設配置下,非同步回撥也是執行於UI執行緒中的:

executor.async(
	Callable<T> {return T},
	{ T ->
		// 此回撥所處執行緒也受派發器控制。
	}
)
複製程式碼

進行非同步任務進度通知

有些時候,我們會需要在任務的處理過程中,對外進行進度狀態通知,以便進行上傳的進度UI更新。使用EasyExecutor。也可以很方便的做到進度通知的效果:

需要進行狀態通知,首先需要定製進度回撥通知:

executor.onProgressChanged { current:Long, total:Long ->
	// 傳參current為當前的進度, total為資料總量。
	// 此回撥所處執行緒也受派發器控制
}
複製程式碼

EasyExecutor本身提供的任務模型就提供了有進行外部狀態通知的例項:

// 普通任務:
executor.execute { notifier -> }
// 非同步任務
executor.async( { notifier -> }, { result -> })
複製程式碼

任務例項中的傳參notifier即是用於進行外部通知的類。對於需要監聽狀態通知的任務。可以通過此例項方便的指定進度資訊。

所以一個完整的進度回撥任務模型應該是如以下程式碼一樣:

executor.onProgressChanged { 
	current, total -> 
		TODO("進行進度變化通知展示")
	}
	.execute { notifier -> 
		// 在合適的處理進度中。使用以下api進行進度狀態派發即可
		notifier.progressChanged(current, total)
	}
複製程式碼

歡迎掃描下方二維碼關注我的公眾號?

[Android開源]EasyExecutor: 讓執行緒任務的使用變得高效、安全、方便、靈活

相關文章