commit c06678d72a12c7f5bac3e8080e805da07f49f8fd
parent 5fd6aec2566b11ebc0581b1e036beafb11409027
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date: Sun, 1 Oct 2023 12:29:02 +0200
refactor: init optimization
Diffstat:
7 files changed, 115 insertions(+), 76 deletions(-)
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/RemoteSideContext.kt b/app/src/main/kotlin/me/rhunk/snapenhance/RemoteSideContext.kt
@@ -26,12 +26,12 @@ import me.rhunk.snapenhance.e2ee.E2EEImplementation
import me.rhunk.snapenhance.messaging.ModDatabase
import me.rhunk.snapenhance.messaging.StreaksReminder
import me.rhunk.snapenhance.scripting.RemoteScriptManager
-import me.rhunk.snapenhance.ui.overlay.SettingsOverlay
import me.rhunk.snapenhance.ui.manager.MainActivity
import me.rhunk.snapenhance.ui.manager.data.InstallationSummary
import me.rhunk.snapenhance.ui.manager.data.ModInfo
import me.rhunk.snapenhance.ui.manager.data.PlatformInfo
import me.rhunk.snapenhance.ui.manager.data.SnapchatAppInfo
+import me.rhunk.snapenhance.ui.overlay.SettingsOverlay
import me.rhunk.snapenhance.ui.setup.Requirements
import me.rhunk.snapenhance.ui.setup.SetupActivity
import java.io.ByteArrayInputStream
@@ -43,6 +43,8 @@ import java.security.cert.X509Certificate
class RemoteSideContext(
val androidContext: Context
) {
+ val coroutineScope = CoroutineScope(Dispatchers.IO)
+
private var _activity: WeakReference<ComponentActivity>? = null
lateinit var bridgeService: BridgeService
@@ -78,7 +80,6 @@ class RemoteSideContext(
}
.components { add(VideoFrameDecoder.Factory()) }.build()
}
- val coroutineScope = CoroutineScope(Dispatchers.IO)
fun reload() {
log.verbose("Loading RemoteSideContext")
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt
@@ -11,6 +11,9 @@ import android.os.Process
import android.widget.Toast
import com.google.gson.Gson
import com.google.gson.GsonBuilder
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
import me.rhunk.snapenhance.core.Logger
import me.rhunk.snapenhance.core.bridge.BridgeClient
import me.rhunk.snapenhance.core.bridge.wrapper.LocaleWrapper
@@ -27,13 +30,11 @@ import me.rhunk.snapenhance.manager.impl.FeatureManager
import me.rhunk.snapenhance.nativelib.NativeConfig
import me.rhunk.snapenhance.nativelib.NativeLib
import me.rhunk.snapenhance.scripting.core.CoreScriptRuntime
-import java.util.concurrent.ExecutorService
-import java.util.concurrent.Executors
import kotlin.reflect.KClass
import kotlin.system.exitProcess
class ModContext {
- private val executorService: ExecutorService = Executors.newCachedThreadPool()
+ val coroutineScope = CoroutineScope(Dispatchers.IO)
lateinit var androidContext: Context
lateinit var bridgeClient: BridgeClient
@@ -73,13 +74,13 @@ class ModContext {
}
}
- fun executeAsync(runnable: ModContext.() -> Unit) {
- executorService.submit {
+ fun executeAsync(runnable: suspend ModContext.() -> Unit) {
+ coroutineScope.launch {
runCatching {
runnable()
}.onFailure {
longToast("Async task failed " + it.message)
- Logger.xposedLog("Async task failed", it)
+ log.error("Async task failed", it)
}
}
}
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt b/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt
@@ -3,6 +3,10 @@ package me.rhunk.snapenhance
import android.app.Activity
import android.app.Application
import android.content.Context
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
import me.rhunk.snapenhance.bridge.SyncCallback
import me.rhunk.snapenhance.core.Logger
import me.rhunk.snapenhance.core.bridge.BridgeClient
@@ -13,8 +17,7 @@ import me.rhunk.snapenhance.core.messaging.MessagingGroupInfo
import me.rhunk.snapenhance.data.SnapClassCache
import me.rhunk.snapenhance.hook.HookStage
import me.rhunk.snapenhance.hook.hook
-import kotlin.time.ExperimentalTime
-import kotlin.time.measureTime
+import kotlin.system.measureTimeMillis
class SnapEnhance {
@@ -55,7 +58,13 @@ class SnapEnhance {
return@connect
}
runCatching {
- init()
+ measureTimeMillis {
+ runBlocking {
+ init(this)
+ }
+ }.also {
+ appContext.log.verbose("init took ${it}ms")
+ }
}.onSuccess {
isBridgeInitialized = true
}.onFailure {
@@ -92,34 +101,28 @@ class SnapEnhance {
}
}
- @OptIn(ExperimentalTime::class)
- private fun init() {
- measureTime {
- with(appContext) {
- reloadConfig()
+ private fun init(scope: CoroutineScope) {
+ with(appContext) {
+ reloadConfig()
+ scope.launch(Dispatchers.IO) {
initNative()
- executeAsync {
- translation.userLocale = getConfigLocale()
- translation.loadFromBridge(bridgeClient)
- }
-
- mappings.loadFromBridge(bridgeClient)
- mappings.init(androidContext)
- eventDispatcher.init()
- //if mappings aren't loaded, we can't initialize features
- if (!mappings.isMappingsLoaded()) return
- features.init()
- syncRemote()
- scriptRuntime.connect(bridgeClient.getScriptingInterface())
+ translation.userLocale = getConfigLocale()
+ translation.loadFromBridge(bridgeClient)
}
- }.also { time ->
- appContext.log.verbose("init took $time")
+
+ mappings.loadFromBridge(bridgeClient)
+ mappings.init(androidContext)
+ eventDispatcher.init()
+ //if mappings aren't loaded, we can't initialize features
+ if (!mappings.isMappingsLoaded()) return
+ features.init()
+ scriptRuntime.connect(bridgeClient.getScriptingInterface())
+ syncRemote()
}
}
- @OptIn(ExperimentalTime::class)
private fun onActivityCreate() {
- measureTime {
+ measureTimeMillis {
with(appContext) {
features.onActivityCreate()
actionManager.init()
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/ExportChatMessages.kt b/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/ExportChatMessages.kt
@@ -5,7 +5,6 @@ import android.content.DialogInterface
import android.os.Environment
import android.text.InputType
import android.widget.EditText
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.joinAll
@@ -44,8 +43,6 @@ class ExportChatMessages : AbstractAction() {
context.feature(Messaging::class).conversationManager
}
- private val coroutineScope = CoroutineScope(Dispatchers.Default)
-
private val dialogLogs = mutableListOf<String>()
private var currentActionDialog: AlertDialog? = null
@@ -83,7 +80,7 @@ class ExportChatMessages : AbstractAction() {
}
private suspend fun askAmountOfMessages() = suspendCancellableCoroutine { cont ->
- coroutineScope.launch(Dispatchers.Main) {
+ context.coroutineScope.launch(Dispatchers.Main) {
val input = EditText(context.mainActivity)
input.inputType = InputType.TYPE_CLASS_NUMBER
input.setSingleLine()
@@ -132,7 +129,7 @@ class ExportChatMessages : AbstractAction() {
}
override fun run() {
- coroutineScope.launch(Dispatchers.Main) {
+ context.coroutineScope.launch(Dispatchers.Main) {
exportType = askExportType() ?: return@launch
mediaToDownload = if (exportType == ExportFormat.HTML) askMediaToDownload() else null
amountOfMessages = askAmountOfMessages()
@@ -289,7 +286,7 @@ class ExportChatMessages : AbstractAction() {
logDialog(conversationSize)
- coroutineScope.launch {
+ context.coroutineScope.launch {
conversations.forEach { conversation ->
launch {
runCatching {
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ScopeSync.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ScopeSync.kt
@@ -1,6 +1,8 @@
package me.rhunk.snapenhance.features.impl
-import kotlinx.coroutines.*
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
import me.rhunk.snapenhance.core.event.events.impl.SendMessageWithContentEvent
import me.rhunk.snapenhance.core.messaging.SocialScope
import me.rhunk.snapenhance.data.ContentType
@@ -13,7 +15,6 @@ class ScopeSync : Feature("Scope Sync", loadParams = FeatureLoadParams.INIT_SYNC
}
private val updateJobs = mutableMapOf<String, Job>()
- private val coroutineScope = CoroutineScope(Dispatchers.Main)
private fun sync(conversationId: String) {
context.database.getDMOtherParticipant(conversationId)?.also { participant ->
@@ -31,7 +32,7 @@ class ScopeSync : Feature("Scope Sync", loadParams = FeatureLoadParams.INIT_SYNC
event.destinations.conversations.map { it.toString() }.forEach { conversationId ->
updateJobs[conversationId]?.also { it.cancel() }
- updateJobs[conversationId] = (coroutineScope.launch {
+ updateJobs[conversationId] = (context.coroutineScope.launch {
delay(DELAY_BEFORE_SYNC)
sync(conversationId)
})
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/MessageLogger.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/MessageLogger.kt
@@ -18,8 +18,7 @@ import me.rhunk.snapenhance.features.FeatureLoadParams
import me.rhunk.snapenhance.ui.addForegroundDrawable
import me.rhunk.snapenhance.ui.removeForegroundDrawable
import java.util.concurrent.Executors
-import kotlin.time.ExperimentalTime
-import kotlin.time.measureTime
+import kotlin.system.measureTimeMillis
private fun Any.longHashCode(): Long {
var h = 1125899906842597L
@@ -91,17 +90,16 @@ class MessageLogger : Feature("MessageLogger",
return computeMessageIdentifier(conversationId, serverMessageId)
}
- @OptIn(ExperimentalTime::class)
override fun asyncOnActivityCreate() {
if (!isEnabled || !context.database.hasArroyo()) {
return
}
- measureTime {
+ measureTimeMillis {
val conversationIds = context.database.getFeedEntries(PREFETCH_FEED_COUNT).map { it.key!! }
- if (conversationIds.isEmpty()) return@measureTime
+ if (conversationIds.isEmpty()) return@measureTimeMillis
fetchedMessages.addAll(messageLoggerInterface.getLoggedIds(conversationIds.toTypedArray(), PREFETCH_MESSAGE_COUNT).toList())
- }.also { context.log.verbose("Loaded ${fetchedMessages.size} cached messages in $it") }
+ }.also { context.log.verbose("Loaded ${fetchedMessages.size} cached messages in ${it}ms") }
}
override fun init() {
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/FeatureManager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/FeatureManager.kt
@@ -1,5 +1,7 @@
package me.rhunk.snapenhance.manager.impl
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
import me.rhunk.snapenhance.ModContext
import me.rhunk.snapenhance.core.Logger
import me.rhunk.snapenhance.features.Feature
@@ -24,22 +26,31 @@ import me.rhunk.snapenhance.features.impl.ui.PinConversations
import me.rhunk.snapenhance.features.impl.ui.UITweaks
import me.rhunk.snapenhance.manager.Manager
import me.rhunk.snapenhance.ui.menu.impl.MenuViewInjector
-import java.util.concurrent.Executors
import kotlin.reflect.KClass
+import kotlin.system.measureTimeMillis
-class FeatureManager(private val context: ModContext) : Manager {
- private val asyncLoadExecutorService = Executors.newFixedThreadPool(5)
+class FeatureManager(
+ private val context: ModContext
+) : Manager {
private val features = mutableListOf<Feature>()
private fun register(vararg featureClasses: KClass<out Feature>) {
- featureClasses.forEach { clazz ->
- runCatching {
- clazz.constructors.first().call().also { feature ->
- feature.context = context
- features.add(feature)
+ runBlocking {
+ featureClasses.forEach { clazz ->
+ context.coroutineScope.launch {
+ runCatching {
+ clazz.java.constructors.first().newInstance()
+ .let { it as Feature }
+ .also {
+ it.context = context
+ synchronized(features) {
+ features.add(it)
+ }
+ }
+ }.onFailure {
+ Logger.xposedLog("Failed to register feature ${clazz.simpleName}", it)
+ }
}
- }.onFailure {
- Logger.xposedLog("Failed to register feature ${clazz.simpleName}", it)
}
}
}
@@ -95,34 +106,61 @@ class FeatureManager(private val context: ModContext) : Manager {
initializeFeatures()
}
- private fun featureInitializer(isAsync: Boolean, param: Int, action: (Feature) -> Unit) {
- features.filter { it.loadParams and param != 0 }.forEach { feature ->
- val callback = {
- runCatching {
- action(feature)
- }.onFailure {
- context.log.error("Failed to init feature ${feature.featureKey}", it)
- context.longToast("Failed to load feature ${feature.featureKey}! Check logcat for more details.")
- }
+ private fun initFeatures(
+ syncParam: Int,
+ asyncParam: Int,
+ syncAction: (Feature) -> Unit,
+ asyncAction: (Feature) -> Unit
+ ) {
+ fun tryInit(feature: Feature, block: () -> Unit) {
+ runCatching {
+ block()
+ }.onFailure {
+ context.log.error("Failed to init feature ${feature.featureKey}", it)
+ context.longToast("Failed to init feature ${feature.featureKey}! Check logcat for more details.")
}
- if (!isAsync) {
- callback()
- return@forEach
+ }
+
+ features.toList().forEach { feature ->
+ if (feature.loadParams and syncParam != 0) {
+ tryInit(feature) {
+ syncAction(feature)
+ }
}
- asyncLoadExecutorService.submit {
- callback()
+ if (feature.loadParams and asyncParam != 0) {
+ context.coroutineScope.launch {
+ tryInit(feature) {
+ asyncAction(feature)
+ }
+ }
}
}
}
private fun initializeFeatures() {
//TODO: async called when all features are initiated ?
- featureInitializer(false, FeatureLoadParams.INIT_SYNC) { it.init() }
- featureInitializer(true, FeatureLoadParams.INIT_ASYNC) { it.asyncInit() }
+ measureTimeMillis {
+ initFeatures(
+ FeatureLoadParams.INIT_SYNC,
+ FeatureLoadParams.INIT_ASYNC,
+ Feature::init,
+ Feature::asyncInit
+ )
+ }.also {
+ context.log.verbose("feature manager init took $it ms")
+ }
}
override fun onActivityCreate() {
- featureInitializer(false, FeatureLoadParams.ACTIVITY_CREATE_SYNC) { it.onActivityCreate() }
- featureInitializer(true, FeatureLoadParams.ACTIVITY_CREATE_ASYNC) { it.asyncOnActivityCreate() }
+ measureTimeMillis {
+ initFeatures(
+ FeatureLoadParams.ACTIVITY_CREATE_SYNC,
+ FeatureLoadParams.ACTIVITY_CREATE_ASYNC,
+ Feature::onActivityCreate,
+ Feature::asyncOnActivityCreate
+ )
+ }.also {
+ context.log.verbose("feature manager onActivityCreate took $it ms")
+ }
}
}
\ No newline at end of file