.NET MAUI Blazor Hybrid安卓Release构建登录异常排查求助
.NET MAUI Blazor Hybrid Android Release构建登录未捕获异常问题
在.NET MAUI Blazor Hybrid应用中,点击登录按钮调用TryToLogin方法时出现未捕获错误,即使添加了try-catch块也未触发捕获。此问题仅出现在Android Release构建中,Android Debug构建一切正常。
相关代码
private class LoginService { public async Task<User> TryToGetUser(UsersRepository usersRepository, string login, string password) { try { var users = await usersRepository.GetAllUsers(); if (users == null || users.Length == 0) return null; foreach (var user in users) { if (user.login == login || user.email == login) { var pasHash = password.GetSHA256Hash(); if (user.password == pasHash) { return user; } } } } catch (Exception ex) { await App.Current.MainPage.DisplayAlert("Ошибка", $"Ошибка: {ex}", "Ок"); return null; } return null; } } private async Task TryToLogin() { if (string.IsNullOrWhiteSpace(login)) { await App.Current.MainPage.DisplayAlert("Ошибка", "Пожалуйста введите свой логин или email в поле, что бы продолжить", "Ок"); return; } if (string.IsNullOrWhiteSpace(password) || password.Length < Constants.minPassLength) { await App.Current.MainPage.DisplayAlert("Ошибка", $"Пожалуйста введите свой пароль в поле, что бы продолжить, минимальное количество символов {Constants.minPassLength}", "Ок"); return; } isLoading = true; LoginService service = new LoginService(); User currentUser = await Task.Run(async () => { return await service.TryToGetUser(usersRepository, login, password); }); if (currentUser == null) { isLoading = false; await App.Current.MainPage.DisplayAlert("Ошибка", "Пользователь с таким логином и паролем не найден, попробуйте проверить регистры", "Ок"); return; } userHandler.Save(currentUser); await SecureStorage.SetAsync("login", login); await SecureStorage.SetAsync("password", password); navigationManager.NavigateTo("/home"); }
可能的问题原因
- Release代码优化吞掉异常:Release模式下编译器启用代码优化,可能修改异常抛出路径,导致局部try-catch无法捕获。异步代码中的异常尤其容易受优化影响。
Task.Run的异常处理漏洞:用Task.Run包裹异步方法时,线程调度或上下文切换可能导致内层try-catch无法覆盖所有异常场景,外层又未添加捕获逻辑。App.Current.MainPage为空:Release构建初始化顺序可能与Debug不同,导致App.Current.MainPage为null,此时DisplayAlert调用会抛出二次异常,掩盖原始错误。- SHA256哈希方法的平台差异:
password.GetSHA256Hash()扩展方法在Release模式下可能因编码、依赖缺失等问题抛出异常,而Debug模式下无此问题。 - 数据访问层异常:
UsersRepository.GetAllUsers()在Release模式下可能出现数据库连接失败、权限不足或序列化错误,这些异常因优化被隐藏。
调试Release构建的方法
- 启用Release调试符号:在项目属性的Release配置中,将
Debug symbols设置为Portable或Full,然后用Visual Studio连接Android设备,以Release模式调试,直接捕获异常堆栈。 - 添加全局异常捕获:在
App.xaml.cs中注册全局异常处理事件,记录未捕获异常到本地文件:public App() { InitializeComponent(); AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; } private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) { var logPath = Path.Combine(FileSystem.AppDataDirectory, "task_errors.log"); File.WriteAllText(logPath, e.Exception.ToString()); e.SetObserved(); } private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { var logPath = Path.Combine(FileSystem.AppDataDirectory, "domain_errors.log"); File.WriteAllText(logPath, e.ExceptionObject.ToString()); } - 替换
DisplayAlert为日志记录:在TryToGetUser的catch块中,避免调用UI相关方法,直接写入异常日志,防止二次异常:catch (Exception ex) { var logPath = Path.Combine(FileSystem.AppDataDirectory, "login_errors.log"); File.AppendAllText(logPath, $"[{DateTime.Now}] {ex.ToString()}\n"); return null; } - 移除
Task.Run直接调用异步方法:TryToGetUser本身是异步方法,无需用Task.Run包裹,减少线程切换带来的上下文问题:User currentUser = await service.TryToGetUser(usersRepository, login, password); - 检查Android权限配置:确保
AndroidManifest.xml中添加了必要权限,比如本地存储、数据库访问权限,Release模式下权限验证更严格。 - 临时禁用代码优化:关闭Release配置的
Optimize code选项,测试问题是否消失。若消失,可给关键方法添加[MethodImpl(MethodImplOptions.NoOptimization)]特性针对性禁用优化。
内容的提问来源于stack exchange,提问作者Vitaliy
相关产品推荐
相关产品推荐

