非Play商店上架APK实现自动更新时文件下载不完整问题咨询
问题根因
- 错误配置GET请求参数:代码中为GET请求设置了
c.setDoOutput(true),该参数用于开启请求输出流,仅POST/PUT等需要提交请求体的请求需要开启,设置为true会自动将请求方法改为POST,服务端返回的189字节为错误响应的文本内容,并非APK文件流。 - 缺少HTTP响应校验:连接建立后没有校验响应状态码,若服务端返回3xx重定向、4xx权限/资源错误、5xx服务端错误,直接读取输入流会拿到错误内容而非APK文件。
- 缺少下载完整性校验:下载完成后没有校验本地文件的大小、哈希值等参数,直接触发安装流程。
解决方案
1 修正HTTP请求配置
直接删除c.setDoOutput(true)这行代码,GET请求无需开启输出流。
2 新增响应状态码校验
连接建立后先判断响应码是否属于2xx成功区间,只有符合要求才继续后续下载逻辑。
3 新增下载完整性校验
下载完成后先对比本地文件大小和服务端返回的Content-Length是否一致,有条件的场景可以提前从服务端获取APK的MD5值,下载完成后计算本地文件的MD5做二次校验,确认文件完整后再触发安装流程。
4 安装流程适配补充
Android 8.0及以上版本需要在Manifest中声明REQUEST_INSTALL_PACKAGES权限,同时使用FileProvider分享APK文件给系统安装器,避免出现权限相关的安装错误。
优化建议
推荐直接使用系统提供的DownloadManager组件实现APK下载,该组件自带断点续传、网络状态监听、下载进度回调、失败重试等能力,稳定性远高于自行实现的HTTP下载逻辑。
修正后的核心代码示例
URL url = new URL(arg0[0]); HttpURLConnection c = (HttpURLConnection) url.openConnection(); c.setRequestMethod("GET"); // 移除原有的c.setDoOutput(true)配置 c.connect(); // 新增响应码校验 int responseCode = c.getResponseCode(); if (responseCode < 200 || responseCode >= 300) { throw new IOException("请求失败,响应码:" + responseCode); } int lenghtOfFile = c.getContentLength(); if (lenghtOfFile <= 0) { throw new IOException("获取文件大小失败"); } String PATH = Objects.requireNonNull(mContext.getExternalFilesDir(null)).getAbsolutePath(); File file = new File(PATH); boolean isCreate = file.mkdirs(); File outputFile = new File(file, "my_apk.apk"); if (outputFile.exists()) { boolean isDelete = outputFile.delete(); } FileOutputStream fos = new FileOutputStream(outputFile); InputStream is = c.getInputStream(); byte[] buffer = new byte[1024 * 8]; // 加大缓冲区提升下载效率 int len1; long total = 0; while ((len1 = is.read(buffer)) != -1) { total += len1; fos.write(buffer, 0, len1); publishProgress((int) ((total * 100) / lenghtOfFile)); } fos.flush(); // 补充flush操作确保所有数据写入磁盘 fos.close(); is.close(); if (mPDialog != null) mPDialog.dismiss(); // 新增下载完整性校验 if (total != lenghtOfFile) { throw new IOException("文件下载不完整"); }
内容的提问来源于stack exchange,提问作者Sanjana Nair
相关产品推荐
相关产品推荐

