(Возможно) Утечка памяти с помощью пользовательского ThreadPoolExecutor - PullRequest
0 голосов
/ 17 октября 2018

Чтобы централизовать обработку фоновых потоков в моем приложении для 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 ответы также приветствуются)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...