Чтобы централизовать обработку фоновых потоков в моем приложении для Android, я определил следующее Runnable
:
class Task(private val runnable: Runnable) : Runnable {
interface Callback {
fun onTaskStarted()
fun onTaskFinished()
}
var callback: Callback? = null
override fun run() {
callback?.run { mainHandler.post { this.onTaskStarted() } }
runnable.run()
callback?.run { mainHandler.post { this.onTaskFinished() } }
// setting callback to null, so the Task instance can be garbage collected
callback = null
}
companion object {
private val mainHandler = Handler(Looper.getMainLooper())
}
}
И следующее ThreadPoolExecutor
:
class CustomThreadPoolExecutor :
ThreadPoolExecutor(2, 2, 0L, TimeUnit.MILLISECONDS, LinkedBlockingQueue<Runnable>()),
Task.Callback {
val taskCount: MutableLiveData<Int> by lazy {
MutableLiveData<Int>().apply { value = 0 }
}
fun executeTask(task: Task) {
task.callback = this
super.execute(task)
}
@MainThread
override fun onTaskStarted() {
taskCount.value = taskCount.value?.plus(1) ?: 1
}
@MainThread
override fun onTaskFinished() {
taskCount.value = taskCount.value?.minus(1) ?: 0
}
}
Последнеепредставляет собой синглтон, предоставленный Dagger :
@Provides
@Singleton
fun provideCustomThreadPoolExecutor(): CustomThreadPoolExecutor = CustomThreadPoolExecutor()
Идея состоит в том, чтобы использовать этого исполнителя для всех фоновых задач.
Объект taskCount
можно наблюдать и дляпример показать / скрыть ProgressBar
в зависимости от того, выполняются ли в данный момент задачи или нет.
Доступ к объекту taskCount
возможен только из основного потока, поэтому не должно быть проблем с параллелизмом (исправьтеменя, если я ошибаюсь).
callback
- это сам экземпляр ThreadPoolExecutor
(который является одиночным), и ссылка очищается, как только задача завершена, так что это не должно бытьлибо проблема (еще раз, пожалуйста, исправьте меня, если я ошибаюсь).
Проблема:
Чтобы вставить данные в мою базу данных, я бы позвонил следующимметод моего Activity
(или Fragment
)
private fun saveTestData(testItems: List<TestItem>) {
customThreadPoolExecutor.executeTask(Task(Runnable {
testService.saveAll(testItems)
}))
}
Runnable
здесь является анонимным внутренним классоми, таким образом (я думаю) содержит ссылку на включающий Activity
(или Fragment
).
Из-за этого Runnable
(и упаковку Task
) нельзя собирать, поэтомукаждый раз, когда я вызываю этот метод, количество экземпляров будет увеличиваться (и никогда не уменьшаться).
Верны ли мои предположения?
Если да, как я могу решить эту проблему?
(Java ответы также приветствуются)