From 040e06cab677f2d1f0d91eb6a8be3f44c62d53a3 Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Fri, 16 Jan 2026 09:40:58 +0800 Subject: [PATCH] =?UTF-8?q?fix(android):=20=E6=94=B9=E8=BF=9B=E9=9F=B3?= =?UTF-8?q?=E9=A2=91=E6=96=87=E4=BB=B6=E5=88=86=E4=BA=AB=E5=92=8C=E6=B7=B1?= =?UTF-8?q?=E9=93=BE=E6=8E=A5=E5=A4=84=E7=90=86=E7=9A=84=E5=8F=AF=E9=9D=A0?= =?UTF-8?q?=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加重试机制和错误处理,确保音频文件和深链接在Flutter引擎未就绪时能够正确传递 修复文件处理中的潜在空指针异常,并优化文件复制逻辑 --- .../com/saitong/voitrans/MainActivity.kt | 218 +++++++++++++----- lib/main.dart | 7 + 2 files changed, 171 insertions(+), 54 deletions(-) diff --git a/android/app/src/main/kotlin/com/saitong/voitrans/MainActivity.kt b/android/app/src/main/kotlin/com/saitong/voitrans/MainActivity.kt index e11ffb7eb..acf5653b8 100644 --- a/android/app/src/main/kotlin/com/saitong/voitrans/MainActivity.kt +++ b/android/app/src/main/kotlin/com/saitong/voitrans/MainActivity.kt @@ -25,10 +25,15 @@ class MainActivity: FlutterActivity() { private var pendingDeepLink: Intent? = null private var pendingNavigationIntent: Intent? = null private var pendingShareIntent: Intent? = null + private var pendingSharedFilePath: String? = null + private var pendingDeepLinkUrl: String? = null private val EXTRA_NAVIGATE_TO = "com.saitong.voitrans.extra.NAVIGATE_TO" private val NAVIGATE_TO_MEETING_RECORD = "meetingRecord" private val ROUTE_MEETING_RECORD = "/meeting/record" + private val DEEPLINK_CHANNEL = "com.saitong.voitrans/deeplink" + private val DELIVERY_RETRY_DELAY_MS = 300L + private val MAX_DELIVERY_RETRIES = 12 override fun onCreate(savedInstanceState: Bundle?) { Log.d(TAG, "🚀 MainActivity onCreate 开始") @@ -78,8 +83,28 @@ class MainActivity: FlutterActivity() { Log.d(TAG, "New Intent data: ${intent.data}") Log.d(TAG, "New Intent extras: ${intent.extras}") - // 处理深链接 - handleNavigationIntent(intent) + intent.let { + if (it.action == Intent.ACTION_SEND) { + pendingShareIntent = it + Log.d(TAG, "📎 暂存分享文件意图,等待Flutter引擎准备完成") + } else if (it.action == Intent.ACTION_VIEW && it.data != null) { + val uri = it.data + if (uri != null && isLikelyAudio(it, uri)) { + pendingShareIntent = it + Log.d(TAG, "📎 暂存打开文件意图,等待Flutter引擎准备完成") + } else { + pendingDeepLink = it + Log.d(TAG, "🔗 暂存深链接,等待Flutter引擎准备完成") + } + } + } + + if (intent.hasExtra(EXTRA_NAVIGATE_TO)) { + pendingNavigationIntent = intent + Log.d(TAG, "🧭 暂存导航意图,等待Flutter引擎准备完成") + } + + deliverOrQueueIntent(intent) } override fun provideFlutterEngine(context: Context): FlutterEngine? { @@ -127,19 +152,19 @@ class MainActivity: FlutterActivity() { Handler(Looper.getMainLooper()).postDelayed({ pendingShareIntent?.let { intent -> Log.d(TAG, "📎 处理待处理的分享/打开文件意图") - handleNavigationIntent(intent) + deliverOrQueueIntent(intent) pendingShareIntent = null } pendingDeepLink?.let { intent -> Log.d(TAG, "🔗 处理待处理的深链接") - handleNavigationIntent(intent) + deliverOrQueueIntent(intent) pendingDeepLink = null } pendingNavigationIntent?.let { intent -> Log.d(TAG, "🧭 处理待处理的导航意图") - handleNavigationIntent(intent) + deliverOrQueueIntent(intent) pendingNavigationIntent = null } }, 1000) // 延迟1秒确保Flutter完全准备好 @@ -147,67 +172,143 @@ class MainActivity: FlutterActivity() { Log.d(TAG, "✅ configureFlutterEngine 完成") } - // 处理深链接的统一方法 - private fun handleNavigationIntent(intent: Intent?) { - intent?.let { - val navigateTo = it.getStringExtra(EXTRA_NAVIGATE_TO) - if (navigateTo == NAVIGATE_TO_MEETING_RECORD) { - pushRouteToFlutter(ROUTE_MEETING_RECORD) - return - } + private fun deliverOrQueueIntent(intent: Intent?) { + intent ?: return - if (it.action == Intent.ACTION_SEND || it.action == Intent.ACTION_VIEW) { - try { - val path = extractAudioPathFromIntent(it) - if (path != null) { - flutterEngine?.dartExecutor?.binaryMessenger?.let { messenger -> - val channel = MethodChannel(messenger, "com.saitong.voitrans/deeplink") - channel.invokeMethod("handleSharedFile", mapOf("filePath" to path)) - return - } - } - } catch (e: Exception) { - Log.e(TAG, "❌ 处理分享/打开文件失败", e) + val navigateTo = intent.getStringExtra(EXTRA_NAVIGATE_TO) + if (navigateTo == NAVIGATE_TO_MEETING_RECORD) { + pushRouteToFlutter(ROUTE_MEETING_RECORD) + return + } + + if (intent.action == Intent.ACTION_SEND || + intent.action == Intent.ACTION_SEND_MULTIPLE || + intent.action == Intent.ACTION_VIEW + ) { + try { + val path = extractAudioPathFromIntent(intent) + if (path != null) { + pendingSharedFilePath = path + deliverPendingSharedFile() + return } + } catch (e: Exception) { + Log.e(TAG, "❌ 处理分享/打开文件失败", e) } + } - if (it.action == Intent.ACTION_VIEW && it.data != null) { - val uri = it.data - Log.d(TAG, "🔗 处理深链接: $uri") - - try { - Log.d(TAG, "🔗 开始发送深链接到 Flutter") - - // 通过 MethodChannel 将深链接信息传递给 Flutter - flutterEngine?.dartExecutor?.binaryMessenger?.let { messenger -> - Log.d(TAG, "🔗 获取到 BinaryMessenger") - - val channel = MethodChannel( - messenger, - "com.saitong.voitrans/deeplink" + if (intent.action == Intent.ACTION_VIEW && intent.data != null) { + val uri = intent.data + Log.d(TAG, "🔗 处理深链接: $uri") + pendingDeepLinkUrl = uri.toString() + deliverPendingDeepLink() + return + } + + Log.d(TAG, "🔗 Intent 不是深链接或数据为空") + } + + private fun getEngineForMessaging(): FlutterEngine? { + return flutterEngine ?: FlutterEngineCache.getInstance().get("main_engine") + } + + private fun deliverPendingSharedFile(attempt: Int = 0) { + val filePath = pendingSharedFilePath ?: return + val engine = getEngineForMessaging() + val messenger = engine?.dartExecutor?.binaryMessenger + if (messenger == null) { + if (attempt < MAX_DELIVERY_RETRIES) { + Handler(Looper.getMainLooper()).postDelayed( + { deliverPendingSharedFile(attempt + 1) }, + DELIVERY_RETRY_DELAY_MS + ) + } else { + Log.w(TAG, "❌ FlutterEngine 未就绪,放弃发送分享文件:$filePath") + } + return + } + + val channel = MethodChannel(messenger, DEEPLINK_CHANNEL) + channel.invokeMethod( + "handleSharedFile", + mapOf("filePath" to filePath), + object : MethodChannel.Result { + override fun success(result: Any?) { + pendingSharedFilePath = null + } + + override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) { + Log.e(TAG, "❌ Flutter 处理分享文件返回错误:$errorCode, $errorMessage") + } + + override fun notImplemented() { + if (attempt < MAX_DELIVERY_RETRIES) { + Handler(Looper.getMainLooper()).postDelayed( + { deliverPendingSharedFile(attempt + 1) }, + DELIVERY_RETRY_DELAY_MS ) - - val arguments = buildUrlArguments(uri.toString()) - - Log.d(TAG, "🔗 准备发送参数: $arguments") - - channel.invokeMethod("handleDeepLink", arguments) - Log.d(TAG, "✅ 深链接信息已发送到 Flutter") - } ?: run { - Log.w(TAG, "❌ BinaryMessenger 为空,无法发送深链接") + } else { + Log.w(TAG, "❌ Flutter 未注册分享处理器,放弃发送分享文件:$filePath") } - } catch (e: Exception) { - Log.e(TAG, "❌ 处理深链接时出错", e) } + } + ) + } + + private fun deliverPendingDeepLink(attempt: Int = 0) { + val url = pendingDeepLinkUrl ?: return + val engine = getEngineForMessaging() + val messenger = engine?.dartExecutor?.binaryMessenger + if (messenger == null) { + if (attempt < MAX_DELIVERY_RETRIES) { + Handler(Looper.getMainLooper()).postDelayed( + { deliverPendingDeepLink(attempt + 1) }, + DELIVERY_RETRY_DELAY_MS + ) } else { - Log.d(TAG, "🔗 Intent 不是深链接或数据为空") + Log.w(TAG, "❌ FlutterEngine 未就绪,放弃发送深链接:$url") } + return } + + val channel = MethodChannel(messenger, DEEPLINK_CHANNEL) + val arguments = buildUrlArguments(url) + channel.invokeMethod( + "handleDeepLink", + arguments, + object : MethodChannel.Result { + override fun success(result: Any?) { + pendingDeepLinkUrl = null + } + + override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) { + Log.e(TAG, "❌ Flutter 处理深链接返回错误:$errorCode, $errorMessage") + } + + override fun notImplemented() { + if (attempt < MAX_DELIVERY_RETRIES) { + Handler(Looper.getMainLooper()).postDelayed( + { deliverPendingDeepLink(attempt + 1) }, + DELIVERY_RETRY_DELAY_MS + ) + } else { + Log.w(TAG, "❌ Flutter 未注册深链接处理器,放弃发送深链接:$url") + } + } + } + ) } private fun extractAudioPathFromIntent(intent: Intent): String? { val uri: Uri? = when (intent.action) { - Intent.ACTION_SEND -> intent.getParcelableExtra(Intent.EXTRA_STREAM) + Intent.ACTION_SEND -> { + val extraStream = intent.getParcelableExtra(Intent.EXTRA_STREAM) + extraStream ?: intent.clipData?.takeIf { it.itemCount > 0 }?.getItemAt(0)?.uri + } + Intent.ACTION_SEND_MULTIPLE -> { + val items = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM) + items?.firstOrNull() ?: intent.clipData?.takeIf { it.itemCount > 0 }?.getItemAt(0)?.uri + } Intent.ACTION_VIEW -> intent.data else -> null } @@ -246,17 +347,26 @@ class MainActivity: FlutterActivity() { val sharedDir = File(applicationContext.filesDir, "shared_audio") if (!sharedDir.exists()) sharedDir.mkdirs() val outFile = File(sharedDir, "audio_${System.currentTimeMillis()}.$ext") - resolver.openInputStream(uri)?.use { inStream -> + val inStream = resolver.openInputStream(uri) + if (inStream == null) { + outFile.delete() + return null + } + inStream.use { FileOutputStream(outFile).use { out -> val buffer = ByteArray(8192) var len: Int while (true) { - len = inStream.read(buffer) + len = it.read(buffer) if (len <= 0) break out.write(buffer, 0, len) } } } + if (!outFile.exists() || outFile.length() <= 0) { + outFile.delete() + return null + } outFile.absolutePath } catch (e: Exception) { Log.e(TAG, "复制分享文件到缓存失败: ${e.message}", e) diff --git a/lib/main.dart b/lib/main.dart index ac646c1fb..cf1d1b228 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -20,6 +20,7 @@ import 'core/themes/theme_service.dart'; import 'core/bindings/initial_binding.dart'; import 'data/services/login_service.dart'; import 'dart:ui' as ui; +import 'modules/meeting/controllers/meeting_home_controller.dart'; import 'modules/splash/controllers/splash_controller.dart'; bool _deepLinkHandlerInitialized = false; @@ -133,6 +134,12 @@ Future _handleSharedAudioFile(String filePath) async { const maxRetries = 10; while (retryCount < maxRetries) { try { + if (Get.isRegistered()) { + final controller = Get.find(); + controller.addAudio(filePath); + return; + } + Get.toNamed( Routes.meeting, arguments: {