UWP应用上传Google Drive时因跨线程更新UI导致崩溃的问题求助
UWP应用上传Google Drive时因跨线程更新UI导致崩溃的问题求助
你遇到的RPC_E_WRONG_THREAD错误是UWP开发里非常常见的跨线程UI更新问题——Google Drive上传的进度回调是在后台线程触发的,而UWP的UI控件(包括绑定的属性)只能由创建它们的UI线程来更新,直接在后台线程修改绑定属性就会触发这个崩溃。
下面给你两个关键的修复方案,既解决崩溃问题,还能优化上传体验:
1. 强制在UI线程更新绑定属性
首先在你的MyFiles类里添加UI线程调度器的引用,确保属性更新操作跑在UI线程上:
修改MyFiles类
// 添加一个CoreDispatcher字段,用来调度UI线程操作 private CoreDispatcher _uiDispatcher; public MyFiles(StorageFile file) { // 获取当前UI线程的调度器(因为MyFiles是在UI线程创建的,这里能拿到正确的实例) _uiDispatcher = CoreWindow.GetForCurrentThread().Dispatcher; // 你的原有代码... FileInfo fileInfo = new FileInfo(file.Path); this.fullpath = file.Path; this.Size = fileInfo.Length; this.Value = 0; this.Name = file.Name; this.progress = new Progress<IUploadProgress>(); this.progress.ProgressChanged += progressChanged; // 修正原代码的语法错误 } private async void progressChanged(object sender, IUploadProgress e) { if (e.Status == UploadStatus.Uploading) { var newValue = (e.BytesSent * 100) / this.Size; // 用调度器把属性更新操作抛回UI线程 await _uiDispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { this.Value = newValue; }); } }
这里的核心是CoreDispatcher.RunAsync,它会把修改Value属性的操作放到UI线程的消息队列里执行,从根源上避免跨线程问题。
2. 优化上传方法,避免阻塞UI线程
你当前的UploadFile方法用了.Result同步调用异步API,会直接阻塞UI线程,导致上传时界面卡死,还可能加剧线程问题。改成异步版本会更友好:
修改DriveClass的UploadFile方法
// 把方法改成异步返回Task public async Task UploadFile(int index, IProgress<IUploadProgress> prog) { try { // 把.AsTask().Result改成await,避免阻塞UI StorageFile file = await StorageFile.GetFileFromPathAsync(SavedData.files[index].fullpath); var fileMetadata = new Google.Apis.Drive.v3.Data.File() { Name = Path.GetFileName(file.Name), MimeType = "application/octet-stream" }; // 同样用await替代.Result using (var stream = await file.OpenStreamForReadAsync()) { var request = this.drive.Files.Create(fileMetadata, stream, "application/octet-stream"); request.ProgressChanged += (IUploadProgress progressInfo) => { switch(progressInfo.Status) { case UploadStatus.Completed: Debug.WriteLine($"done!"); break; case UploadStatus.Failed: Debug.WriteLine($"Failed To Upload Into Google Drive"); break; default: Debug.WriteLine($"{progressInfo.BytesSent} has sent"); break; } prog.Report(progressInfo); }; request.ChunkSize = 262144; // 用异步上传方法替代同步Upload() await request.UploadAsync(); var uploadedFile = request.ResponseBody; Debug.WriteLine($"File uploaded: {uploadedFile.Name} ({uploadedFile.Id})"); } } catch (Exception ex) { Debug.WriteLine("Can't upload the file because -> " + ex.Message); } }
修改MediaPage的ButtonClick事件
// 把事件处理方法改成async private async void ButtonClick(object sender, RoutedEventArgs e) { // 如果要串行上传,用foreach+await foreach (var file in SavedData.files) { int index = SavedData.files.IndexOf(file); await DriveClass.UploadFile(index, file.progress); } // 如果要并行上传(更快,但要注意线程安全),可以用Task.WhenAll // var uploadTasks = new List<Task>(); // for (int i = 0; i < SavedData.files.Count; i++) // { // uploadTasks.Add(DriveClass.UploadFile(i, SavedData.files[i].progress)); // } // await Task.WhenAll(uploadTasks); }
额外提醒
你原来的代码里this.progress += progressChanged是语法错误,应该是this.progress.ProgressChanged += progressChanged——这个小错误可能也会导致进度回调没触发,记得修正。
这样修改后,既解决了跨线程崩溃的问题,上传过程中UI也不会卡死,用户体验会好很多。
备注:内容来源于stack exchange,提问作者Ariel P
相关产品推荐
相关产品推荐

