6.7.0 - Alpha6 - 使用 LiveData 及 SharedFlow 替代已弃用的 LocalBroadcastManager

This commit is contained in:
SuperMonster003
2025-09-28 22:59:10 +08:00
parent c2bfb064d2
commit ac2b242be0
13 changed files with 357 additions and 180 deletions

View File

@@ -2,7 +2,6 @@ package org.autojs.autojs
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
@@ -11,7 +10,6 @@ import android.os.Build
import android.util.Log
import android.view.View
import android.widget.ImageView
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.multidex.MultiDexApplication
import com.bumptech.glide.Glide
import com.bumptech.glide.request.target.CustomViewTarget
@@ -21,6 +19,7 @@ import com.google.mlkit.common.sdkinternal.MlKitContext
import com.hjq.toast.Toaster
import com.tencent.bugly.Bugly
import com.tencent.bugly.crashreport.CrashReport
import io.reactivex.android.schedulers.AndroidSchedulers
import org.autojs.autojs.apkbuilder.ApkBuilder
import org.autojs.autojs.app.GlobalAppContext
import org.autojs.autojs.core.pref.Pref
@@ -28,6 +27,7 @@ import org.autojs.autojs.core.ui.inflater.ImageLoader
import org.autojs.autojs.core.ui.inflater.util.Drawables
import org.autojs.autojs.event.GlobalKeyObserver
import org.autojs.autojs.external.receiver.DynamicBroadcastReceivers
import org.autojs.autojs.ipc.InAppEventBus
import org.autojs.autojs.leakcanary.LeakCanarySetup
import org.autojs.autojs.pluginclient.DevPluginService
import org.autojs.autojs.runtime.api.augment.ocr.Ocr
@@ -86,7 +86,7 @@ class App : MultiDexApplication() {
if (!BuildConfig.DEBUG) {
@Suppress("SpellCheckingInspection")
FlurryAgent.Builder()
.withLogEnabled(BuildConfig.DEBUG)
.withLogEnabled(false)
.build(this, "D42MH48ZN4PJC5TKNYZD")
}
}
@@ -94,9 +94,9 @@ class App : MultiDexApplication() {
private fun setUpDebugEnvironment() {
Bugly.isDev = false
val crashHandler = CrashHandler(CrashReportActivity::class.java)
val strategy = CrashReport.UserStrategy(applicationContext)
strategy.setCrashHandleCallback(crashHandler)
val strategy = CrashReport.UserStrategy(applicationContext).apply {
crashHandleCallback = crashHandler
}
CrashReport.initCrashReport(applicationContext, BUGLY_APP_ID, false, strategy)
@@ -128,27 +128,44 @@ class App : MultiDexApplication() {
private fun initDynamicBroadcastReceivers() {
dynamicBroadcastReceivers = DynamicBroadcastReceivers(this)
val localActions = ArrayList<String>()
val actions = ArrayList<String>()
val systemActions = ArrayList<String>()
TimedTaskManager.allIntentTasks
.filter { task -> task.action != null }
.doOnComplete {
if (localActions.isNotEmpty()) {
dynamicBroadcastReceivers.register(localActions, true)
}
if (actions.isNotEmpty()) {
dynamicBroadcastReceivers.register(actions, false)
if (systemActions.isNotEmpty()) {
dynamicBroadcastReceivers.register(systemActions, false)
}
// @Archived by SuperMonster003 on Sep 27, 2025.
// ! LocalBroadcastManager is deprecated.
// ! zh-CN: LocalBroadcastManager 已被弃用.
// # Intent(DynamicBroadcastReceivers.ACTION_STARTUP).let {
// # LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(it)
// # }
// @Hint by JetBrains AI Assistant on Sep 27, 2025.
// ! Why schedule on next main-loop tick?
// ! Post the emit to the next frame on the main thread to avoid a race where
// ! the SharedFlow collectors (registered via launchIn on Main) are not yet active
// ! at the exact moment of emission. Emitting in the next loop ensures all
// ! subscriptions are set up, preventing event loss when replay=0.
// ! zh-CN:
// ! 为什么要在主线程的下一帧再触发?
// ! 将触发放到主线程消息队列的下一轮执行, 避免 "先发后订" 的竞态:
// ! 订阅 (launchIn(Main)) 尚未真正激活时如果立即发送, replay=0 会丢事件.
// ! 下一帧再发可以确保收集器已就绪, 从而可靠接收启动事件.
AndroidSchedulers.mainThread().scheduleDirect {
InAppEventBus.tryEmit(DynamicBroadcastReceivers.ACTION_STARTUP)
}
LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(
Intent(
DynamicBroadcastReceivers.ACTION_STARTUP
)
)
}
.subscribe({
if (it.isLocal) {
localActions.add(it.action)
} else {
actions.add(it.action)
systemActions.add(it.action)
}
}, { it.printStackTrace() })
@@ -235,6 +252,11 @@ class App : MultiDexApplication() {
super.onConfigurationChanged(newConfig)
}
fun clear() {
dynamicBroadcastReceivers.unregisterAll()
InAppEventBus.clear()
}
companion object {
private const val BUGLY_APP_ID = "19b3607b53"

View File

@@ -2,18 +2,21 @@ package org.autojs.autojs
import android.annotation.SuppressLint
import android.app.Application
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.util.Log
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.autojs.autojs.core.accessibility.AccessibilityTool
import org.autojs.autojs.core.accessibility.Capture
import org.autojs.autojs.core.accessibility.LayoutInspector.CaptureAvailableListener
import org.autojs.autojs.core.console.GlobalConsole
import org.autojs.autojs.execution.ScriptExecutionGlobalListener
import org.autojs.autojs.external.fileprovider.AppFileProvider
import org.autojs.autojs.ipc.LayoutInspectEvent
import org.autojs.autojs.ipc.LayoutInspectEventBus
import org.autojs.autojs.runtime.ScriptRuntime
import org.autojs.autojs.runtime.api.AppUtils
import org.autojs.autojs.runtime.api.AppUtils.Companion.ActivityShortForm
@@ -39,37 +42,52 @@ open class AutoJs(appContext: Application) : AbstractAutoJs(appContext) {
private val mA11yTool = AccessibilityTool(appContext)
private val mJob = Job()
private val mScope = CoroutineScope(Dispatchers.Main.immediate + mJob)
init {
scriptEngineService.registerGlobalScriptExecutionListener(ScriptExecutionGlobalListener())
LocalBroadcastManager.getInstance(appContext).registerReceiver(object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
try {
val action = intent.action ?: return
when {
action.equals(LayoutBoundsFloatyWindow::class.java.name, true) -> {
mA11yTool.ensureService()
capture(object : LayoutInspectFloatyWindow {
override fun create(capture: Capture) = LayoutBoundsFloatyWindow(capture, context, true)
})
}
action.equals(LayoutHierarchyFloatyWindow::class.java.name, true) -> {
mA11yTool.ensureService()
capture(object : LayoutInspectFloatyWindow {
override fun create(capture: Capture) = LayoutHierarchyFloatyWindow(capture, context, true)
})
}
// @Archived by SuperMonster003 on Sep 27, 2025.
// ! LocalBroadcastManager is deprecated.
// ! zh-CN: LocalBroadcastManager 已被弃用.
// # LocalBroadcastManager.getInstance(appContext).registerReceiver(object : BroadcastReceiver() {
// # override fun onReceive(context: Context, intent: Intent) {
// # ... ...
// # val action = intent.action ?: return
// # when {
// # action.equals(LayoutBoundsFloatyWindow::class.java.name, true) -> { ... ... }
// # action.equals(LayoutHierarchyFloatyWindow::class.java.name, true) -> { ... ... }
// # }
// # ... ...
// # }
// # }, IntentFilter().apply {
// # addAction(LayoutBoundsFloatyWindow::class.java.name)
// # addAction(LayoutHierarchyFloatyWindow::class.java.name)
// # })
LayoutInspectEventBus.events.onEach { event ->
try {
when (event) {
LayoutInspectEvent.ShowLayoutBounds -> {
mA11yTool.ensureService()
capture(object : LayoutInspectFloatyWindow {
override fun create(capture: Capture) = LayoutBoundsFloatyWindow(capture, appContext, true)
})
}
} catch (e: Exception) {
if (isBackgroundThread()) {
throw e
LayoutInspectEvent.ShowLayoutHierarchy -> {
mA11yTool.ensureService()
capture(object : LayoutInspectFloatyWindow {
override fun create(capture: Capture) = LayoutHierarchyFloatyWindow(capture, appContext, true)
})
}
}
} catch (e: Exception) {
if (isBackgroundThread()) {
throw e
}
}
}, IntentFilter().apply {
addAction(LayoutBoundsFloatyWindow::class.java.name)
addAction(LayoutHierarchyFloatyWindow::class.java.name)
})
}.launchIn(mScope)
}
private interface LayoutInspectFloatyWindow {
@@ -131,6 +149,10 @@ open class AutoJs(appContext: Application) : AbstractAutoJs(appContext) {
).forEach { putProperty(it.fullName, it.className) }
}
fun clear() {
mJob.cancel()
}
companion object {
private var isInitialized = false
private val TAG = AutoJs::class.java.simpleName

View File

@@ -36,7 +36,7 @@ public class BaseBroadcastReceiver extends BroadcastReceiver {
}
}
static void runTask(Context context, Intent intent, IntentTask task) {
public static void runTask(Context context, Intent intent, IntentTask task) {
Log.d(LOG_TAG, "runTask: action = " + intent.getAction() + ", script = " + task.getScriptPath());
ScriptFile file = new ScriptFile(task.getScriptPath());
ExecutionConfig config = new ExecutionConfig();

View File

@@ -1,151 +1,182 @@
package org.autojs.autojs.external.receiver;
package org.autojs.autojs.external.receiver
import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.IntentFilter;
import android.os.Build;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import android.util.Log;
import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import android.util.Log
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.autojs.autojs.ipc.InAppEventBus
import org.autojs.autojs.timing.IntentTask
import org.autojs.autojs.timing.TimedTaskManager
import org.autojs.autojs.timing.IntentTask;
class DynamicBroadcastReceivers(private val context: Context) {
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
private val mActions: MutableSet<String> = LinkedHashSet()
private val mReceiverRegistries: MutableList<ReceiverRegistry> = ArrayList<ReceiverRegistry>()
private val mDefaultActionReceiver = BaseBroadcastReceiver()
private val mPackageActionReceiver = BaseBroadcastReceiver()
public class DynamicBroadcastReceivers {
public static final String ACTION_STARTUP = "org.autojs.autojs.action.startup";
private static final String LOG_TAG = "DynBroadcastReceivers";
private final Set<String> mActions = new LinkedHashSet<>();
private final List<ReceiverRegistry> mReceiverRegistries = new ArrayList<>();
private final BaseBroadcastReceiver mDefaultActionReceiver = new BaseBroadcastReceiver();
private final BaseBroadcastReceiver mPackageActionReceiver = new BaseBroadcastReceiver();
private final Context mContext;
@SuppressLint("UnspecifiedRegisterReceiverFlag")
public DynamicBroadcastReceivers(Context context) {
mContext = context;
init {
@SuppressLint("UnspecifiedRegisterReceiverFlag")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
mContext.registerReceiver(mDefaultActionReceiver, createIntentFilter(StaticBroadcastReceiver.ACTIONS), Context.RECEIVER_EXPORTED);
context.registerReceiver(mDefaultActionReceiver, createIntentFilter(StaticBroadcastReceiver.ACTIONS), Context.RECEIVER_EXPORTED)
} else {
mContext.registerReceiver(mDefaultActionReceiver, createIntentFilter(StaticBroadcastReceiver.ACTIONS));
context.registerReceiver(mDefaultActionReceiver, createIntentFilter(StaticBroadcastReceiver.ACTIONS))
}
IntentFilter filter = createIntentFilter(StaticBroadcastReceiver.PACKAGE_ACTIONS);
filter.addDataScheme("package");
val filter: IntentFilter = createIntentFilter(StaticBroadcastReceiver.PACKAGE_ACTIONS)
filter.addDataScheme("package")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
mContext.registerReceiver(mPackageActionReceiver, filter, Context.RECEIVER_EXPORTED);
context.registerReceiver(mPackageActionReceiver, filter, Context.RECEIVER_EXPORTED)
} else {
mContext.registerReceiver(mPackageActionReceiver, filter);
context.registerReceiver(mPackageActionReceiver, filter)
}
}
}
public void registerIntent(IntentTask task) {
register(Collections.singletonList(task.getAction()), task.isLocal());
fun registerIntent(task: IntentTask) {
register(mutableListOf(task.action), task.isLocal)
}
public synchronized void register(List<String> actions, boolean local) {
LinkedHashSet<String> newActions = new LinkedHashSet<>();
for (String action : actions) {
if (!StaticBroadcastReceiver.ACTIONS.contains(action)
&& !StaticBroadcastReceiver.PACKAGE_ACTIONS.contains(action)
&& !mActions.contains(action)) {
newActions.add(action);
@Synchronized
fun register(actions: MutableList<String>, local: Boolean) {
val newActions = LinkedHashSet<String>()
for (action in actions) {
if (!StaticBroadcastReceiver.ACTIONS.contains(action) && !StaticBroadcastReceiver.PACKAGE_ACTIONS.contains(action) && !mActions.contains(action)
) {
newActions.add(action)
}
}
if (newActions.isEmpty()) {
return;
return
}
ReceiverRegistry receiverRegistry = new ReceiverRegistry(newActions, local);
receiverRegistry.register();
mReceiverRegistries.add(receiverRegistry);
val receiverRegistry = ReceiverRegistry(newActions, local)
receiverRegistry.register()
mReceiverRegistries.add(receiverRegistry)
mActions.addAll(newActions)
}
public synchronized void unregister(String action) {
@Synchronized
fun unregister(action: String?) {
if (!mActions.contains(action)) {
return;
return
}
mActions.remove(action);
Iterator<ReceiverRegistry> iterator = mReceiverRegistries.iterator();
mActions.remove(action)
val iterator = mReceiverRegistries.iterator()
while (iterator.hasNext()) {
ReceiverRegistry receiverRegistry = iterator.next();
val receiverRegistry = iterator.next()
if (!receiverRegistry.actions.contains(action)) {
continue;
continue
}
receiverRegistry.actions.remove(action);
receiverRegistry.unregister();
receiverRegistry.actions.remove(action)
receiverRegistry.unregister()
if (!receiverRegistry.register()) {
iterator.remove();
iterator.remove()
}
break;
break
}
}
public synchronized void unregisterAll() {
for (ReceiverRegistry registry : mReceiverRegistries) {
registry.unregister();
@Synchronized
fun unregisterAll() {
for (registry in mReceiverRegistries) {
registry.unregister()
}
mReceiverRegistries.clear();
mReceiverRegistries.clear()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mContext.unregisterReceiver(mDefaultActionReceiver);
mContext.unregisterReceiver(mPackageActionReceiver);
context.unregisterReceiver(mDefaultActionReceiver)
context.unregisterReceiver(mPackageActionReceiver)
}
}
static IntentFilter createIntentFilter(Collection<String> actions) {
IntentFilter filter = new IntentFilter();
for (String action : actions) {
filter.addAction(action);
}
return filter;
}
private inner class ReceiverRegistry(var actions: LinkedHashSet<String>, var local: Boolean) {
private class ReceiverRegistry {
BroadcastReceiver receiver;
LinkedHashSet<String> actions;
boolean local;
ReceiverRegistry(LinkedHashSet<String> actions, boolean local) {
this.actions = actions;
this.local = local;
receiver = new BaseBroadcastReceiver();
}
void unregister() {
if (local) {
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(mContext);
broadcastManager.unregisterReceiver(receiver);
} else {
mContext.unregisterReceiver(receiver);
}
}
var receiver: BroadcastReceiver = BaseBroadcastReceiver()
var jobs: List<Job> = emptyList()
@SuppressLint("UnspecifiedRegisterReceiverFlag")
boolean register() {
if (actions.isEmpty())
return false;
IntentFilter intentFilter = createIntentFilter(actions);
if (local) {
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(mContext);
broadcastManager.registerReceiver(receiver, intentFilter);
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
mContext.registerReceiver(receiver, intentFilter, Context.RECEIVER_EXPORTED);
} else {
mContext.registerReceiver(receiver, intentFilter);
}
fun register(): Boolean {
if (actions.isEmpty()) {
return false
}
if (local) {
// @Archived by SuperMonster003 on Sep 27, 2025.
// ! LocalBroadcastManager is deprecated.
// ! zh-CN: LocalBroadcastManager 已被弃用.
// # LocalBroadcastManager.getInstance(mContext).registerReceiver(receiver, intentFilter);
if (jobs.isNotEmpty()) {
jobs.forEach { it.cancel() }
}
jobs = actions.map { action ->
InAppEventBus.streamOf(action).onEach {
try {
TimedTaskManager.getIntentTaskOfAction(action)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ intentTask: IntentTask ->
BaseBroadcastReceiver.runTask(context, Intent(action), intentTask)
}, { obj: Throwable? ->
obj?.printStackTrace()
})
} catch (e: Exception) {
e.printStackTrace()
}
}.launchIn(appScope)
}
Log.d(LOG_TAG, "register (in-app): $actions")
} else {
val intentFilter: IntentFilter = createIntentFilter(actions)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
context.registerReceiver(receiver, intentFilter, Context.RECEIVER_EXPORTED)
} else {
context.registerReceiver(receiver, intentFilter)
}
Log.d(LOG_TAG, "register (system): $actions")
}
return true
}
fun unregister() {
if (local) {
// @Archived by SuperMonster003 on Sep 27, 2025.
// ! LocalBroadcastManager is deprecated.
// ! zh-CN: LocalBroadcastManager 已被弃用.
// # LocalBroadcastManager.getInstance(mContext).unregisterReceiver(receiver);
jobs.forEach { it.cancel() }
jobs = emptyList()
} else {
context.unregisterReceiver(receiver)
}
Log.d(LOG_TAG, "register: " + actions);
return true;
}
}
companion object {
private const val LOG_TAG = "DynBroadcastReceivers"
private val appScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
@JvmStatic
val ACTION_STARTUP = "org.autojs.autojs.action.startup"
private fun createIntentFilter(actions: MutableCollection<String>): IntentFilter {
return IntentFilter().apply {
actions.forEach { addAction(it) }
}
}
}
}

View File

@@ -0,0 +1,36 @@
package org.autojs.autojs.ipc
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
object InAppEventBus {
// Map<action, eventStream>
private val streams = mutableMapOf<String, MutableSharedFlow<Unit>>()
@JvmStatic
@Synchronized
fun streamOf(action: String): SharedFlow<Unit> {
return streams.getOrPut(action) {
MutableSharedFlow(
replay = 1,
extraBufferCapacity = 64,
onBufferOverflow = BufferOverflow.DROP_OLDEST,
)
}
}
@JvmStatic
@Synchronized
fun tryEmit(action: String): Boolean {
return streams[action]?.tryEmit(Unit) == true
}
@JvmStatic
@Synchronized
fun clear() {
streams.clear()
}
}

View File

@@ -0,0 +1,25 @@
package org.autojs.autojs.ipc
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
sealed interface LayoutInspectEvent {
object ShowLayoutBounds : LayoutInspectEvent
object ShowLayoutHierarchy : LayoutInspectEvent
}
object LayoutInspectEventBus {
// One-time event: replay=0, provide buffering
// and discard oldest on overflow to avoid emit suspension.
// zh-CN: 单次事件: replay=0, 提供一定缓冲并在溢出时丢弃最旧, 避免 emit 挂起.
private val _events = MutableSharedFlow<LayoutInspectEvent>(
replay = 0,
extraBufferCapacity = 64,
onBufferOverflow = BufferOverflow.DROP_OLDEST,
)
val events: SharedFlow<LayoutInspectEvent> get() = _events
suspend fun showLayoutBounds() = _events.emit(LayoutInspectEvent.ShowLayoutBounds)
suspend fun showLayoutHierarchy() = _events.emit(LayoutInspectEvent.ShowLayoutHierarchy)
}

View File

@@ -2,16 +2,26 @@ package org.autojs.autojs.runtime.api.augment.app
import android.content.Intent
import android.net.Uri
import android.os.Handler
import android.os.Looper
import android.provider.Settings
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.core.net.toUri
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.autojs.autojs.annotation.RhinoFunctionBody
import org.autojs.autojs.annotation.RhinoRuntimeFunctionInterface
import org.autojs.autojs.extension.AnyExtensions.isJsNullish
import org.autojs.autojs.extension.AnyExtensions.jsBrief
import org.autojs.autojs.extension.FlexibleArray
import org.autojs.autojs.extension.ScriptableExtensions.defineProp
import org.autojs.autojs.extension.ScriptableExtensions.deleteProp
import org.autojs.autojs.extension.ScriptableExtensions.prop
import org.autojs.autojs.extension.ScriptableObjectExtensions.inquire
import org.autojs.autojs.external.receiver.BaseBroadcastReceiver
import org.autojs.autojs.ipc.LayoutInspectEventBus
import org.autojs.autojs.runtime.ScriptRuntime
import org.autojs.autojs.runtime.api.AppUtils.Companion.ActivityShortForm
import org.autojs.autojs.runtime.api.AppUtils.Companion.BroadcastShortForm
@@ -20,6 +30,8 @@ import org.autojs.autojs.runtime.api.augment.Augmentable
import org.autojs.autojs.runtime.api.augment.shell.Shell
import org.autojs.autojs.runtime.exception.ShouldNeverHappenException
import org.autojs.autojs.runtime.exception.WrappedIllegalArgumentException
import org.autojs.autojs.timing.IntentTask
import org.autojs.autojs.timing.TimedTaskManager
import org.autojs.autojs.util.RhinoUtils
import org.autojs.autojs.util.RhinoUtils.UNDEFINED
import org.autojs.autojs.util.RhinoUtils.coerceBoolean
@@ -32,13 +44,10 @@ import org.autojs.autojs.util.RootUtils
import org.autojs.autojs6.BuildConfig
import org.mozilla.javascript.Context
import org.mozilla.javascript.NativeObject
import org.mozilla.javascript.ScriptableObject.PERMANENT
import org.mozilla.javascript.Undefined
import java.io.File
import java.net.URI
import org.autojs.autojs.util.App as PresetApp
import androidx.core.net.toUri
import org.autojs.autojs.extension.ScriptableExtensions.deleteProp
@Suppress("unused", "UNUSED_PARAMETER")
class App(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) {
@@ -423,15 +432,14 @@ class App(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) {
fun sendBroadcast(scriptRuntime: ScriptRuntime, args: Array<out Any?>): Undefined = ensureArgumentsOnlyOne(args) { o ->
when (o) {
is String -> {
runCatching {
sendLocalBroadcastSyncInternal(intentRhinoWithRuntime(scriptRuntime, newNativeObject().also {
val value = BroadcastShortForm.valueOf(o.uppercase()).className
it.defineProp("action", value, PERMANENT)
}))
}.getOrElse {
when (it) {
is WrappedIllegalArgumentException -> throw WrappedIllegalArgumentException("Broadcast short form $o cannot be found")
else -> throw it
CoroutineScope(Dispatchers.Main).launch {
when (BroadcastShortForm.valueOf(o.uppercase())) {
BroadcastShortForm.INSPECT_LAYOUT_BOUNDS -> LayoutInspectEventBus.showLayoutBounds()
BroadcastShortForm.LAYOUT_BOUNDS -> LayoutInspectEventBus.showLayoutBounds()
BroadcastShortForm.BOUNDS -> LayoutInspectEventBus.showLayoutBounds()
BroadcastShortForm.INSPECT_LAYOUT_HIERARCHY -> LayoutInspectEventBus.showLayoutHierarchy()
BroadcastShortForm.LAYOUT_HIERARCHY -> LayoutInspectEventBus.showLayoutHierarchy()
BroadcastShortForm.HIERARCHY -> LayoutInspectEventBus.showLayoutHierarchy()
}
}
}
@@ -459,7 +467,35 @@ class App(scriptRuntime: ScriptRuntime) : Augmentable(scriptRuntime) {
@JvmStatic
fun sendLocalBroadcastSyncInternal(intent: Intent?) {
intent?.let { LocalBroadcastManager.getInstance(globalContext).sendBroadcastSync(it) }
// @Archived by SuperMonster003 on Sep 27, 2025.
// ! LocalBroadcastManager is deprecated.
// ! zh-CN: LocalBroadcastManager 已被弃用.
// # intent?.let { LocalBroadcastManager.getInstance(globalContext).sendBroadcastSync(it) }
intent?.action?.let { action ->
try {
// Get tasks synchronously in background thread, then execute in main thread.
// zh-CN: 在后台线程同步获取任务, 再回到主线程执行.
val mainHandler = Handler(Looper.getMainLooper())
Schedulers.io().scheduleDirect {
try {
val task = TimedTaskManager.getIntentTaskOfAction(action)
.firstOrError()
.blockingGet()
task?.let {
mainHandler.post {
BaseBroadcastReceiver.runTask(globalContext, intent, it)
}
}
} catch (e: Throwable) {
e.printStackTrace()
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
@JvmStatic

View File

@@ -24,6 +24,7 @@ import com.google.android.material.tabs.TabLayout
import com.huaban.analysis.jieba.CharsDictionaryDatabase
import com.huaban.analysis.jieba.PhrasesDictionaryDatabase
import com.huaban.analysis.jieba.WordDictionaryDatabase
import org.autojs.autojs.App
import org.autojs.autojs.AutoJs
import org.autojs.autojs.app.FragmentPagerAdapterBuilder
import org.autojs.autojs.app.FragmentPagerAdapterBuilder.StoredFragmentPagerAdapter
@@ -318,6 +319,9 @@ class MainActivity : BaseActivity(), DelegateHost, HostActivity {
ScreenCapturerForegroundService.stopService()
AutoJs.instance.scriptEngineService.stopAll()
AutoJs.instance.clear()
App.app.clear()
WordDictionaryDatabase.getInstance(applicationContext).close()
CharsDictionaryDatabase.getInstance(applicationContext).close()

View File

@@ -420,7 +420,9 @@ class TimedTaskSettingActivity : BaseActivity() {
val task = IntentTask().apply {
action = actionString
scriptPath = mScriptFile.path
isLocal = actionString == DynamicBroadcastReceivers.ACTION_STARTUP
if (actionString == DynamicBroadcastReceivers.ACTION_STARTUP) {
isLocal = true
}
}
when (val intentTask = mIntentTask) {
null -> {