Flutter Android端出现PathNotFoundException:无法找到证书文件
问题原因
Flutter在iOS和Android平台对assets资源的处理逻辑不同:
- iOS会将assets映射为可直接通过
File访问的文件路径; - Android则将assets打包在APK内部,并非文件系统中的真实文件,无法用
dart:io的File类直接读取,这就是Android端报错PathNotFoundException的核心原因。
修复方案
需要改用Flutter官方提供的rootBundle加载assets资源,同时调整初始化逻辑适配异步加载(因为rootBundle的资源加载是异步操作)。
方案一:使用异步工厂构造函数
修改UserServiceHandler的实现,通过工厂方法异步加载证书并创建实例:
import 'dart:typed_data'; import 'package:flutter/services.dart'; import 'package:grpc/grpc.dart'; import 'package:injectable/injectable.dart'; import 'package:test/src/core/secure/secure_storage_service.dart'; import 'package:test/src/features/auth/data/interceptor/auth_interceptor.dart'; import '../../../../generated/user.pbgrpc.dart'; @Injectable(as: UserServiceClient) class UserServiceHandler extends UserServiceClient { UserServiceHandler._(ClientChannel channel, SecureStorageService storageService) : super( channel, interceptors: [AuthInterceptor(storageService)], options: const CallOptions(timeout: Duration(seconds: 30)), ); static Future<UserServiceHandler> create(SecureStorageService storageService) async { // 通过rootBundle加载证书文件 final ByteData certData = await rootBundle.load('assets/certs/ca_bundle.crt'); final List<int> certBytes = certData.buffer.asUint8List(); final channel = ClientChannel( _host, port: 50051, options: ChannelOptions( credentials: ChannelCredentials.secure(certificates: certBytes), ), ); return UserServiceHandler._(channel, storageService); } static String get _host => 'localhost'; }
之后在Injectable的依赖配置中,指定使用create方法创建该类的实例(可通过@factoryMethod注解或手动修改injectable.config.dart实现)。
方案二:App启动时提前加载证书
在App初始化阶段提前加载证书字节,再传入UserServiceHandler的构造函数:
// main.dart import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get_it/get_it.dart'; import 'package:test/src/core/secure/secure_storage_service.dart'; import 'package:test/src/features/auth/data/handler/user_service_handler.dart'; final getIt = GetIt.instance; void main() async { WidgetsFlutterBinding.ensureInitialized(); // 提前加载证书资源 final ByteData certData = await rootBundle.load('assets/certs/ca_bundle.crt'); final List<int> certBytes = certData.buffer.asUint8List(); // 注册依赖时传入证书字节 getIt.registerSingleton<UserServiceHandler>( UserServiceHandler(getIt<SecureStorageService>(), certBytes), ); runApp(const MyApp()); }
对应的UserServiceHandler构造函数修改:
@Injectable() class UserServiceHandler extends UserServiceClient { UserServiceHandler(SecureStorageService storageService, List<int> certBytes) : super( ClientChannel( _host, port: 50051, options: ChannelOptions( credentials: ChannelCredentials.secure(certificates: certBytes), ), ), interceptors: [AuthInterceptor(storageService)], options: const CallOptions(timeout: Duration(seconds: 30)), ); static String get _host => 'localhost'; }
额外检查项
- 确认
pubspec.yaml的assets配置无误:- assets/certs/会自动包含该目录下的所有文件,无需单独声明单个文件; - 检查文件名大小写:Android系统严格区分文件名大小写,确保代码中的路径
assets/certs/ca_bundle.crt与实际文件的大小写完全一致。
内容的提问来源于stack exchange,提问作者Адильжан Омаркан
相关产品推荐
相关产品推荐

