阅读 1222

[Android开源]EasyExecutor: 让线程任务的使用变得高效、安全、方便、灵活

什么是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)
	}
复制代码

欢迎扫描下方二维码关注我的公众号👇

关注下面的标签,发现更多相似文章
评论