1、首先我们先创建好下载的位置–根据url创建文件。

  /*** <p>Title: FlieStorageManager</p >* <p>Description: TODO</p >* <p>Company: ihaveu</p >** @author MaWei* @date 2018/2/5*/
public class FlieStorageManager {public static FlieStorageManager sManager = new FlieStorageManager();private Context mContext;public static FlieStorageManager getInstance(){return sManager;}public void init(Context mContext){this.mContext = mContext;}public FlieStorageManager() {}/*** 根据url 设置文件名返回文件* @param url* @return*/public File getFileByName(String url){File parent;// 判断手机有没有挂载SD卡if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {// 通过Context.getExternalCacheDir()方法可以获取到 SDCard/Android/data/你的应用包名/cache/目录,一般存放临时缓存数据parent = mContext.getExternalCacheDir();}else {// 没有SD卡保存在系统目录的cache下parent = mContext.getCacheDir();}String fileName = Md5Uills.generateCode(url);// 路径、 文件名File file = new File(parent, fileName);if(!file.exists()) {try {file.createNewFile();} catch (IOException e) {e.printStackTrace();}}return file;}}

我们的路径很明显放在的cach目录下。

那么我们简单封装一下okhttp联网的框架的几个请求。
然后给大家看一下简单的单线程下载是怎么实现的

/*** <p>Title: HttpManager</p >* <p>Description: 联网请求工具类</p >* <p>Company: ihaveu</p >** @author MaWei* @date 2018/2/5*/
public class HttpManager {public static HttpManager mManager = new HttpManager();/** 请求失败*/public static final int NETWORK_ERROR_CODE = 1;/** 获取不到文件的总长度*/public static final int CONTENT_LENGTH_ERROR_CODE = 2;/** 队列中存在下载的任务*/public static final int TASK_RUNNING_ERROR_CODE = 3;/** okhttp请求类*/private OkHttpClient mClient;private Context mContext;private void init(Context mContext){this.mContext = mContext;}public HttpManager() {this.mClient = new OkHttpClient();}public static HttpManager getInstance(){return mManager;}/*** 同步请求** @param url* @return*/public Response syncRequest(String url) {Request request = new Request.Builder().url(url).build();try {return mClient.newCall(request).execute();} catch (IOException e) {e.printStackTrace();}return null;}/*** 同步请求(根据指定位置下载)* 根据请求头 Range字段 指定下载的位置*/public Response syncRequestByRange(String url, long start, long end) {Request request = new Request.Builder().url(url).addHeader("Range", "bytes=" + start + "-" + end).build();try {return mClient.newCall(request).execute();} catch (IOException e) {e.printStackTrace();}return null;}/*** 异步调用* @param url* @param callback*/public void asyncRequest(final String url, Callback callback) {Request request = new Request.Builder().url(url).build();mClient.newCall(request).enqueue(callback);}/*** 异步请求(单线程下载)*/public void asyncSingleThreadRequest(final String url, final DownLoadCallBack callback) {Request request = new Request.Builder().url(url).build();// okhttp的异步请求mClient.newCall(request).enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {}@Overridepublic void onResponse(Call call, Response response) throws IOException {if (!response.isSuccessful() && callback != null) {callback.fail(NETWORK_ERROR_CODE, "请求失败");}// 根据url创建一个文件File file = FlieStorageManager.getInstance().getFileByName(url);// 创建字节数组,以这个字节数组 读写byte[] buffer = new byte[1024 * 500];int len;FileOutputStream fileOut = new FileOutputStream(file);// 返回的response转成数据流InputStream inStream = response.body().byteStream();// 读取返回数据流 写到创建的文件中while ((len = inStream.read(buffer, 0, buffer.length)) != -1) {fileOut.write(buffer, 0, len);fileOut.flush();}// 写入完成callback.success(file);}});}
}

然后看一下我们的回调接口:

/*** <p>Title: DownLoadCallBack</p >* <p>Description: TODO</p >* <p>Company: ihaveu</p >** @author MaWei* @date 2018/2/5*/
public interface DownLoadCallBack {void success(File file);void fail(int errorCode, String errorMessage);void progress(int progress);
}

可以看出HttpManager 是我简单封好一个okhttp工具类。 这个工具类有同步请求的回调,异步的回调。

我还给大家简单谢了一个单线程下载的例子。就是,大家直接调用asyncSingleThreadRequest()这个方法,就是单线程的方法。里面的注释我也都写好了,很简单相信看一眼就明白了吧。

单线程下载的业务流程: 根据url创建好一个文件,然后在异步请求回调中通过响应的**response.body().byteSream(),转换的响应数据的流,然后通过流的形式写在我们文件中。

OK,我们继续下面操作。

我们接着创建一个多线程下载的管理类:

/*** <p>Title: DownloadManager</p >* <p>Description: 下载、线程管理类</p >* <p>Company: ihaveu</p >** @author MaWei* @date 2018/2/6*/
public class DownloadManager {private static DownloadManager mManager = new DownloadManager();/** 核心和最大线程数量*/private final static int MAX_THREAD = 2;/*** 创建线程池* 参数1:核心线程数量* 参数2:线程池最大数量* 参数3:线程存活时间* 参数4:设置时间等级* 参数5:先不管* 原理:线程池首先会创建核心线程, 如果在执行时,超过最大数量会抛出异常*/private static final ThreadPoolExecutor sThreadPool = new ThreadPoolExecutor(MAX_THREAD, MAX_THREAD, 60,TimeUnit.MICROSECONDS, new LinkedBlockingDeque<Runnable>(), new ThreadFactory() {private AtomicInteger mInteger = new AtomicInteger(1);@Overridepublic Thread newThread(Runnable runnable) {// 指定Runnable  和线程名称Thread mThread = new Thread(runnable, "download thread #" + mInteger.getAndIncrement());return mThread;}});public static DownloadManager getInstance(){return mManager;}public DownloadManager() {}/*** 判断每个线程下载多长的数据,并多线程下载*/public void downLoad(final String url, final DownLoadCallBack callBack){HttpManager.getInstance().asyncRequest(url, new Callback() {@Overridepublic void onFailure(Call call, IOException e) {}@Overridepublic void onResponse(Call call, Response response) throws IOException {if(response == null && callBack != null) {callBack.fail(HttpManager.NETWORK_ERROR_CODE, "网络出问题了");return;}long length = response.body().contentLength();if(length == -1) {// 获取不到文件的总长度callBack.fail(HttpManager.CONTENT_LENGTH_ERROR_CODE, "contenLength -1");return;}processDownload(url, length, callBack);}});}/*** 下载* @param length     下载文件的长度*/private void processDownload(String url, long length, DownLoadCallBack callBack) {// 计算每一个线程下载的大小long threadDownloadSize = length / MAX_THREAD;// 分配每一个线程下载for(int i = 0; i < MAX_THREAD; i++) {// 计算每一个线程从多少下载 比如长度100  2个线程  0-49  50-99, 下面是计算的算法long startSize = i * threadDownloadSize;long endSize = (i + 1) * threadDownloadSize - 1;// 执行下载sThreadPool.execute(new DownloadRunnable(startSize, endSize, url, callBack));}}
}

总结:首先创建一个核心线程线程、最大线程数量为2 的线程池来管理线程。
然后我们发起异步请求,先访问下载的文件,得到文件的总长度后,然后根据线程的数量平分每个线程下载的长度, 然后通过线程池下载文件。

OK,接下来看一下核心的线程类是怎么运行的。

/*** <p>Title: DownloadRunnable</p >* <p>Description: 下载执行的线程</p >* <p>Company: ihaveu</p >** @author MaWei* @date 2018/2/6*/
public class DownloadRunnable  implements Runnable{/** 指定下载开始位置*/private long mStart;/** 指定下载结束位置*/private long mEnd;/** 请求url*/private String mUrl;/** 结果回调*/private DownLoadCallBack mCallBack;public DownloadRunnable(long mStart, long mEnd, String mUrl, DownLoadCallBack mCallBack) {this.mStart = mStart;this.mEnd = mEnd;this.mUrl = mUrl;this.mCallBack = mCallBack;}@Overridepublic void run() {// 下载完成后返回的结果 responseResponse response = HttpManager.getInstance().syncRequestByRange(mUrl, mStart, mEnd);if(response == null && mCallBack != null) {mCallBack.fail(HttpManager.NETWORK_ERROR_CODE, "网络出问题了");return;}// 获取本地下载储存的文件File file = FlieStorageManager.getInstance().getFileByName(mUrl);// 多个线程对文件指定的位置写入数据(因为是多线程下载,多个线程肯定会对一个文件可读 可写 可修改)try {// 参数1:指定操作的文件  参数2:可读 可写 可修改RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rwd");// 指定一个偏移,下载的起始位置randomAccessFile.seek(mStart);byte[] buffer = new byte[1024];int len = 0;InputStream inputStream = response.body().byteStream();// 读取返回来的数据, 写入本地文件中while((len = inputStream.read(buffer, 0, buffer.length)) != -1) {randomAccessFile.write(buffer, 0, len);}// 下载成功mCallBack.success(file);} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}
}

这就是通过线程池运行的Runnable,看线程run方法中,是通过url创建一个文件,然后通过这个文件转换成RandomAccessFile文件, 目的是 “多个线程对文件指定的位置写入数据(因为是多线程下载,多个线程肯定会对一个文件可读 可写 可修改)” 然后这个文件通过seek方法,指定文件从什么位置开始下载。那么不同线程下载不同得位置就OK了。 但是需要提醒的是,多线程同时下载是同步操作。

OK那么多线程下载就完成了, 接下来我们只需要在你需要的地方直接调用就可以实现多线程下载了。
例子(多线程下载,下载完成显示在ImageView上):

   private void multipleDownFileImage() {DownloadManager.getInstance().downLoad("http://szimg.mukewang.com/5763765d0001352105400300-360-202.jpg", new DownLoadCallBack() {@Overridepublic void success(final File file) {Log.e("file", "file success: " + file.getAbsolutePath());Log.e("file", "file : " + file.length());runOnUiThread(new Runnable() {@Overridepublic void run() {Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());image.setImageBitmap(bitmap);}});}@Overridepublic void fail(int errorCode, String errorMessage) {}@Overridepublic void progress(int progress) {}});}

有需要直接copy代码就可以用,接下来我会给大家介绍一些优化的地方,和用数据库存储下载的数据,然后继续下载。 敬请期待!

Android 多线程下载文件原理霸气解析介绍 -----文件的下载(3)相关推荐

  1. java dom xml 换行,dom4j解析xml文件_用DOM解析XML文件,怎么才能让解析出来的文本不用换行_dom解析xml文件...

    网友求助:dom4j解析xml文件_用DOM解析XML文件,怎么才能让解析出来的文本不用换行_dom解析xml文件 问题importjava.text.SimpleDateFormat; import ...

  2. android str 字幕文件,视频显示字幕(解析srt文件)

    1.定义一个实体类 用于解析srt文件的内容 public class SRT { private int beginTime; private int endTime; private String ...

  3. python 解析pb文件_利用Python解析json文件

    写在前面 在金融风控领域,我们经常会使用到json格式的数据,例如运营商数据.第三方数据等.而这些数据往往不能直接作为结构化数据进行分析和建模.本文将介绍一种简单的.可复用性高的基于pandas的方法 ...

  4. 00截断上传绕过_【文件上传与解析】文件上传与解析漏洞总结v1.0

    点击上方"公众号" 可以订阅哦! Hello,各位小伙伴晚上好~ 这里是依旧勤劳写公众号的小编~ 今天本公众号将推出一个新的模块,那就是漏洞知识点总结模块!!!(此处应有掌声~) ...

  5. java在线打开xml文件_java实现简单解析XML文件功能示例

    本文实例讲述了java实现简单解析XML文件功能.分享给大家供大家参考,具体如下: package demo; import java.io.File; import java.io.IOExcept ...

  6. android 如何extends 多个,Android多线程:继承Thread类 使用解析(含实例教程)

    前言 在Android开发中,多线程的使用十分常见 今天,我将全面解析多线程其中一种常见用法:继承Thread类. 目录 示意图 1. 简介 示意图 2. 使用讲解 2.1 使用步骤 示意图 2.2 ...

  7. Android权限管理原理,Android手机Root授权原理细节全解析

    首先关于Root的方式,这里不做详解,可以有很多漏洞,比如利用uid溢出后归为0,得到Root权限,然后操作文件系统等. 手机Root后,最重要的是,给手机安装了su程序和superuser apk. ...

  8. 操作分布式文件之六:如何解析远程文件

    假设通过FttpAdapter已经读取到远程文件中一部分数据,如下: FttpAdapter fa = new FttpAdapter("fttp://10.232.20.151/home/ ...

  9. html中加载shp文件,运用shapefile.js解析Shp文件

    shapefile.open("http://localhost:8181/shp/zd.shp") .then(source => source.read() .then( ...

最新文章

  1. mqtt android简书,iOS MQTT协议笔记
  2. linux下makefile
  3. thinkphp删除某一学生_基于ThinkPHP实现批量删除
  4. linux6.5怎样安装vim,在Centos 6.5下成功安装和配置了vim7.4
  5. Janusec WAF网关安装体验
  6. 程序员实用算法 源码_程序员必须知道的十大基础实用算法综述
  7. matlab分析具体问题论文,matlab论文12010245327马文建.doc
  8. 杨东援:交通管控,从智能走向智慧
  9. 谷歌云服务器账号,免费使用谷歌云服务器一年
  10. 内涵一下我参加过的那些竞赛。
  11. 2021鹅厂产品管培生宣讲会干货总结
  12. pikachu~~~CSRF(get,post,token)
  13. C#时间格式转换问题(12小时制和24小时制)
  14. 计算机内区位码默认是几进制,汉字的区位码怎么转换成十六进制?
  15. Python——describe函数值输出含义/分位值的计算和含义
  16. iphone html阅读,iPhone如何使用Safari浏览器阅读列表功能
  17. 2021-12-02 迈向程序猿的第四十一步
  18. Android原生界面添加水印
  19. bdsup2sub java_蓝光原盘压制Remux和Rip、Re 图文实例教程
  20. typedef- -

热门文章

  1. 当我们再看在线直播课的时候,到底在看什么?
  2. 多种实用小工具集合多功能工具箱微信小程序源码 带流量主
  3. Kali 相关辑录一
  4. 二叉树两个节点最近公共祖先的解法
  5. 案例分享|智能客服、供应管理,深度学习为品牌电商创新服务
  6. 联想服务器不显示开机,安装打印机提示“打印后台程序服务没有启动”的解决方案...
  7. 批处理脚本自动发送浏览器收藏的书签给指定邮箱
  8. html表白earth,适合用来表白的英文情书
  9. HMAC的原理和应用
  10. edge浏览器启动很慢的解决方法,电脑配置很高