跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

Android 自动更新:从下载 APK 到安装的全流程

探讨了 Android 应用中全量 APK 自动更新的实现,包含权限、多线程下载、进度弹窗与安装触发。指出了 Android 7.0+ FileProvider 适配和运行时权限的要点,并简介了 DownloadManager 替代方案。

zhang发布于 2026/6/8更新于 2026/8/2325 浏览
Android 自动更新:从下载 APK 到安装的全流程

Android 自动更新:从下载 APK 到安装的全流程

引言

在 Android 上做自动更新,没有 iOS 的 APNs,只能自己搭一套。我用了比较原始但可靠的全量 APK 下载方案,记录一下关键步骤和坑。

权限配置

先得在 manifest 里声明权限。INTERNET 不用多说。存储权限 Android 6.0 以后需要动态申请,不然文件落不了地。安装未知来源权限 Android 8.0 起也需要运行时请求。

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="lgx.acc.updatedemo">
    <!-- 网络权限 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <!-- 存储权限 (Android 6.0+ 需动态申请) -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <!-- 安装权限 (Android 8.0+ 需动态申请) -->
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
</manifest>

动态申请存储权限的逻辑就不展开了,网上有很多,这里记住一点:如果没申请成功,下载会悄无声息失败。

核心思路

设计上就几条:下载走子线程,UI 刷新回主线程,用 Handler 传消息,外加一个取消标志。UI 这边用 Dialog 展示进度,体验比通知栏好控制。

实现步骤

检查更新

先得有个地方判断版本号。实际项目里会请求服务端接口,这里先简化成变量。

public void checkUpdateInfo() {
    // 请求服务端接口,拿到最新版本信息后与本地比较
    boolean isNew = false; // 假设需要更新
    if (!isNew) {
        showUpdateDialog();
    }
}

更新提示弹窗

发现有新版就弹窗,给'下载'和'以后再说'按钮。这里直接用 AlertDialog 就行。

private void showUpdateDialog() {
    AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
    builder.setTitle("软件版本更新");
    builder.setMessage("发现新版本,是否立即下载?");
    builder.setPositiveButton("下载", (dialog, which) -> {
        showDownloadDialog();
    });
    builder.setNegativeButton("以后再说", (dialog, which) -> {
        dialog.dismiss();
    });
    builder.create().show();
}

下载进度 Dialog

自定义一个带进度条的 XML 布局,放在 Dialog 里。布局文件 progress.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="16dp">

    <TextView
        android:id="@+id/text_progress"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="下载进度:0%"
        android:textSize="14sp" />

    <ProgressBar
        android:id="@+id/progress"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="100"
        android:progress="0" />
</LinearLayout>

下载任务

下载跑在 Runnable 里,用 HttpURLConnection。注意 read timeout 别设太长,不然取消时要等很久。进度计算加了个 content-length 未知时的处理,防止除零。

private Runnable mdownApkRunnable = new Runnable() {
    @Override
    public void run() {
        URL url;
        try {
            // 建议使用 HTTPS 地址以保证安全
            url = new URL("https://example.com/app/release.apk");
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(5000);
            conn.setReadTimeout(5000);
            conn.connect();

            int length = conn.getContentLength();
            InputStream ins = conn.getInputStream();
            File file = new File(savePath);
            if (!file.exists()) {
                file.mkdirs();
            }
            File apkFile = new File(saveFileName);
            FileOutputStream fos = new FileOutputStream(apkFile);
            int count = 0;
            byte[] buf = new byte[1024];
            int numread;

            while (!intercept && (numread = ins.read(buf)) != -1) {
                count += numread;
                // 防止除以零错误
                if (length > 0) {
                    progress = (int) (((float) count / length) * 100);
                } else {
                    progress = 100;
                }
                // 通知 UI 刷新进度
                mHandler.sendEmptyMessage(DOWN_UPDATE);
                fos.write(buf, 0, numread);
            }

            fos.close();
            ins.close();
            conn.disconnect();

            if (!intercept) {
                // 下载完成通知安装
                mHandler.sendEmptyMessage(DOWN_OVER);
            }
        } catch (Exception e) {
            e.printStackTrace();
            // 处理异常,例如显示错误提示
            mHandler.sendEmptyMessage(DOWN_ERROR);
        }
    }
};

