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

Android 11(API 29)应用生产环境文件下载失效问题求助

Android 11生产环境文件下载无响应(调试/内部测试正常)

问题背景

应用上线2年,通过<a href>标签结合PHP readfile()函数实现从Web服务器下载文件的功能。Android 11版本前运行正常,但升级至Android 11后,生产环境触发下载无任何响应,但通过Android Studio调试安装、或上传至Google Play内部测试渠道时功能正常。

已在Manifest中配置相关权限及requestLegacyExternalStorage,配置如下:

<uses-permission android:maxSdkVersion="28" android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.RECORD_VIDEO"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-feature android:name="android.hardware.location.gps" android:required="true"/>

<application 
    android:hardwareAccelerated="true" 
    android:icon="@mipmap/ic_launcher" 
    android:label="@string/app_name" 
    android:networkSecurityConfig="@xml/network_security_config" 
    android:requestLegacyExternalStorage="true" 
    android:supportsRtl="true" 
    android:usesCleartextTraffic="true">

可能原因及解决方案

1. ProGuard/R8 混淆破坏下载逻辑

生产环境通常会开启代码混淆,若WebView下载相关的类或回调被误混淆,会导致下载流程失效,而调试/内部测试版本可能未开启混淆,因此功能正常。

解决方法:在ProGuard规则文件(proguard-rules.pro)中添加以下规则,保护WebView下载相关组件:

-keep class android.webkit.DownloadListener { *; }
-keep class android.webkit.WebChromeClient { *; }
-keep class android.webkit.WebViewClient { *; }

若自定义了下载回调类,也要添加对应的-keep规则。

2. PHP服务器响应头不规范

Android 11+的WebView对下载请求的响应头要求更严格,生产环境服务器的响应头配置可能与测试环境不一致,导致下载被拦截。

解决方法:检查PHP端的响应头设置,确保包含以下关键参数:

  • Content-Disposition: 必须设置为attachment; filename="文件名.后缀",明确标识为附件下载
  • Content-Length: 准确设置文件大小
  • Content-Type: 匹配文件的MIME类型(如application/pdf、image/jpeg等)

示例PHP代码修正:

$filePath = 'path/to/your/file';
$fileName = 'downloaded_file.pdf';

header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="' . $fileName . '"');
header('Content-Length: ' . filesize($filePath));

readfile($filePath);
exit;

可通过curl -I 下载链接命令对比测试环境与生产环境的响应头差异。

3. Google Play签名导致Manifest属性失效

部分情况下,Google Play App Signing流程可能意外修改Manifest中的requestLegacyExternalStorage属性,导致生产环境无法启用旧存储模式。

解决方法:下载生产环境的APK包,通过反编译工具查看Manifest文件,确认requestLegacyExternalStorage="true"属性是否存在。若缺失,需重新打包并确保该属性在Manifest中正确配置后再上传。

4. 运行时权限未正确获取

虽然Manifest中声明了READ_EXTERNAL_STORAGE权限,但Android 10+(含11)需要动态申请该权限。调试时IDE可能自动授予权限,而生产环境用户可能未授权,导致下载路径无法访问。

解决方法:在触发下载前,检查并请求READ_EXTERNAL_STORAGE权限:

if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_CODE_STORAGE);
} else {
    // 执行下载逻辑
}

5. 切换至DownloadManager实现下载

依赖WebView的<a href>跳转下载在Android 11+的存储规范下兼容性较差,改用系统DownloadManager是更稳定的方案,无需依赖WebView,且自动适配Scoped Storage。

示例代码:

String downloadUrl = "https://your-server.com/file/download";
String fileName = "target_file.ext";

DownloadManager.Request request = new DownloadManager.Request(Uri.parse(downloadUrl));
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE);
request.setTitle("文件下载");
request.setDescription("正在下载文件");
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
// 下载到公共Download目录,无需WRITE权限
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);

DownloadManager downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
if (downloadManager != null) {
    downloadManager.enqueue(request);
}

内容的提问来源于stack exchange,提问作者alexowhile

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 13:35:18