使用Xamarin以编程方式下载APK遇阻,求C#解决方案
解决Android C#环境下APK下载路径错误的问题
我太懂你这种找了一天资料全是Java、好不容易试了C#代码还碰路径报错的烦躁了!咱们一步步把这个问题拆解开解决:
先搞懂“路径部分不存在”的核心原因
这个报错本质和Android存储权限、目录访问逻辑直接相关:
- Android 6.0(API 23)及以上版本,光在Manifest里加权限没用,必须动态申请存储访问权限;
- 直接用
Android.OS.Environment.DirectoryDownloads拼接路径时,可能因为权限不足导致程序无法识别该目录; - WebClient在Android异步场景下的兼容性不如更现代的HttpClient,容易踩坑。
具体解决方案步骤
1. 先把存储权限搞定
首先在AndroidManifest.xml里添加基础权限:
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <!-- Android 13+ 需要的媒体权限 --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" android:minSdkVersion="33" /> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" android:minSdkVersion="33" />
然后在代码里动态申请权限(以Xamarin.Android为例):
private async Task CheckAndRequestPermissions() { // 检查并申请存储写入权限 var storageStatus = await Permissions.CheckStatusAsync<Permissions.StorageWrite>(); if (storageStatus != PermissionStatus.Granted) { storageStatus = await Permissions.RequestAsync<Permissions.StorageWrite>(); } // Android 13+ 额外申请媒体写入权限 if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Tiramisu) { var mediaStatus = await Permissions.CheckStatusAsync<Permissions.MediaWrite>(); if (mediaStatus != PermissionStatus.Granted) { mediaStatus = await Permissions.RequestAsync<Permissions.MediaWrite>(); } } }
2. 确保目标目录真实可访问
别直接硬拼路径,用系统API获取可靠的Downloads目录,还要确认目录存在:
// 获取系统Downloads目录的完整路径 var downloadDir = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads); // 目录不存在就创建 if (!downloadDir.Exists) { downloadDir.Mkdirs(); } // 组合最终的APK保存路径 var apkSavePath = Path.Combine(downloadDir.AbsolutePath, "AppDownload.apk");
3. 用HttpClient替代WebClient(更稳定适配Android)
WebClient在.NET现代版本里已经被标记为过时,而且Android主线程不允许同步网络操作,改用异步的HttpClient更稳妥:
private async Task DownloadApkAsync() { // 先检查权限,没权限啥都白搭 await CheckAndRequestPermissions(); var downloadDir = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads); if (!downloadDir.Exists) { downloadDir.Mkdirs(); } var apkSavePath = Path.Combine(downloadDir.AbsolutePath, "AppDownload.apk"); using (var httpClient = new HttpClient()) { try { // 发起异步下载请求 var response = await httpClient.GetAsync("http://mywebspace.bplaced.net/AppDownload.apk"); response.EnsureSuccessStatusCode(); // 确保请求成功,失败会抛出异常 // 把下载流写入本地文件 using (var downloadStream = await response.Content.ReadAsStreamAsync()) using (var fileStream = new FileStream(apkSavePath, FileMode.Create, FileAccess.Write)) { await downloadStream.CopyToAsync(fileStream); } // 下载完成给个提示 Toast.MakeText(this, "APK下载搞定啦!", ToastLength.Long).Show(); } catch (Exception ex) { // 捕获异常,方便排查问题 Toast.MakeText(this, $"下载失败:{ex.Message}", ToastLength.Long).Show(); } } }
4. 调用下载方法
记得在非主线程触发,比如按钮点击事件里:
private async void DownloadBtn_Click(object sender, EventArgs e) { await DownloadApkAsync(); }
额外提醒
- 如果你的应用适配Android 10(API 29)及以上,需要在Manifest里加
android:requestLegacyExternalStorage="true"来兼容旧存储模型; - 下载完成后要自动安装的话,还得处理“未知来源应用”的安装权限,以及Android 11+的包安装API适配。
内容的提问来源于stack exchange,提问作者Ray Yago
相关产品推荐
相关产品推荐

