Xamarin Android中使用Storage Access Framework下载文件至公共外部目录:URL传递为空问题排查及异步实现咨询
我来帮你拆解这两个问题,一步步解决你的困扰:
问题1:获取到的url始终为空的原因及解决方法
你遇到的url为空问题,核心原因是系统返回的Intent不会携带你自定义的额外数据:
当你调用Intent.ActionCreateDocument让用户选择保存路径时,这个Intent是发给系统文件选择器的,用户操作完成后返回的data是系统全新创建的Intent——它只会包含用户选中的文件Uri(data.Data),完全不会保留你之前添加的download_url这类自定义extra字段,所以data.GetStringExtra("download_url")自然拿不到值。
解决方法:把下载URL暂存到Activity的成员变量中
我们需要换个方式传递URL,比如把它存在MainActivity的实例变量里:
- 修改
FileDownloader.cs,不再把URL放入Intent,而是暂存到Activity:
public void DownloadFile(string url, string fileName) { Intent intent = new Intent(Intent.ActionCreateDocument); intent.AddCategory(Intent.CategoryOpenable); intent.PutExtra(Intent.ExtraTitle, fileName); intent.SetType("application/pdf"); var activity = MainActivity.Instance; // 将URL暂存到Activity的成员变量 activity.CurrentDownloadUrl = url; activity.StartActivityForResult(intent, CREATE_PDF_REQUEST); }
- 在
MainActivity.cs中添加成员变量:
// 新增用于暂存下载URL的变量 public string CurrentDownloadUrl { get; set; } // 确保你已经定义了这个常量 private const int CREATE_PDF_REQUEST = 1;
- 修改
OnActivityResult,从成员变量中获取URL:
protected override async void OnActivityResult(int requestCode, Result resultCode, Intent data) { base.OnActivityResult(requestCode, resultCode, data); try { switch (requestCode) { case CREATE_PDF_REQUEST: // 先判断用户是否取消了选择 if (resultCode != Result.Ok || data == null) return; string url = CurrentDownloadUrl; if (string.IsNullOrEmpty(url)) { Toast.MakeText(this, "下载地址无效", ToastLength.Short).Show(); return; } // 后续下载逻辑写在这里 break; default: // 其他处理逻辑 } } catch (Exception ex) { // 异常处理逻辑 } }
问题2:OnActivityResult中处理异步调用的正确方式
直接在OnActivityResult中使用await是可行的,但要注意写法
你可以把OnActivityResult改成async void(因为它是重写的生命周期方法,返回类型必须是void),直接用await处理异步下载——这种写法适合事件/生命周期回调场景,不会阻塞UI线程,也能正确捕获异常。
不推荐用Task.Run(() => await client.DownloadFileAsync())
DownloadFileAsync本身就是异步IO操作,不需要额外用Task.Run开线程,直接await即可,避免不必要的线程切换。另外,WebClient是较旧的API,更推荐用HttpClient来实现下载,代码更灵活且符合现代.NET异步规范:
修改后的OnActivityResult完整下载逻辑:
protected override async void OnActivityResult(int requestCode, Result resultCode, Intent data) { base.OnActivityResult(requestCode, resultCode, data); try { switch (requestCode) { case CREATE_PDF_REQUEST: if (resultCode != Result.Ok || data == null) return; string url = CurrentDownloadUrl; if (string.IsNullOrEmpty(url)) { Toast.MakeText(this, "下载地址无效", ToastLength.Short).Show(); return; } // 使用HttpClient实现异步下载(推荐) using (var httpClient = new HttpClient()) using (var remoteStream = await httpClient.GetStreamAsync(url)) using (var localStream = ContentResolver.OpenOutputStream(data.Data)) { // 把远程流复制到本地文件流 await remoteStream.CopyToAsync(localStream); // 下载完成提示(要回到UI线程) RunOnUiThread(() => { Toast.MakeText(this, "文件下载完成", ToastLength.Short).Show(); }); } break; default: // 其他处理逻辑 } } catch (Exception ex) { RunOnUiThread(() => { Toast.MakeText(this, $"下载失败:{ex.Message}", ToastLength.Long).Show(); }); } }
内容的提问来源于stack exchange,提问作者Kaustabh Kakoty
相关产品推荐
相关产品推荐

