refactor: init optimization

This commit is contained in:
rhunk
2023-10-01 12:29:02 +02:00
parent 5fd6aec256
commit c06678d72a
7 changed files with 115 additions and 76 deletions

View File

@@ -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")

View File

@@ -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)
}
}
}

View File

@@ -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()

View File

@@ -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 {

View File

@@ -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)
})

View File

@@ -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() {

View File

@@ -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.")
}
}
features.toList().forEach { feature ->
if (feature.loadParams and syncParam != 0) {
tryInit(feature) {
syncAction(feature)
}
}
if (!isAsync) {
callback()
return@forEach
}
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")
}
}
}