Flutter集成Agora SDK(v6.3.0)遇动态库加载及空检查异常
问题描述
使用agora_rtc_engine: ^6.3.0开发Flutter实时音视频功能时,出现两类错误:
1. 动态库加载失败
Invalid argument(s): Failed to load dynamic library 'libiris_method_channel.so': dlopen failed: library "libiris_method_channel.so" not found
E/flutter ( 3573): #0 _open (dart:ffi-patch/ffi_dynamic_library_patch.dart:11:43)
E/flutter ( 3573): #1 new DynamicLibrary.open (dart:ffi-patch/ffi_dynamic_library_patch.dart:22:12)
E/flutter ( 3573): #2 _loadLib (package:iris_method_channel/src/platform/io/iris_event_io.dart:16:31)
E/flutter ( 3573): #3 new IrisEventIO (package:iris_method_channel/src/platform/io/iris_event_io.dart:26:54)
E/flutter ( 3573): #4 IrisMethodChannelInternalIO._execute (package:iris_method_channel/src/platform/io/iris_method_channel_internal_io.dart:421:64)
E/flutter ( 3573): #5 _delayEntrypointInvocation.(dart:isolate-patch/isolate_patch.dart:300:17)
E/flutter ( 3573): #6 _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
2. 未处理空检查异常
E/flutter ( 3573): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Null check operator used on a null value
E/flutter ( 3573): #0 _HotRestartFinalizer._finalize (package:iris_method_channel/src/platform/io/iris_method_channel_internal_io.dart:155:74)
E/flutter ( 3573): #1 _RootZone.runUnaryGuarded (dart:async/zone.dart:1594:10)
E/flutter ( 3573): #2 _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:339:11)
E/flutter ( 3573): #3 _BufferingStreamSubscription._add (dart:async/stream_impl.dart:271:7)
E/flutter ( 3573): #4 _SyncStreamControllerDispatch._sendData (dart:async/stream_controller.dart:784:19)
E/flutter ( 3573): #5 _StreamController._add (dart:async/stream_controller.dart:658:7)
E/flutter ( 3573): #6 _StreamController.add (dart:async/stream_controller.dart:606:5)
E/flutter ( 3573): #7 _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
相关实现代码:
import 'dart:convert'; import 'package:flutter/services.dart'; import 'package:agora_rtc_engine/agora_rtc_engine.dart'; import 'package:permission_handler/permission_handler.dart'; import 'dart:async'; import 'package:flutter/material.dart'; class newww extends StatefulWidget { const newww({Key? key}) : super(key: key); @override State<newww> createState() => _newwwState(); } class _newwwState extends State<newww> { int? _remoteUid; bool _localUserJoined = false; late RtcEngine _engine; @override void initState() { super.initState(); initAgora(); } Future<void> initAgora() async { // retrieve permissions await [Permission.microphone, Permission.camera].request(); //create the engine _engine = createAgoraRtcEngine(); await _engine.initialize(const RtcEngineContext( appId: "a28be509a7404ebc9f42459393ba1cfa", channelProfile: ChannelProfileType.channelProfileLiveBroadcasting, )); _engine.registerEventHandler( RtcEngineEventHandler( onJoinChannelSuccess: (RtcConnection connection, int elapsed) { debugPrint("local user ${connection.localUid} joined"); setState(() { _localUserJoined = true; }); }, onUserJoined: (RtcConnection connection, int remoteUid, int elapsed) { debugPrint("remote user $remoteUid joined"); setState(() { _remoteUid = remoteUid; }); }, onUserOffline: (RtcConnection connection, int remoteUid, UserOfflineReasonType reason) { debugPrint("remote user $remoteUid left channel"); setState(() { _remoteUid = null; }); }, onTokenPrivilegeWillExpire: (RtcConnection connection, String token) { debugPrint( '[onTokenPrivilegeWillExpire] connection: ${connection.toJson()}, token: $token'); }, ), ); await _engine.setClientRole(role: ClientRoleType.clientRoleBroadcaster); await _engine.enableVideo(); await _engine.startPreview(); await _engine.joinChannel( token: "007eJxTYDBL3aC4U2Hicl9Vsc4Eo0bx4z8K4u59u6EaeHiuVIObhK8CQ6KRRVKqqYFlormJgUlqUrJlmomRiamlsaVxUqJhclri6dXaaQ2BjAxJsreYGBkgEMRnYShJLS5hYAAAzAceCQ==", channelId: "test", uid: 0, options: const ChannelMediaOptions(), ); } @override void dispose() { super.dispose(); _dispose(); } Future<void> _dispose() async { await _engine.leaveChannel(); await _engine.release(); } // Create UI with local view and remote view @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Agora Video Call'), ), body: Stack( children: [ Center( child: _remoteVideo(), ), Align( alignment: Alignment.topLeft, child: SizedBox( width: 100, height: 150, child: Center( child: _localUserJoined ? AgoraVideoView( controller: VideoViewController( rtcEngine: _engine, canvas: const VideoCanvas(uid: 0), ), ) : const CircularProgressIndicator(), ), ), ), ], ), ); } // Display remote user's video Widget _remoteVideo() { if (_remoteUid != null) { return AgoraVideoView( controller: VideoViewController.remote( rtcEngine: _engine, canvas: VideoCanvas(uid: _remoteUid), connection: const RtcConnection(channelId: "test"), ), ); } else { return const Text( 'Please wait for remote user to join', textAlign: TextAlign.center, ); } } }
解决方案
针对动态库加载失败
- 清理重建项目:执行
flutter clean清除缓存,再运行flutter pub get拉取依赖,最后重新编译。Android端可手动删除android/app/build目录后再构建。 - 配置支持的ABI架构:在
android/app/build.gradle的defaultConfig中添加NDK配置,确保包含Agora支持的架构:android { defaultConfig { ndk { abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86_64' } } } - 调整依赖版本:尝试将
agora_rtc_engine切换到6.2.0或最新稳定版,部分版本存在动态库打包疏漏问题。
针对空检查异常
- 优化引擎释放逻辑:在
_dispose方法中增加空值判断并捕获异常,避免操作未初始化的引擎:Future<void> _dispose() async { try { await _engine.leaveChannel(); await _engine.release(); } catch (e) { debugPrint('Engine dispose error: $e'); } } - 避免热重启触发异常:该问题常出现在热重启后,测试时尽量使用冷启动;或在
initState中确保每次初始化都是全新的引擎实例。 - 延迟执行释放操作:在
dispose中通过WidgetsBinding延迟执行清理,确保UI框架完成处理后再释放引擎:@override void dispose() { super.dispose(); WidgetsBinding.instance.addPostFrameCallback((_) { _dispose(); }); }
内容的提问来源于stack exchange,提问作者Amna Shaikh

