Replace SingleThreadTaskRunner and AndroidTaskRunner with CoroutineTaskRunner
This commit is contained in:
parent
400d543191
commit
61d8f358eb
@ -1578,17 +1578,6 @@
|
||||
file="src/main/res/mipmap-anydpi-v26"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="StaticFieldLeak"
|
||||
message="This `AsyncTask` class should be static or leaks might occur (org.isoron.uhabits.tasks.AndroidTaskRunner.CustomAsyncTask)"
|
||||
errorLine1=" private inner class CustomAsyncTask(val task: Task) : AsyncTask<Void?, Int?, Void?>() {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/org/isoron/uhabits/tasks/AndroidTaskRunner.kt"
|
||||
line="57"
|
||||
column="25"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="VectorPath"
|
||||
message="Very long vector path (1667 characters), which is bad for performance. Considering reducing precision, removing minor details or rasterizing vector."
|
||||
|
||||
@ -19,10 +19,11 @@
|
||||
package org.isoron.uhabits
|
||||
|
||||
import android.content.Context
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import me.tatarka.inject.annotations.Component
|
||||
import me.tatarka.inject.annotations.Provides
|
||||
import org.isoron.uhabits.core.AppScope
|
||||
import org.isoron.uhabits.core.tasks.SingleThreadTaskRunner
|
||||
import org.isoron.uhabits.core.tasks.CoroutineTaskRunner
|
||||
import org.isoron.uhabits.core.tasks.TaskRunner
|
||||
import org.isoron.uhabits.inject.AppContext
|
||||
import org.isoron.uhabits.inject.HabitsApplicationComponent
|
||||
@ -41,5 +42,8 @@ abstract class HabitsApplicationTestComponent(
|
||||
|
||||
@AppScope
|
||||
@Provides
|
||||
override fun taskRunner(): TaskRunner = SingleThreadTaskRunner()
|
||||
override fun taskRunner(): TaskRunner = CoroutineTaskRunner(
|
||||
mainDispatcher = Dispatchers.Main,
|
||||
ioDispatcher = Dispatchers.IO
|
||||
)
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
package org.isoron.uhabits.inject
|
||||
|
||||
import android.content.Context
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import me.tatarka.inject.annotations.Component
|
||||
import me.tatarka.inject.annotations.Provides
|
||||
import org.isoron.platform.io.AndroidFileOpener
|
||||
@ -35,6 +36,7 @@ import org.isoron.uhabits.core.models.sqlite.SQLiteHabitList
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.core.preferences.WidgetPreferences
|
||||
import org.isoron.uhabits.core.reminders.ReminderScheduler
|
||||
import org.isoron.uhabits.core.tasks.CoroutineTaskRunner
|
||||
import org.isoron.uhabits.core.tasks.TaskRunner
|
||||
import org.isoron.uhabits.core.ui.NotificationTray
|
||||
import org.isoron.uhabits.core.ui.screens.habits.list.HabitCardListCache
|
||||
@ -49,7 +51,6 @@ import org.isoron.uhabits.io.AndroidLogging
|
||||
import org.isoron.uhabits.notifications.AndroidNotificationTray
|
||||
import org.isoron.uhabits.preferences.SharedPreferencesStorage
|
||||
import org.isoron.uhabits.receivers.ReminderController
|
||||
import org.isoron.uhabits.tasks.AndroidTaskRunner
|
||||
import org.isoron.uhabits.utils.DatabaseUtils
|
||||
import org.isoron.uhabits.widgets.WidgetUpdater
|
||||
import java.io.File
|
||||
@ -137,7 +138,10 @@ abstract class HabitsApplicationComponent(
|
||||
|
||||
@AppScope
|
||||
@Provides
|
||||
open fun taskRunner(): TaskRunner = AndroidTaskRunner()
|
||||
open fun taskRunner(): TaskRunner = CoroutineTaskRunner(
|
||||
mainDispatcher = Dispatchers.Main,
|
||||
ioDispatcher = Dispatchers.IO
|
||||
)
|
||||
|
||||
@AppScope
|
||||
@Provides
|
||||
|
||||
@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016-2025 Álinson Santos Xavier <git@axavier.org>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.isoron.uhabits.tasks
|
||||
|
||||
import android.os.AsyncTask
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.isoron.uhabits.core.tasks.Task
|
||||
import org.isoron.uhabits.core.tasks.TaskRunner
|
||||
import java.util.HashMap
|
||||
import java.util.LinkedList
|
||||
|
||||
class AndroidTaskRunner : TaskRunner {
|
||||
private val activeTasks: LinkedList<CustomAsyncTask> = LinkedList()
|
||||
private val taskToAsyncTask: HashMap<Task, CustomAsyncTask> = HashMap()
|
||||
private val listeners: LinkedList<TaskRunner.Listener> = LinkedList<TaskRunner.Listener>()
|
||||
override fun addListener(listener: TaskRunner.Listener) {
|
||||
listeners.add(listener)
|
||||
}
|
||||
|
||||
override fun execute(task: Task) {
|
||||
task.onAttached(this)
|
||||
CustomAsyncTask(task).execute()
|
||||
}
|
||||
|
||||
override val activeTaskCount: Int
|
||||
get() = activeTasks.size
|
||||
|
||||
override fun publishProgress(task: Task, progress: Int) {
|
||||
val asyncTask = taskToAsyncTask[task] ?: return
|
||||
asyncTask.publish(progress)
|
||||
}
|
||||
|
||||
override fun removeListener(listener: TaskRunner.Listener) {
|
||||
listeners.remove(listener)
|
||||
}
|
||||
|
||||
private inner class CustomAsyncTask(val task: Task) : AsyncTask<Void?, Int?, Void?>() {
|
||||
|
||||
fun publish(progress: Int) {
|
||||
publishProgress(progress)
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun doInBackground(vararg params: Void?): Void? {
|
||||
if (isCancelled) return null
|
||||
runBlocking { task.doInBackground() }
|
||||
return null
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun onPostExecute(aVoid: Void?) {
|
||||
if (isCancelled) return
|
||||
task.onPostExecute()
|
||||
activeTasks.remove(this)
|
||||
taskToAsyncTask.remove(task)
|
||||
for (l in listeners) l.onTaskFinished(task)
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun onPreExecute() {
|
||||
if (isCancelled) return
|
||||
for (l in listeners) l.onTaskStarted(task)
|
||||
activeTasks.add(this)
|
||||
taskToAsyncTask[task] = this
|
||||
task.onPreExecute()
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun onProgressUpdate(vararg values: Int?) {
|
||||
values[0]?.let { task.onProgressUpdate(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -18,12 +18,14 @@
|
||||
*/
|
||||
package org.isoron.uhabits
|
||||
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import org.isoron.platform.time.LocalDate
|
||||
import org.isoron.platform.time.setToday
|
||||
import org.isoron.uhabits.core.commands.CommandRunner
|
||||
import org.isoron.uhabits.core.models.HabitList
|
||||
import org.isoron.uhabits.core.models.memory.MemoryModelFactory
|
||||
import org.isoron.uhabits.core.tasks.SingleThreadTaskRunner
|
||||
import org.isoron.uhabits.core.tasks.CoroutineTaskRunner
|
||||
import org.isoron.uhabits.core.tasks.TaskRunner
|
||||
import org.isoron.uhabits.core.test.HabitFixtures
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
@ -33,7 +35,7 @@ open class BaseAndroidJVMTest {
|
||||
private lateinit var habitList: HabitList
|
||||
protected lateinit var fixtures: HabitFixtures
|
||||
private lateinit var modelFactory: MemoryModelFactory
|
||||
private lateinit var taskRunner: SingleThreadTaskRunner
|
||||
private lateinit var taskRunner: TaskRunner
|
||||
private lateinit var commandRunner: CommandRunner
|
||||
|
||||
@Before
|
||||
@ -42,7 +44,10 @@ open class BaseAndroidJVMTest {
|
||||
modelFactory = MemoryModelFactory()
|
||||
habitList = modelFactory.buildHabitList()
|
||||
fixtures = HabitFixtures(modelFactory, habitList)
|
||||
taskRunner = SingleThreadTaskRunner()
|
||||
taskRunner = CoroutineTaskRunner(
|
||||
mainDispatcher = UnconfinedTestDispatcher(),
|
||||
ioDispatcher = UnconfinedTestDispatcher()
|
||||
)
|
||||
commandRunner = CommandRunner(taskRunner)
|
||||
}
|
||||
|
||||
|
||||
@ -18,33 +18,51 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core.tasks
|
||||
|
||||
import org.isoron.platform.runSuspend
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class SingleThreadTaskRunner : TaskRunner {
|
||||
override val activeTaskCount: Int
|
||||
get() = 0
|
||||
class CoroutineTaskRunner(
|
||||
mainDispatcher: CoroutineDispatcher,
|
||||
private val ioDispatcher: CoroutineDispatcher
|
||||
) : TaskRunner {
|
||||
|
||||
private val scope = CoroutineScope(SupervisorJob() + mainDispatcher)
|
||||
private val listeners = mutableListOf<TaskRunner.Listener>()
|
||||
private var activeCount = 0
|
||||
|
||||
override val activeTaskCount: Int get() = activeCount
|
||||
|
||||
private val listeners: MutableList<TaskRunner.Listener> = mutableListOf()
|
||||
override fun addListener(listener: TaskRunner.Listener) {
|
||||
listeners.add(listener)
|
||||
}
|
||||
|
||||
override fun execute(task: Task) {
|
||||
for (l in listeners) l.onTaskStarted(task)
|
||||
if (!task.isCanceled()) {
|
||||
task.onAttached(this)
|
||||
task.onPreExecute()
|
||||
runSuspend { task.doInBackground() }
|
||||
task.onPostExecute()
|
||||
}
|
||||
for (l in listeners) l.onTaskFinished(task)
|
||||
}
|
||||
|
||||
override fun publishProgress(task: Task, progress: Int) {
|
||||
task.onProgressUpdate(progress)
|
||||
}
|
||||
|
||||
override fun removeListener(listener: TaskRunner.Listener) {
|
||||
listeners.remove(listener)
|
||||
}
|
||||
|
||||
override fun execute(task: Task) {
|
||||
task.onAttached(this)
|
||||
scope.launch {
|
||||
activeCount++
|
||||
listeners.forEach { it.onTaskStarted(task) }
|
||||
task.onPreExecute()
|
||||
if (!task.isCanceled()) {
|
||||
withContext(ioDispatcher) {
|
||||
task.doInBackground()
|
||||
}
|
||||
}
|
||||
task.onPostExecute()
|
||||
activeCount--
|
||||
listeners.forEach { it.onTaskFinished(task) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun publishProgress(task: Task, progress: Int) {
|
||||
scope.launch {
|
||||
task.onProgressUpdate(progress)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -33,6 +33,7 @@ import org.isoron.uhabits.core.models.NumericalHabitType.AT_MOST
|
||||
import org.isoron.uhabits.core.models.PaletteColor
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.core.tasks.ExportCSVTask
|
||||
import org.isoron.uhabits.core.tasks.Task
|
||||
import org.isoron.uhabits.core.tasks.TaskRunner
|
||||
import org.isoron.uhabits.core.ui.callbacks.CheckMarkDialogCallback
|
||||
import org.isoron.uhabits.core.ui.callbacks.NumberPickerCallback
|
||||
@ -107,10 +108,14 @@ open class ListHabitsBehavior(
|
||||
}
|
||||
|
||||
open fun onRepairDB() {
|
||||
taskRunner.execute {
|
||||
habitList.repair()
|
||||
screen.showMessage(Message.DATABASE_REPAIRED)
|
||||
}
|
||||
taskRunner.execute(object : Task {
|
||||
override suspend fun doInBackground() {
|
||||
habitList.repair()
|
||||
}
|
||||
override fun onPostExecute() {
|
||||
screen.showMessage(Message.DATABASE_REPAIRED)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
open fun onSendBugReport() {
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.isoron.uhabits.core
|
||||
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import org.isoron.platform.io.Database
|
||||
import org.isoron.platform.io.DatabaseOpener
|
||||
import org.isoron.platform.io.FileOpener
|
||||
@ -31,7 +32,8 @@ import org.isoron.uhabits.core.commands.CommandRunner
|
||||
import org.isoron.uhabits.core.models.HabitList
|
||||
import org.isoron.uhabits.core.models.ModelFactory
|
||||
import org.isoron.uhabits.core.models.memory.MemoryModelFactory
|
||||
import org.isoron.uhabits.core.tasks.SingleThreadTaskRunner
|
||||
import org.isoron.uhabits.core.tasks.CoroutineTaskRunner
|
||||
import org.isoron.uhabits.core.tasks.TaskRunner
|
||||
import org.isoron.uhabits.core.test.HabitFixtures
|
||||
import kotlin.test.BeforeTest
|
||||
|
||||
@ -39,7 +41,7 @@ open class BaseUnitTest {
|
||||
protected open lateinit var habitList: HabitList
|
||||
protected lateinit var fixtures: HabitFixtures
|
||||
protected lateinit var modelFactory: ModelFactory
|
||||
protected lateinit var taskRunner: SingleThreadTaskRunner
|
||||
protected lateinit var taskRunner: TaskRunner
|
||||
protected open lateinit var commandRunner: CommandRunner
|
||||
protected val fileOpener: FileOpener = createTestFileOpener()
|
||||
private var _databaseOpener: DatabaseOpener? = null
|
||||
@ -57,7 +59,10 @@ open class BaseUnitTest {
|
||||
habitList = memoryModelFactory.buildHabitList()
|
||||
fixtures = HabitFixtures(memoryModelFactory, habitList)
|
||||
modelFactory = memoryModelFactory
|
||||
taskRunner = SingleThreadTaskRunner()
|
||||
taskRunner = CoroutineTaskRunner(
|
||||
mainDispatcher = UnconfinedTestDispatcher(),
|
||||
ioDispatcher = UnconfinedTestDispatcher()
|
||||
)
|
||||
commandRunner = CommandRunner(taskRunner)
|
||||
}
|
||||
|
||||
|
||||
@ -21,18 +21,22 @@ package org.isoron.uhabits.core.tasks
|
||||
import dev.mokkery.mock
|
||||
import dev.mokkery.verify.VerifyMode.Companion.order
|
||||
import dev.mokkery.verifySuspend
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import org.isoron.uhabits.core.BaseUnitTest
|
||||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.Test
|
||||
|
||||
class SingleThreadTaskRunnerTest : BaseUnitTest() {
|
||||
private lateinit var runner: SingleThreadTaskRunner
|
||||
class CoroutineTaskRunnerTest : BaseUnitTest() {
|
||||
private lateinit var runner: CoroutineTaskRunner
|
||||
private var task: Task = mock()
|
||||
|
||||
@BeforeTest
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
runner = SingleThreadTaskRunner()
|
||||
runner = CoroutineTaskRunner(
|
||||
mainDispatcher = UnconfinedTestDispatcher(),
|
||||
ioDispatcher = UnconfinedTestDispatcher()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
Loading…
Reference in New Issue
Block a user