Browse Source

fix(android): 改进音频文件分享和深链接处理的可靠性

添加重试机制和错误处理,确保音频文件和深链接在Flutter引擎未就绪时能够正确传递
修复文件处理中的潜在空指针异常,并优化文件复制逻辑
newdev_shunjiawei
lxm 9 months ago
parent
commit
040e06cab6
  1. 208
      android/app/src/main/kotlin/com/saitong/voitrans/MainActivity.kt
  2. 7
      lib/main.dart

208
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")
if (intent.action == Intent.ACTION_VIEW && intent.data != null) {
val uri = intent.data
Log.d(TAG, "🔗 处理深链接: $uri")
pendingDeepLinkUrl = uri.toString()
deliverPendingDeepLink()
return
}
try {
Log.d(TAG, "🔗 开始发送深链接到 Flutter")
Log.d(TAG, "🔗 Intent 不是深链接或数据为空")
}
// 通过 MethodChannel 将深链接信息传递给 Flutter
flutterEngine?.dartExecutor?.binaryMessenger?.let { messenger ->
Log.d(TAG, "🔗 获取到 BinaryMessenger")
private fun getEngineForMessaging(): FlutterEngine? {
return flutterEngine ?: FlutterEngineCache.getInstance().get("main_engine")
}
val channel = MethodChannel(
messenger,
"com.saitong.voitrans/deeplink"
)
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 arguments = buildUrlArguments(uri.toString())
val channel = MethodChannel(messenger, DEEPLINK_CHANNEL)
channel.invokeMethod(
"handleSharedFile",
mapOf("filePath" to filePath),
object : MethodChannel.Result {
override fun success(result: Any?) {
pendingSharedFilePath = null
}
Log.d(TAG, "🔗 准备发送参数: $arguments")
override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) {
Log.e(TAG, "❌ Flutter 处理分享文件返回错误:$errorCode, $errorMessage")
}
channel.invokeMethod("handleDeepLink", arguments)
Log.d(TAG, "✅ 深链接信息已发送到 Flutter")
} ?: run {
Log.w(TAG, "❌ BinaryMessenger 为空,无法发送深链接")
override fun notImplemented() {
if (attempt < MAX_DELIVERY_RETRIES) {
Handler(Looper.getMainLooper()).postDelayed(
{ deliverPendingSharedFile(attempt + 1) },
DELIVERY_RETRY_DELAY_MS
)
} 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<Uri>(Intent.EXTRA_STREAM)
extraStream ?: intent.clipData?.takeIf { it.itemCount > 0 }?.getItemAt(0)?.uri
}
Intent.ACTION_SEND_MULTIPLE -> {
val items = intent.getParcelableArrayListExtra<Uri>(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)

7
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<void> _handleSharedAudioFile(String filePath) async {
const maxRetries = 10;
while (retryCount < maxRetries) {
try {
if (Get.isRegistered<MeetingHomeController>()) {
final controller = Get.find<MeetingHomeController>();
controller.addAudio(filePath);
return;
}
Get.toNamed(
Routes.meeting,
arguments: {

Loading…
Cancel
Save