数码知识屋
霓虹主题四 · 更硬核的阅读氛围

Android App里加个下载功能,其实没那么难

发布时间:2026-01-25 06:01:16 阅读:93 次

你是不是也遇到过这种场景:用户点一下‘ref="/tag/91/" style="color:#E3A3CF;font-weight:bold;">下载PDF说明书’,App就卡住不动了;或者后台下着文件,一锁屏就断掉;更别提进度条永远停在99%、点暂停再继续直接重头来……这些不是玄学,是下载功能没集成对。

别硬写DownloadManager,先想清楚要啥

Android系统自带的DownloadManager确实能跑通基本流程,但它的限制也很实在:不能监听实时进度、不支持断点续传、没法自定义请求头(比如带token下载私有资源)。如果你只是下个APK或公开图片,它够用;但要做一个带进度、可暂停、支持登录态的下载模块,得换思路。

推荐组合:OkHttp + FileChannel + HandlerThread

轻量、可控、不依赖第三方库。核心逻辑就是:用OkHttp发请求,边读响应流边写入本地文件,同时用HandlerThread发进度消息到主线程更新UI。

OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
    .url("https://example.com/report.pdf")
    .header("Authorization", "Bearer " + token)
    .build();

client.newCall(request).enqueue(new Callback() {
    @Override
    public void onResponse(Call call, Response response) throws IOException {
        if (!response.isSuccessful()) throw new IOException("HTTP " + response.code());

        InputStream is = response.body().byteStream();
        File file = new File(getExternalFilesDir(null), "report.pdf");
        FileOutputStream fos = new FileOutputStream(file);
        byte[] buffer = new byte[8192];
        long total = 0;
        long contentLength = response.body().contentLength();

        while (true) {
            int read = is.read(buffer);
            if (read == -1) break;
            fos.write(buffer, 0, read);
            total += read;
            // 发送进度:total / contentLength
            handler.post(() -> updateProgress((int) (total * 100 / contentLength)));
        }
        fos.close();
        is.close();
    }

    @Override
    public void onFailure(Call call, IOException e) {
        handler.post(() -> showError(e.getMessage()));
    }
});

注意几个坑

• Android 10+ 默认禁止直接写外部存储,记得用getExternalFilesDir()或申请MANAGE_EXTERNAL_STORAGE(仅必要时);
• 不要在主线程做IO操作,上面代码里enqueue()已保证异步,但写文件和计算进度必须在子线程完成;
• 网络请求被中断(比如切后台、关WiFi)时,OkHttp会自动抛异常,你在onFailure里处理重试逻辑就行。

真需要后台持续下载?试试WorkManager

如果用户按Home键、App进后台,你还想让它接着下,就得靠WorkManager。把下载逻辑包成Worker,设置成Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED),系统会在合适时机唤醒执行,还能自动处理重试和电池优化白名单提示。

一句话:下载功能不是拼凑API,而是根据场景选对工具——简单任务用DownloadManager,可控需求上OkHttp,长期后台任务交WorkManager。别一上来就堆RxJava+Retrofit,有时候几行干净的OkHttp代码,反而更稳、更好查bug。