当前位置:

首页 > 系统应用 > 7.3.2 Android 文件下载(1)

7.3.2 Android 文件下载(1)

本文目录

    本节引言: 这又是一个很容易踩坑的主题,刚入门的同学建议先有点心理准备……这一节会按顺序展开:先看普通的单线程下载,再过渡到普通多线程下载,接着再往下讲;另外,还会带上一个特别实用的例子:利用Android那只DownloadManager完成apk更新并覆盖安装的实现代码。这样一看,这一节其实还挺

    本节引言:

    这又是一个很容易踩坑的主题,刚入门的同学建议先有点心理准备……这一节会按顺序展开:先看普通的单线程下载,再过渡到普通多线程下载,接着再往下讲;另外,还会带上一个特别实用的例子:利用Android那只DownloadManager完成apk更新并覆盖安装的实现代码。这样一看,这一节其实还挺有意思,下面就正式开始。PS:完整的多线程断点续传会放到下一节单独讲!

    1.普通单线程下载文件:

    直接使用URLConnection.openStream()打开网络输入流,然后将流写入到文件中!

    核心方法:

    public static void downLoad(String path,Context context)throws Exception
    {
      URL url = new URL(path);
      InputStream is = url.openStream();
      //截取最后的文件名
      String end = path.substring(path.lastIndexOf("."));
      //打开手机对应的输出流,输出到文件中
      OutputStream os = context.openFileOutput("Cache_"+System.currentTimeMillis()+end, Context.MODE_PRIVATE);
      byte[] buffer = new byte[1024];
      int len = 0;
      //从输入六中读取数据,读到缓冲区中
      while((len = is.read(buffer)) > 0)
      {
        os.write(buffer,0,len);
      }
      //关闭输入输出流
      is.close();
      os.close();
    }

    运行结果:

    2.普通多线程下载:

    我们都知道使用多线程下载文件可以更快地完成文件的下载,但是为什么呢?

    答:因为抢占的服务器资源多,假设服务器最多服务100个用户,服务器中的一个线程 对应一个用户100条线程在计算机中并发执行,由CPU划分时间片轮流执行,加入a有99条线程 下载文件,那么相当于占用了99个用户资源,自然就有用较快的下载速度

    PS:当然不是线程越多就越好,开启过多线程的话,app需要维护和同步每条线程的开销, 这些开销反而会导致下载速度的降低,另外还和你的网速有关!

    多线程下载的流程:

    • 获取网络连接
    • 本地磁盘创建相同大小的空文件
    • 计算每条线程需从文件哪个部分开始下载,结束
    • 依次创建,启动多条线程来下载网络资源的指定部分

    PS:这里直接创建一个Ja va项目,然后在JUnit里运行指定方法即可,

    核心代码如下:

    public class Downloader {
      //添加@Test标记是表示该方法是Junit测试的方法,就可以直接运行该方法了
        @Test
        public void download() throws Exception
        {
          //设置URL的地址和下载后的文件名
          String filename = "meitu.exe";
          String path = "http://10.13.20.32:8080/Test/XiuXiu_Green.exe";
          URL url = new URL(path);
          HttpURLConnection conn = (HttpURLConnection) url.openConnection();
          conn.setConnectTimeout(5000);
          conn.setRequestMethod("GET");
          //获得需要下载的文件的长度(大小)
          int filelength = conn.getContentLength();
          System.out.println("要下载的文件长度"+filelength);
          //生成一个大小相同的本地文件
          RandomAccessFile file = new RandomAccessFile(filename, "rwd");
          file.setLength(filelength);
          file.close();
          conn.disconnect();
          //设置有多少条线程下载
          int threadsize = 3;
          //计算每个线程下载的量
          int threadlength = filelength % 3 == 0 ? filelength/3:filelength+1;
          for(int i = 0;i < threadsize;i++)
          {
            //设置每条线程从哪个位置开始下载
            int startposition = i * threadlength;
            //从文件的什么位置开始写入数据
            RandomAccessFile threadfile = new RandomAccessFile(filename, "rwd");
            threadfile.seek(startposition);
            //启动三条线程分别从startposition位置开始下载文件
            new DownLoadThread(i,startposition,threadfile,threadlength,path).start();
          }
          int quit = System.in.read();
          while('q' != quit)
          {
            Thread.sleep(2000);
          }
        }
    private class DownLoadThread extends Thread {
        private int threadid;
        private int startposition;
        private RandomAccessFile threadfile;
        private int threadlength;
        private String path;
        public DownLoadThread(int threadid, int startposition,
            RandomAccessFile threadfile, int threadlength, String path) {
          this.threadid = threadid;
          this.startposition = startposition;
          this.threadfile = threadfile;
          this.threadlength = threadlength;
          this.path = path;
        }
        public DownLoadThread() {}
        @Override
        public void run() {
          try
          {
            URL url = new URL(path);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(5000);
            conn.setRequestMethod("GET");
            //指定从什么位置开始下载
            conn.setRequestProperty("Range", "bytes="+startposition+"-");
            //System.out.println(conn.getResponseCode());
            if(conn.getResponseCode() == 206)
            {
              InputStream is = conn.getInputStream();
              byte[] buffer = new byte[1024];
              int len = -1;
              int length = 0;
              while(length < threadlength && (len = is.read(buffer)) != -1)
              {
                threadfile.write(buffer,0,len);
                //计算累计下载的长度
                length += len;
              }
              threadfile.close();
              is.close();
              System.out.println("线程"+(threadid+1) + "已下载完成");
            }
          }catch(Exception ex){System.out.println("线程"+(threadid+1) + "下载出错"+ ex);}
        }
    }
    }

    运行截图:

    如图,使用多线程完成了对文件的下载!双击exe文件可运行,说明文件并没有损坏!

    注意事项:

    • int filelength = conn.getContentLength(); //获得下载文件的长度(大小)
    • RandomAccessFile file = new RandomAccessFile(filename, "rwd"); //该类运行对文件进行读写,是多线程下载的核心
    • nt threadlength = filelength % 3 == 0 ? filelength/3:filelength+1; //计算每个线程要下载的量
    • conn.setRequestProperty("Range", "bytes="+startposition+"-"); //指定从哪个位置开始读写,这个是URLConnection提供的方法
    • //System.out.println(conn.getResponseCode()); //这个注释了的代码是用来查看conn的返回码的,我们前面用的都是200, 而针对多线程的话,通常是206,必要时我们可以通过调用该方法查看返回码!
    • int quit = System.in.read();while('q' != quit){Thread.sleep(2000);} //这段代码是做延时操作的,因为我们用的是本地下载,可能该方法运行完了,而我们的 线程还没有开启,这样会引发异常,这里的话,让用户输入一个字符,如果是'q'的话就退出

    3.使用DownloadManager更新应用并覆盖安装:

    下面的代码可以直接用,加入到项目后,记得为这个内部广播注册一个过滤器:

    AndroidManifest.xml

    :
    import android.app.DownloadManager;
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.content.pm.ApplicationInfo;
    import android.content.pm.PackageManager;
    import android.database.Cursor;
    import android.net.Uri;
    import android.os.Bundle;
    import android.os.Environment;
    import android.support.v7.app.AppCompatActivity;
    /**
     * Created by Jay on 2015/9/9 0009.
     */
    public class UpdateAct extends AppCompatActivity {
        //这个更新的APK的版本部分,我们是这样命名的:xxx_v1.0.0_xxxxxxxxx.apk
        //这里我们用的是git提交版本的前九位作为表示
        private static final String FILE_NAME = "ABCDEFGHI";
    @Override
        public void onCreate(Bundle sa vedInstanceState) {
            super.onCreate(sa vedInstanceState);
            String endpoint = "";
            try {
                //这部分是获取AndroidManifest.xml里的配置信息的,包名,以及Meta_data里保存的东西
                ApplicationInfo info = getPackageManager().getApplicationInfo(
                        getPackageName(), PackageManager.GET_META_DATA);
                //我们在meta_data保存了xxx.xxx这样一个数据,是https开头的一个链接,这里替换成http
                endpoint = info.metaData.getString("xxxx.xxxx").replace("https",
                        "http");
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            //下面的都是拼接apk更新下载url的,path是保存的文件夹路径
            final String _Path = this.getIntent().getStringExtra("path");
            final String _Url = endpoint + _Path;
            final DownloadManager _DownloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
            DownloadManager.Request _Request = new DownloadManager.Request(
                    Uri.parse(_Url));
            _Request.setDestinationInExternalPublicDir(
                    Environment.DIRECTORY_DOWNLOADS, FILE_NAME + ".apk");
            _Request.setTitle(this.getString(R.string.app_name));
            //是否显示下载对话框
            _Request.setShowRunningNotification(true);
            _Request.setMimeType("application/com.trinea.download.file");
            //将下载请求放入队列
            _DownloadManager.enqueue(_Request);
            this.finish();
        }
    //注册一个广播接收器,当下载完毕后会收到一个android.intent.action.DOWNLOAD_COMPLETE
        //的广播,在这里取出队列里下载任务,进行安装
        public static class Receiver extends BroadcastReceiver {
            public void onReceive(Context context, Intent intent) {
                final DownloadManager _DownloadManager = (DownloadManager) context
                        .getSystemService(Context.DOWNLOAD_SERVICE);
                final long _DownloadId = intent.getLongExtra(
                        DownloadManager.EXTRA_DOWNLOAD_ID, 0);
                final DownloadManager.Query _Query = new DownloadManager.Query();
                _Query.setFilterById(_DownloadId);
                final Cursor _Cursor = _DownloadManager.query(_Query);
                if (_Cursor.moveToFirst()) {
                    final int _Status = _Cursor.getInt(_Cursor
                            .getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS));
                    final String _Name = _Cursor.getString(_Cursor
                            .getColumnIndexOrThrow("local_filename"));
                    if (_Status == DownloadManager.STATUS_SUCCESSFUL
                            && _Name.indexOf(FILE_NAME) != 0) {
    Intent _Intent = new Intent(Intent.ACTION_VIEW);
                        _Intent.setDataAndType(
                                Uri.parse(_Cursor.getString(_Cursor
                                        .getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_URI))),
                                "application/vnd.android.package-archive");
                        _Intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        context.startActivity(_Intent);
                    }
                }
                _Cursor.close();
            }
        }
    }

    4.参考代码下载:

    普通单线程下载文件:DownLoadDemo1.zip 普通多线程下载文件:J2SEMulDownLoader.zip

    本节小结:

    好的,本节给大家介绍了普通单线程以及多线程下载文件,还有利用Android自带DownManager来 下载更新APK,然后覆盖的实现!相信会对大家的实际开发带来便利,好的,就说这么多,谢谢~

    本文内容来源于网友投稿,如有侵权请联系删除。
    作者最新文章
    系统应用 Android
    相关文章 更多
    XMind iOS版与安卓版功能有什么区别?
    XMind iOS版与安卓版功能有什么区别?

    XMind iOS版和安卓版都能完成完整的思维导图编辑,但在文件保存、导出格式及系统分享流程上存在差异。本文详解两端在实际使用中的不同点,帮助跨平台用户优化工作流。

    微软官网下载Win10 64位官方ISO镜像的完整步骤
    微软官网下载Win10 64位官方ISO镜像的完整步骤

    详解如何从微软官网获取纯净的Windows 10 64位ISO镜像文件,包括正确的下载入口、版本与语言选择技巧,以及下载后的完整性校验步骤,确保系统安装源安全可靠。

    Linux如何开启端口号?开放端口命令详解
    Linux如何开启端口号?开放端口命令详解

    详细讲解Linux如何开启端口号以及常用的开放端口命令。

    docker容器自动重启怎么解决 排查原因与修复方法
    docker容器自动重启怎么解决 排查原因与修复方法

    遇到Docker容器频繁自动重启?本文提供从查看容器日志、解读退出代码到配置Restart Policy的完整排查与修复方案,确保服务稳定性。

    Linux设置静态路由命令与永久生效教程
    Linux设置静态路由命令与永久生效教程

    学习如何在Linux中使用命令行添加临时静态路由,并通过修改网络配置文件实现重启后依然有效的永久静态路由设置,涵盖CentOS和Ubuntu的不同配置方法。

    摄像头连接电脑后怎么打开查看实时画面教程
    摄像头连接电脑后怎么打开查看实时画面教程

    摄像头连接电脑后不知如何查看画面?本教程演示如何使用Windows自带的“相机”应用快速调取实时视频流,无需下载额外软件,步骤简单直观,适用于大多数USB及内置摄像头。

    如何在 windows 11 关闭自动关机
    如何在 windows 11 关闭自动关机

    针对 Windows 11 电脑在无人操作时自动关机或重启的问题,本文详解如何通过任务计划程序禁用更新重启任务,并调整电源睡眠策略,从根源解决非预期的自动关机现象。

    android页面跳转屏幕方向改变变量如何解决
    android页面跳转屏幕方向改变变量如何解决

    详解Android应用中处理屏幕旋转导致Activity重建时的变量保存方法,包括使用onSaveInstanceState捆绑包和Jetpack ViewModel架构组件的最佳实践。

    电脑摄像头怎么测试人脸识别功能及效果判断方法
    电脑摄像头怎么测试人脸识别功能及效果判断方法

    详细讲解在Windows电脑上测试摄像头人脸识别功能的步骤,包括开启Windows Hello、录入面部数据、不同环境下的效果验证及常见问题排查,帮助用户确认设备兼容性与识别准确率。

    docker 容器无法启动的配置检查命令及排查方法
    docker 容器无法启动的配置检查命令及排查方法

    当 Docker 容器启动后立即退出时,不要盲目重启。本文详解如何通过 docker logs 查看标准输出错误,利用 docker inspect 检查挂载与网络配置,并修正入口点脚本权限问题,提供一套高效的因果排查流程。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    macOS软件
    macOS软件

    正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

    Mac软件 更多
    photoshop
    photoshop
    Windows、macOS 、 iPad

    Photoshop 2026 是 Adobe 推出的专业图像处理与视觉设计软件,支持 Windows、macOS 和 iPad 等平台,广泛应用于摄影修图、电商设计、平面海报、数字绘画及视觉合成等创作场景。

    Blender
    Blender
    Windows、macOS 和 Linux

    Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。

    灵活计算器
    灵活计算器
    macOS/iOS/Android

    灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

    WINDOWS 更多
    3dmax(3ds max)
    3dmax(3ds max)
    Windows

    Autodesk 3ds Max 是一款专业的三维建模、动画与渲染软件,广泛应用于建筑可视化、游戏开发、影视动画、广告设计和产品展示等领域。

    photoshop
    photoshop
    Windows、macOS 、 iPad

    Photoshop 2026 是 Adobe 推出的专业图像处理与视觉设计软件,支持 Windows、macOS 和 iPad 等平台,广泛应用于摄影修图、电商设计、平面海报、数字绘画及视觉合成等创作场景。

    Blender
    Blender
    Windows、macOS 和 Linux

    Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。