Просмотр исходного кода

[feat]键盘插件,处理app被强制停止后,再切换键盘,调用Flutter方法会失败的问题

hezihao 11 месяцев назад
Родитель
Сommit
c6cb57fd52

+ 11 - 1
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/component/child/impl/AiKeyboardProloguePanelComponent.kt

@@ -72,6 +72,11 @@ class AiKeyboardProloguePanelComponent @JvmOverloads constructor(
      */
     private var mCurrentClickItem: AiKeyboardKeyModel? = null
 
+    /**
+     * 是否第一次加载
+     */
+    private var mIsFirstLoad = true
+
     override fun onInflateViewId(): Int {
         return R.layout.component_ai_keyboard_prologue_panel
     }
@@ -279,7 +284,12 @@ class AiKeyboardProloguePanelComponent @JvmOverloads constructor(
                 vMagicIndicator.navigator.notifyDataSetChanged()
                 LogUtil.d("开场白列表更新,刷新Tab栏 => ${tabList.size}个Tab")
                 // 默认选中第一个Tab
-                handleTabClick(0)
+                if (tabList.isNotEmpty()) {
+                    if (mIsFirstLoad) {
+                        handleTabClick(0)
+                        mIsFirstLoad = false
+                    }
+                }
             }
             // 监听Tab切换,更新键盘列表
             getKeyboardViewModel().currentPrologueTab.observe(getLifecycleOwner()) { newTab ->

+ 11 - 1
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/keyboard/CustomKeyboardService.kt

@@ -44,8 +44,18 @@ class CustomKeyboardService : InputMethodLifecycleService(), ICustomKeyboardServ
 
     override fun onStartInputView(info: EditorInfo?, restarting: Boolean) {
         super.onStartInputView(info, restarting)
-        // 键盘弹起
         LogUtil.d("CustomKeyboardService => onStartInputView()")
+        // 键盘弹起,刷新数据
+//        if (PluginInitializer.isPluginInitComplete()) {
+//            mKeyboardViewModel.refreshData()
+//        } else {
+//            PluginInitializer.addPluginInitListener(object : PluginInitializer.PluginInitListener {
+//                override fun onPluginInitComplete() {
+//                    mKeyboardViewModel.refreshData()
+//                    PluginInitializer.removePluginInitListener(this)
+//                }
+//            })
+//        }
     }
 
     override fun onFinishInputView(finishingInput: Boolean) {

+ 10 - 0
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/mvvm/viewmodel/KeyboardViewModel.kt

@@ -206,6 +206,16 @@ class KeyboardViewModel : BaseViewModel() {
     // ----------------------------------- 获取业务数据方法 -----------------------------------
 
     /**
+     * 刷新数据
+     */
+    fun refreshData() {
+        getCurrentKeyboardInfo()
+        getKeyboardList()
+        getCharacterList()
+        getPrologueList()
+    }
+
+    /**
      * 用户是否已登录
      */
     fun isLogin(

+ 38 - 0
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/util/bridge/channel/ProxyMethodChannel.kt

@@ -0,0 +1,38 @@
+package com.atmob.keyboard_android.util.bridge.channel
+
+
+import com.atmob.keyboard_android.util.bridge.util.PluginInitializer
+import io.flutter.plugin.common.BinaryMessenger
+import io.flutter.plugin.common.MethodChannel
+import java.util.concurrent.CopyOnWriteArrayList
+
+/**
+ * 代理MethodChannel,处理Flutter端方法还未初始化时,将调用任务延迟到初始化完成后再执行
+ */
+class ProxyMethodChannel(messenger: BinaryMessenger, name: String) :
+    MethodChannel(messenger, name) {
+    private val mPendingTaskQueue = CopyOnWriteArrayList<Runnable>()
+
+    init {
+        // 添加初始化监听
+        PluginInitializer.addPluginInitListener(object : PluginInitializer.PluginInitListener {
+            override fun onPluginInitComplete() {
+                // 初始化完毕,执行待执行的任务
+                for (task in mPendingTaskQueue) {
+                    task.run()
+                }
+                mPendingTaskQueue.clear()
+            }
+        })
+    }
+
+    override fun invokeMethod(method: String, arguments: Any?, callback: Result?) {
+        if (PluginInitializer.isPluginInitComplete()) {
+            super.invokeMethod(method, arguments, callback)
+        } else {
+            mPendingTaskQueue.add(Runnable {
+                super.invokeMethod(method, arguments, callback)
+            })
+        }
+    }
+}

+ 4 - 7
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/util/bridge/method/KeyboardExposeNativeMethodHandler.kt

@@ -13,6 +13,7 @@ import com.atmob.keyboard_android.util.bridge.callback.NativeMethodHandler
 import com.atmob.keyboard_android.util.bridge.callback.ResultCallback
 import com.atmob.keyboard_android.util.bridge.enums.ExposeNativeMethod
 import com.atmob.keyboard_android.util.bridge.model.resp.KeyboardInfo
+import com.atmob.keyboard_android.util.bridge.util.PluginInitializer
 import com.atmob.keyboard_android.util.flow.FloatingWindowUtil
 
 /**
@@ -52,6 +53,8 @@ class KeyboardExposeNativeMethodHandler : NativeMethodHandler {
                 // 注册键盘切换监听
                 FlutterBridgeManager.registerDefaultKeyboardChangeEvent()
                 resultCallback.onSuccess(null)
+                // 通知初始化完毕
+                PluginInitializer.notifyInitComplete()
             }
 
             // 开启或关闭悬浮窗
@@ -118,13 +121,7 @@ class KeyboardExposeNativeMethodHandler : NativeMethodHandler {
 
             // 登录成功\退出登录\注销账号,刷新用户数据
             ExposeNativeMethod.REFRESH_DATA.methodName -> {
-                KeyboardHolder.getKeyboardService()?.getKeyboardViewModel()?.run {
-                    // 刷新数据
-                    getCurrentKeyboardInfo()
-                    getKeyboardList()
-                    getCharacterList()
-                    getPrologueList()
-                }
+                KeyboardHolder.getKeyboardService()?.getKeyboardViewModel()?.refreshData()
                 resultCallback.onSuccess(null)
             }
 

+ 26 - 9
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/util/bridge/util/FlutterMethodCaller.kt

@@ -5,18 +5,24 @@ import android.os.Looper
 import com.atmob.keyboard_android.constant.PluginConfig
 import com.atmob.keyboard_android.util.JsonUtil
 import com.atmob.keyboard_android.util.LogUtil
+import com.atmob.keyboard_android.util.bridge.channel.ProxyMethodChannel
 import com.atmob.keyboard_android.util.bridge.model.base.CallResult
 import io.flutter.embedding.engine.FlutterEngine
 import io.flutter.plugin.common.MethodChannel
+import java.util.concurrent.atomic.AtomicInteger
 
 /**
  * Flutter方法调用者
  */
 class FlutterMethodCaller {
+    companion object {
+        private const val MAX_RETRY_COUNT = 3
+    }
+
     /**
      * 用于与 Flutter 端通信的 MethodChannel
      */
-    private lateinit var mMethodChannel: MethodChannel
+    private lateinit var mMethodChannel: ProxyMethodChannel
 
     /**
      * 主线程Handler,用于Flutter回调安卓端时,在主线程回调外部
@@ -27,7 +33,7 @@ class FlutterMethodCaller {
      * 初始化
      */
     fun init(engine: FlutterEngine) {
-        mMethodChannel = MethodChannel(
+        mMethodChannel = ProxyMethodChannel(
             engine.dartExecutor.binaryMessenger,
             PluginConfig.FLUTTER_METHOD_CHANNEL_NAME
         )
@@ -50,7 +56,8 @@ class FlutterMethodCaller {
         onSuccess: ((resultObj: T) -> Unit)? = null,
         onFail: ((errorCode: Int, errorMsg: String) -> Unit)? = null
     ) {
-        mMethodChannel.invokeMethod(methodName, args, object : MethodChannel.Result {
+        val retryCount = AtomicInteger(0)
+        val result = object : MethodChannel.Result {
             override fun success(result: Any?) {
                 if (isReturnJson) {
                     val resultJson = result.toString()
@@ -96,25 +103,35 @@ class FlutterMethodCaller {
 
             override fun notImplemented() {
                 LogUtil.d("方法名:${methodName},调用失败,Flutter未实现该方法")
-                runOnUIThread {
-                    onFail?.invoke(-1, "method not implemented")
+                val currentRetryCount = retryCount.incrementAndGet()
+                // 到达重试最大次数,回调结果
+                if (currentRetryCount >= MAX_RETRY_COUNT) {
+                    runOnUIThread {
+                        onFail?.invoke(-100, "")
+                    }
+                } else {
+                    // 重试
+                    runOnUIThread(300, callback = {
+                        mMethodChannel.invokeMethod(methodName, args, this)
+                    })
                 }
             }
-        })
+        }
+        mMethodChannel.invokeMethod(methodName, args, result)
     }
 
     /**
      * 在主线程运行回调
      */
-    private fun runOnUIThread(callback: () -> Unit) {
+    private fun runOnUIThread(delayTime: Long = 0, callback: () -> Unit) {
         // 如果已经在主线程了,直接回调
         if (Looper.getMainLooper().thread == Thread.currentThread()) {
             callback.invoke()
         } else {
             // 不在主线程,则切到主线程后,再回调
-            mMainHandler.post {
+            mMainHandler.postDelayed({
                 callback.invoke()
-            }
+            }, delayTime)
         }
     }
 }

+ 63 - 0
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/util/bridge/util/PluginInitializer.kt

@@ -0,0 +1,63 @@
+package com.atmob.keyboard_android.util.bridge.util
+
+import java.util.concurrent.CopyOnWriteArrayList
+
+/**
+ * 插件初始化器
+ */
+object PluginInitializer {
+    /**
+     * 是否初始化完毕
+     */
+    @Volatile
+    private var isPluginInitComplete: Boolean = false
+
+    /**
+     * 监听器
+     */
+    private val mPluginInitListenerList = CopyOnWriteArrayList<PluginInitListener>()
+
+    /**
+     * 插件初始化完毕回调
+     */
+    interface PluginInitListener {
+        fun onPluginInitComplete()
+    }
+
+    /**
+     * 添加监听
+     */
+    fun addPluginInitListener(listener: PluginInitListener) {
+        // 已经初始化完毕,直接回调
+        if (isPluginInitComplete) {
+            listener.onPluginInitComplete()
+        } else {
+            // 未初始化,添加到监听器
+            mPluginInitListenerList.add(listener)
+        }
+    }
+
+    /**
+     * 移除监听
+     */
+    fun removePluginInitListener(listener: PluginInitListener) {
+        mPluginInitListenerList.remove(listener)
+    }
+
+    /**
+     * 通知初始化完毕
+     */
+    fun notifyInitComplete() {
+        isPluginInitComplete = true
+        for (listener in mPluginInitListenerList) {
+            listener.onPluginInitComplete()
+        }
+    }
+
+    /**
+     * 是否初始化完成
+     */
+    fun isPluginInitComplete(): Boolean {
+        return isPluginInitComplete
+    }
+}