启动线程并处理好 Handler 消息,更新进度和下载完成状态。

安装 APK

下载完直接调安装 Intent。Android 7.0 以上会报 FileUriExposedException,必须用 FileProvider 转 content URI。实际项目里直接照搬 FileProvider 配置即可。

private void installAPK() {
    File apkFile = new File(saveFileName);
    if (!apkFile.exists()) return;
    Intent intent = new Intent(Intent.ACTION_VIEW);
    // 7.0 以上需用 FileProvider,此处为示意
    intent.setDataAndType(Uri.parse("file://" + apkFile.toString()),
            "application/vnd.android.package-archive");
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    mContext.startActivity(intent);
}

踩坑与建议

网络请求

生产环境一定用 HTTPS,并且最好在下载完成后校验一下签名,防止包被替换。

兼容性

Android 11 分区存储后,WRITE_EXTERNAL_STORAGE 基本废弃,建议用 DownloadManager 或 MediaStore。不过对于全量更新 APK,很多人还是用外部存储的公有目录,短期还能用。

DownloadManager

如果只是想简单把下载丢给系统,DownloadManager 最省心,可以后台下载并显示通知。缺点是没法自定义进度 UI,对更新流程控制弱一些。

// DownloadManager 示例思路
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(apkUrl));
request.setTitle("App Update");
request.setDescription("Downloading update...");
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);

总结

这套基于 HttpURLConnection + Handler + Dialog 的方案,适合快速理解自动更新的内部流程。真正线上项目我更推荐 OkHttp 处理下载,同时结合 DownloadManager 做保底。再提醒一下:各版本权限差异一定要处理,不然用户点完更新就 crash,留存都跌没了。

目录

  1. Android 自动更新:从下载 APK 到安装的全流程
  2. 引言
  3. 权限配置
  4. 核心思路
  5. 实现步骤
  6. 检查更新
  7. 更新提示弹窗
  8. 下载进度 Dialog
  9. 下载任务
  10. 安装 APK
  11. 踩坑与建议
  12. 网络请求
  13. 兼容性
  14. DownloadManager
  15. 总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 数据结构与算法:递归原理及 Java 实现案例
  • Python 同时适配 Elasticsearch 与 Easysearch 实战复盘
  • 计算机视觉高级应用与前沿发展
  • Python OCC 三维建模实战指南:构建专业级 CAD 应用
  • 基于扣子平台搭建 AI 智能体与变现路径指南
  • Flutter Spry 适配鸿蒙:端侧轻量 Web 服务实战
  • 数据结构:链表详解与节点链接原理
  • MCP+Skill 能力下的前端 JS 逆向自动化落地
  • VRChat 跨语言交流工具 VRCT 使用指南
  • FPGA 验证环境构建:Testbench 编写与 Quartus II+ModelSim 联合仿真
  • Whisper-large-v3 部署避坑指南:解决启动、性能与识别率问题
  • 基于 AutoGPT 与 Python 构建自主智能体实战指南
  • AI 大模型重塑物流业:行业实践与架构演进
  • Cats Blender 插件使用指南:VRChat 模型优化与制作
  • Windows 系统 Elasticsearch 安装配置指南
  • C 语言快速排序详解:从基础到非递归实现
  • Ubuntu 22.04 基于 ROS2 Humble 的 PX4 无人机仿真环境搭建
  • 网络安全从业者需掌握的十大核心基础知识
  • C++ 哈希表核心机制:从哈希冲突到负载因子
  • Linux 入门:常用命令、软件安装与项目部署指南

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online