Android核心技术-day05-05-安卓下的多线程下载(带ProgressBar)有bug版

Android入门第56天-在Android里使用OKHttp多线程下载文件并展示其进度 OkHttp是一个神器。OkHttp分为异步、同步两种调用。今天我们就会基于OkHttp的异步调用实现一个多线程并行下载文件并以进度条展示总进度的实用例子。当然这不是我们的Android里使用OkHttp的最终目标,我们最终在下一篇中会在今天这一课的基础上加入“断点续传”的功能,从而以这么连续的几篇从易到难的循序渐进的过程,让大家熟悉和掌握Android中使用OkHttp的技巧以便于形成大脑的“肌肉记忆”。 阅读详情

package com.gaozewen.multidownloader;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.Toast;

import com.lidroid.xutils.HttpUtils;
import com.lidroid.xutils.exception.HttpException;
import com.lidroid.xutils.http.ResponseInfo;
import com.lidroid.xutils.http.callback.RequestCallBack;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private EditText mEt_thread_count;
    private EditText mEt_path;
    private LinearLayout mLl_container;

    public String path = "http://192.168.1.102:8080/xunlei.exe";
    public int totalThreadCount = 0;
    public int runningThreadCount = 0;
    private ArrayList<ProgressBar> mPbs;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 1. 找到控件
        // 2. 在 button 点击事件中下载文件
        // 3. 下载的时候显示下载进度
        mEt_thread_count = (EditText) findViewById(R.id.et_thread_count);
        mEt_path = (EditText) findViewById(R.id.et_path);
        mLl_container = (LinearLayout) findViewById(R.id.ll_container);
    }

    /**
     * 用第三方控件 xUtils 下载
     * @param view
     */
    public void download2(View view) {
        path =  mEt_path.getText().toString().trim();
        HttpUtils http = new HttpUtils();
        http.download(path, getCacheDir() + "/" + getDownloadFileName(path), true, new RequestCallBack<File>() {
            @Override
            public void onSuccess(ResponseInfo<File> responseInfo) {
                Toast.makeText(MainActivity.this,"下载成功", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onFailure(HttpException e, String s) {
                Toast.makeText(MainActivity.this,"下载失败", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onLoading(long total, long current, boolean isUploading) {
                System.out.println(current);
                super.onLoading(total, current, isUploading);
            }
        });
    }

    public void download(View view) {
        totalThreadCount = Integer.valueOf(mEt_thread_count.getText().toString().trim());
        path = mEt_path.getText().toString().trim();
        mLl_container.removeAllViews();

        mPbs = new ArrayList<>();
        for (int i = 0; i < totalThreadCount; i++) {
            // 有几个线程就创建几个 进度条
            ProgressBar pb = (ProgressBar) View.inflate(this, R.layout.pb, null);
            mLl_container.addView(pb);
            mPbs.add(pb);
        }

        new Thread() {
            @Override
            public void run() {
                try {
                    URL url = new URL(path);
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    conn.setRequestMethod("GET");
                    int code = conn.getResponseCode();
                    if (code == 200) {
                        int length = conn.getContentLength();
                        System.out.println("file length: " + length);
                        // 创建一个空的文件,并且设置他的文件长度等于服务器上的文件长度
                        RandomAccessFile raf = new RandomAccessFile(getCacheDir() + "/" + getDownloadFileName(path), "rw");
                        raf.setLength(length);
                        raf.close();

                        int blockSize = length / totalThreadCount;
                        System.out.println("every block size: " + blockSize);
                        runningThreadCount = totalThreadCount;
                        for (int threadId = 0; threadId < totalThreadCount; threadId++) {
                            int startPosition = blockSize * threadId;
                            int endPosition = blockSize * (threadId + 1) - 1; // -1 是到当前块的最后一个字节
                            if (threadId == (totalThreadCount - 1)) {
                                endPosition = length - 1;
                            }
                            System.out.println(String.format("threadId: %s\ndownload rage: %s to %s", threadId, startPosition, endPosition));

                            new DownloadThread(threadId, startPosition, endPosition).start();
                        }
                    } else {

                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }

    /**
     * 从网路路径获取文件名
     *
     * @param path 网络路径
     * @return 文件名
     */
    private static String getDownloadFileName(String path) {
        return path.substring(path.lastIndexOf("/") + 1);
    }

    /**
     * 下载文件的线程
     */
    private class DownloadThread extends Thread {
        /**
         * 线程 id
         */
        private int threadId;
        /**
         * 当前线程下载的起始位置
         */
        private int startPosition;
        /**
         * 当前线程下载的终止位置
         */
        private int endPosition;

        /**
         * 当前线程需要去下载的总共的字节
         */
        private int threadTotal;

        /**
         * 上次断点 下载的总共字节数
         */
        private int lastDownloadTotalSize;

        public DownloadThread(int threadId, int startPosition, int endPosition) {
            this.threadId = threadId;
            this.startPosition = startPosition;
            this.endPosition = endPosition;
            this.threadTotal = endPosition - startPosition;

            mPbs.get(threadId).setMax(threadTotal); // 设置进度条
        }

        @Override
        public void run() {
            System.out.println(String.format("threadID: %s  begin working", threadId));
            // lest thread download it's self range data
            try {
                // 查询 记录断点信息的文件
                File finfo = new File(getCacheDir(), totalThreadCount + getDownloadFileName(path) + threadId + ".txt");
                if (finfo.exists() && finfo.length() > 0) { // 断点下载
                    FileInputStream fis = new FileInputStream(finfo);
                    BufferedReader br = new BufferedReader(new InputStreamReader(fis));
                    String lastPosition = br.readLine();
                    int intLastPosition = Integer.parseInt(lastPosition);

                    // 进度条断点续借位置
                    lastDownloadTotalSize = intLastPosition - startPosition;

                    // This thread download data before times;
                    startPosition = intLastPosition;
                    fis.close();
                }
                // 第一次下载
                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                System.out.println(String.format("begin and end: %s  range of download: %s to %s", threadId, startPosition, endPosition));
                conn.setRequestProperty("Range", String.format("bytes=%s-%s", startPosition, endPosition));
                // 从服务器下载资源
                int code = conn.getResponseCode();
                if (code == 206) {
                    InputStream is = conn.getInputStream();
                    // 第一次没有这个文件则创建,下次直接打开这个文件
                    RandomAccessFile raf = new RandomAccessFile(getCacheDir() + "/" + getDownloadFileName(path), "rw");

                    // !!! position of begin to write
                    raf.seek(startPosition); // 将指针移动到这次开始续传的位置

                    byte[] buffer = new byte[1024];
                    int len = -1;
                    int total = 0; // 当前线程 这次下载的数据
                    while ((len = is.read(buffer)) != -1) {
                        raf.write(buffer, 0, len);
                        total += len;
                        // 创建记录 保存当前线程下载的位置 (注意: rwd 同步实时写入到设备中)
                        RandomAccessFile infoRaf = new RandomAccessFile(getCacheDir() + "/" + totalThreadCount + getDownloadFileName(path) + threadId + ".txt", "rwd");
                        infoRaf.write(String.valueOf(startPosition + total).getBytes());
                        infoRaf.close();

                        // 每次读写显示一个进度
                        mPbs.get(threadId).setProgress(total + lastDownloadTotalSize);
                    }
                    raf.close();
                    is.close();
                    System.out.println(String.format("thread: %s  download complete...", threadId));
                } else {
                    System.out.println("request download failed");
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                synchronized (MainActivity.class) {
                    runningThreadCount--;
                    if (runningThreadCount <= 0) { // 多线程下载完成 删除 位置信息文件
                        System.out.println("multi thread download complete.");
                        for (int i = 0; i < totalThreadCount; i++) {
                            File positionFile = new File(getCacheDir(),totalThreadCount + getDownloadFileName(path) + i + ".txt");
//                            System.out.println(positionFile.delete());
                        }
                    }
                }
            }
        }
    }

}

 

Android高效多线程文件下载实现 本文还有配套的精品资源,点击获取 简介:本项目为Android平台上的"down_ANDROID",重点讲解如何使用Download Service在后台高效下载大文件,以及多线程技术加速下载过程。通过该示例项目,开发者可以了解如何创建自定义下载管理器,实现文件的下载、暂停、恢复和取消等操作。实现步骤包括初始化下载任务、设置请求头、创建多线程、下载部分文件、保存本地文件、合... 阅读详情

相关推荐

实现Android多线程文件下载的核心技术

本文还有配套的精品资源,点击获取 简介:在Android开发中,多线程下载技术对提升大文件下载速度和优化用户体验至关重要。本文详细介绍了多线程下载的基本原理以及如何利用Android技术如AsyncTask、Thread和Handler、IntentService、下载管理库以及存储合并技术等实现多线程下载。同时,文章提供了代码示例和源码说明,帮助开发者通过实践更好地理解和...

weixin_33750664的博客 1409

Android 多线程下载以及断点续传

在日常开发中,我们不可避免的会接到类似这样的需求,下载一个比较大的素材文件或者安装包文件,以此实现APP的自动更新,APP内的素材替换等。这里其实我们可以记录每一次写入文件的进度,当下载任务被暂停的时候,我们就将对应的任务记录下载,记录相应的url,存储文件,当前下载的进度等基本信息,当用户再次出发的时候我们就可以从这些信息恢复进度,继续下载。获取到对应的内容,那么我们就要在文件的指定区域去写入,由于我们是多线程下载,因此文件并不是每次都是从前往后一个个字节写入的,随时可能在文件的任何一个地方写入数据。

涛的博客 3627

Android多线程下载实战示例项目

在Java中,Runnable是一种轻量级的任务封装方式,它允许我们将需要并发执行的代码放入run()方法中。结合Thread对象启动,即可实现真正的并行执行。针对多线程下载场景,我们可以定义一个名为的类,该类实现Runnable接口,并接收必要的参数(如URL、起始偏移量、结束偏移量、本地保存路径等)来完成特定分块的下载。@Overridetry {// 定位到该块的起始位置= -1) {} else {

weixin_33363025的博客 981

Android基础篇-多线程下载(一)

效果图 下载文件: 讲解: 多线程下载多个文件: 下载首先就必须明白http协议,不会的读者:请点击这里 首先我们知道关于操作网络我们是不能直接在UI(主)线程中直接操作的,所以这里我们就得开启线程 在主线程调用的时候new多次开启的线程,就实现了线程,然后在每个线程中进行网络操作,这就是所谓的多线程下载多个

王涛的博客 2469

Android-使用AsyncTask实现的多任务多线程下载的Demo支持断点续传。

使用AsyncTask实现的多任务多线程下载的Demo,支持断点续传。

DRM驱动架构浅析-上(DRM基础概要与U-Boot阶段驱动解析)

本文主要探讨了Rockchip平台的DRM显示驱动架构及其在U-Boot阶段的实现。

一路寒风身如絮,命海沉浮客独行 1761

Android文件下载——多线程下载

为了实现多线程下载,我们需要使用下面几个部分的知识来实现: 1. 相关逻辑 1.1 HTTP首部信息 HTTP请求头部字段Range,可以用来标识当前请求所请求的这个文件的数据范围,这个范围是byte类型的范围,比如: connection = (HttpURLConnection) url_c.openConnection(); ... connection.setRequestProperty("Range", "bytes=" + startPos +"-" + endPos); 这样就可以在下载一

梦否 2985

关于三维全波电磁场仿真软件CST的安装和问题解决

百度网盘链接:https://pan.baidu.com/s/1RgWr8Qau1EzSOlh29SewFQ提取码:fzza这里是2023版本的CST,需要其他版本的uu们可以去官网或者其他人的网盘下载。

weixin_60728441的博客 3599

Android基础篇-多线程下载(二)

效果图 LOG效果图: 多线程下载 如果基础不怎么好的读者,请先去了解我的第一篇多线程的博客代码:请点击这里 多线程下载同一个文件: 1. 好处:可以提升下载速度,但是并不是你开启下载的线程越多速度就达到很快,因为最终的线程下载速度还是取决于你的带宽,开启多个线程只能让你和别人同时下载时,你的速度是可以比别人更快的 2.分析问题:我

王涛的博客 1426

TeraTerm 使用技巧

⏹TeraTerm 使用技巧

菜鸟记录 9980

RealSense D455启动教程

RealSense D455启动教程 修改rs_camera.launch

每天进步一点点 2416

Android实现多线程下载文件,支持断点

本篇博客主要介绍多线程去下载文件,以下载多个app为例。不管去下在app,音视频等文件,实现起来都一样。

小编君的博客 2246

Linux下科大讯飞离线语音识别SDK从下载到实战(附常见问题解决)

本文详细介绍了在Linux系统下,如何从科大讯飞开放平台获取离线语音识别SDK,完成环境准备、编译部署到实战开发的完整流程。文章重点解析了SDK目录结构、语法词典定制、示例代码修改,并针对开发中常见的“词典加载失败”、音频格式、交叉编译及性能优化等问题提供了深度解决方案,助力嵌入式与IoT开发者快速实现稳定可靠的离线命令识别功能。

honey的博客 1238

探秘Dpuntu的Android Downloader:一款高效、灵活的Android资源下载工具

探秘Dpuntu的Android Downloader:一款高效、灵活的Android资源下载工具 在Android开发中,资源的下载管理是一个必不可少的部分,而带来的正是为此目的打造的一款强大且易用的工具。本文将深入探讨其功能、技术实现和应用场景,以期帮助更多的开发者提高工作效率。 项目简介 Android Downloader 是一个开源库,旨在简化Android应用中的文件下载任务。它支持多...

gitblog_00040的博客 444

SAP 人力资源工资配置项3---工资项配置

工资项设置

xiouluoyang的专栏 1万+

从LLM到Agent:智能体核心范式与认知架构全景解读

摘要: Agent技术正成为AI领域的新焦点,其核心在于通过外部认知基础设施(如记忆、工具、协议等)将大语言模型(LLM)从“生成文本”升级为“执行任务”的智能体。2026年的研究显示,Agent能力提升的关键已从模型本身转向外部化架构,如Harness工程(执行框架)和认知解耦(如ORCA框架)。当前Agent框架生态呈现多元化竞争(如LangGraph、AutoGen、CrewAI),但可观测性与治理仍是薄弱环节。未来竞争壁垒将取决于外部基础设施的完善程度,而非单纯模型性能。

2601_95807009的博客 50

Android多线程下载文件拆解:原理、实现与优化

在开发需要下载大型文件的应用时,传统单线程下载面临速度瓶颈、断网风险和内存压力等问题。多线程下载技术通过并行下载、断点续传和文件分块写入等方式有效解决这些问题。核心技术包括HTTP Range请求、随机文件访问、线程同步控制和进度监控。实现流程包括检查服务器支持、预分配文件空间、计算分块范围、启动多线程下载和合并文件。代码实现涉及检查服务器支持、文件分块计算和多线程下载核心逻辑。增强功能包括断点续传和动态线程数调整。性能优化测试表明,3-5线程时达到最佳性价比,建议采用动态线程数策略。常见问题包括写入文件错

2508_91837411的博客 1670

KMD 核心功能模块——命令提交与调度:Ring Buffer 与硬件引擎交互

《KMD 中的中断与同步机制——从 Fence 到 Timeline Semaphore 的深度解析》GPU 需要持续接收命令,但硬件执行是流式处理。Ring Buffer 作为。导致 CVE-2022-1234(GPU 挂起),修复后强制检查。:命令提交延迟从 50μs 降至 5μs(20 倍提升)。,CPU 通过写入该地址更新指针(避免直接操作寄存器)。,实现 CPU 与 GPU 的高效协同。),GPU 读指针更新是硬件自动完成的。(通过硬件状态机),实现真正的零等待。AMDGPU 驱动曾因。

amy_mhd的博客 82

Android之——多线程下载示例

一、概述          说到Android中的文件下载,Android API中明确要求将耗时的操作放到一个子线程中执行,文件的下载无疑是需要耗费时间的。下面,我们一起来实现一个Android中利用多线程下载文件的小例子。 二、服务端准备         在这个小例子中我以下载有道词典为例,在网上下载有道词典的安装包,在eclipse中新建项目web,将下载的有道词典安装包放置在

冰河的专栏 4426

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

Android中多线程下载列表实现

实现了一下Android中的文件多线程下载模块,支持自定义线程数、断点续传、下载任务的删除,添加等功能,这里封装了一下,功能已全部实现。不过由于使用的是最简单的手动线程数组及消息通知实现,可能还存在某些小问题。笔者会在后面的使用过程中再进行优化完善。先看一下程序测试效果,这里指定了5个下载任务,以及2个下载线程,具体如下:

It's just a matter of time. 4143

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

【实战】android的多线程下载

MainActivity.java package com.itheima.multithreaddownload; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.InputStream; import java.io.RandomAccessFile;

Mind_programmonkey的博客 708

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Android 实例:多线程下载,进度条,音乐播放器(2)

其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。上面分享的腾讯、头条、阿里、美团、字节跳动等公司2019-2021年的高频面试题,博主还把这些技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,上面只是以图片的形式给大家展示一部分。【Android思维脑图(技能树)】知识不体系?

2401_84149882的博客 307

Android多线程断点续传详解与案例

(1)Android多线程断点续传 Android多线程断点续传下载器涉及了Android应用开发大部分的核心知识和难点: (1)Android中主线程和非主线通信机制:Handler、Looper、Message、MessageQueue。 (2)多线程编程、多线程的管理。 (3)Android网络编程。 (4)IoC技术,自己动手实现设计模式中的Liste

人生起落,生活苦甜,坚持追求! 3301

安卓多线程下载文件(一)

本文介绍了如何在安卓应用中实现多线程下载文件的功能,并支持断点续传。首先,通过比喻将文件下载比作搬运砖块,解释了多线程下载的原理。接着,设计了用于记录线程信息的数据表,包括线程编号、文件地址、下载起始位置、结束位置、已下载字节数和文件总长度等字段。然后,创建了一个继承自SQLiteOpenHelper的数据库帮助类DBHelper,用于管理线程信息表的创建和更新。最后,定义了一个ThreadInfo实体类,用于存储每个线程的下载信息。通过这些步骤,开发者可以实现一个高效且支持断点续传的文件下载功能。

csdn_too的博客 880

Android 多线程文件下载

原理一:HTTP多线程下载原理 1、发送一个含有Rang头的Head请求,如果返回状态码为206,则允许多线程下载 原理二:多线程下载原理 1、使用HttpClient的Head请求获取请求文件的信息 2、发送一个Rang的Head请求判断是否允许多线程下载 3、通过主任务创建多个分段下载线程,分段下载文件,然后用Java的随机读写文件类保存下载的内容 调度程序(有Progress...

hyshucom 209

android等待多个线程结束,android实现多线程下载文件(支持暂停、取消、断点续传)...

多线程下载文件(支持暂停、取消、断点续传)多线程同时下载文件即:在同一时间内通过多个线程对同一个请求地址发起多个请求,将需要下载的数据分割成多个部分,同时下载,每个线程只负责下载其中的一部分,最后将每一个线程下载的部分组装起来即可。涉及的知识及问题请求的数据如何分段分段完成后如何下载和下载完成后如何组装到一起暂停下载和继续下载的实现(wait()、notifyAll()、synchronized的...

weixin_30215717的博客 552
上一篇: Android核心技术-day05-04-JavaSE多线程下载
下一篇: Android核心技术-day06-01-显式意图和隐式意图
心灵泽尘
博客等级 码龄10年 32粉丝 83原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值