You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的实例变量里:

  1. 修改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);
}
  1. 在MainActivity.cs中添加成员变量:
// 新增用于暂存下载URL的变量
public string CurrentDownloadUrl { get; set; }
// 确保你已经定义了这个常量
private const int CREATE_PDF_REQUEST = 1;
  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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 17:04:09