diff --git a/docs/guide/special-feature.md b/docs/guide/special-feature.md index 7c6ffe7c..5f1319cd 100644 --- a/docs/guide/special-feature.md +++ b/docs/guide/special-feature.md @@ -1135,6 +1135,48 @@ dataChannel.checkingVersionEquals { isEquals -> 详情请参考 [YukiHookDataChannel](api/document?id=yukihookdatachannel-class)。 +### 重复创建回调事件的规则 + +!> 在模块和宿主中,每一个 `dataChannel` 对应的 `key` 的回调事件**都不允许重复创建**,若重复,之前的回调事件会被新增加的回调事件替换,若在模块中使用,在同一个 `Activity` 中不可以重复,不同的 `Activity` 中相同的 `key` 允许重复。 + +这里只列出了在模块中使用的例子,在宿主中相同的 `key` 始终不允许重复创建。 + +> 示例如下 + +```kotlin +class MainActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + // 回调事件 A + dataChannel(packageName = "com.example.demo").wait(key = "test_key") { + // Your code here. + } + // 回调事件 B + dataChannel(packageName = "com.example.demo").wait(key = "test_key") { + // Your code here. + } + // 回调事件 C + dataChannel(packageName = "com.example.demo").wait(key = "other_test_key") { + // Your code here. + } + } +} + +class OtherActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + // 回调事件 D + dataChannel(packageName = "com.example.demo").wait(key = "test_key") { + // Your code here. + } + } +} +``` + +在上述示例中,回调事件 A 会被回调事件 B 替换掉,回调事件 C 的 `key` 不与其它重复,回调事件 D 在另一个 Activity 中,所以最终回调事件 B、C、D 都可被创建成功。 + ## 宿主生命周期扩展功能 > 这是一个自动 Hook 宿主 APP 生命周期的扩展功能。 diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/channel/YukiHookDataChannel.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/channel/YukiHookDataChannel.kt index 5eddeef0..c4d887da 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/channel/YukiHookDataChannel.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/channel/YukiHookDataChannel.kt @@ -29,6 +29,7 @@ package com.highcapable.yukihookapi.hook.xposed.channel +import android.app.Activity import android.app.Application import android.content.BroadcastReceiver import android.content.Context @@ -38,7 +39,6 @@ import android.os.Bundle import android.os.Parcelable import com.highcapable.yukihookapi.YukiHookAPI import com.highcapable.yukihookapi.hook.log.yLoggerW -import com.highcapable.yukihookapi.hook.utils.putIfAbsentCompat import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication import com.highcapable.yukihookapi.hook.xposed.bridge.YukiHookBridge import com.highcapable.yukihookapi.hook.xposed.channel.data.ChannelData @@ -83,7 +83,7 @@ class YukiHookDataChannel private constructor() { } /** 注册广播回调数组 */ - private var receiverCallbacks = HashMap Unit)>() + private var receiverCallbacks = HashMap Unit>>() /** 当前注册广播的 [Context] */ private var receiverContext: Context? = null @@ -93,7 +93,17 @@ class YukiHookDataChannel private constructor() { object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { if (intent == null) return - intent.action?.also { action -> receiverCallbacks.takeIf { it.isNotEmpty() }?.forEach { (_, it) -> it(action, intent) } } + intent.action?.also { action -> + receiverCallbacks.takeIf { it.isNotEmpty() }?.apply { + val destroyedCallbacks = arrayListOf() + forEach { (key, it) -> + if (it.first is Activity && (it.first as? Activity?)?.isDestroyed == true) + destroyedCallbacks.add(key) + else it.second(action, intent) + } + destroyedCallbacks.takeIf { it.isNotEmpty() }?.forEach { remove(it) } + } + } } } } @@ -201,7 +211,7 @@ class YukiHookDataChannel private constructor() { * @param result 回调结果数据 */ fun wait(key: String, value: T? = null, result: (value: T) -> Unit) { - receiverCallbacks.putIfAbsentCompat(key) { action, intent -> + receiverCallbacks[key + "_single_" + context?.javaClass?.name] = Pair(context) { action, intent -> if (action == if (isXposedEnvironment) hostActionName(packageName) else moduleActionName(context)) (intent.extras?.get(key) as? T?).also { if (it != null || value != null) (it ?: value)?.let { e -> result(e) } } } @@ -214,7 +224,7 @@ class YukiHookDataChannel private constructor() { * @param result 回调结果数据 */ fun wait(data: ChannelData, value: T? = data.value, result: (value: T) -> Unit) { - receiverCallbacks.putIfAbsentCompat(data.key) { action, intent -> + receiverCallbacks[data.key + "_cdata_" + context?.javaClass?.name] = Pair(context) { action, intent -> if (action == if (isXposedEnvironment) hostActionName(packageName) else moduleActionName(context)) (intent.extras?.get(data.key) as? T?).also { if (it != null || value != null) (it ?: value)?.let { e -> result(e) } } } @@ -228,7 +238,7 @@ class YukiHookDataChannel private constructor() { * @param result 回调结果 */ fun wait(key: String, result: () -> Unit) { - receiverCallbacks.putIfAbsentCompat(key) { action, intent -> + receiverCallbacks[key + "_vwfl_" + context?.javaClass?.name] = Pair(context) { action, intent -> if (action == if (isXposedEnvironment) hostActionName(packageName) else moduleActionName(context)) if (intent.getStringExtra(key) == VALUE_WAIT_FOR_LISTENER) result() }