线程下载只需要确定好下载一个文件需要多少个线程,一般来说最好为3条线程,因为线程过多会占用系统资源,而且线程间的相互竞争也会导致下载变慢。

其次下载的时候将文件分割为三份(假设用3条线程下载)下载,在java中就要用到上次提到的RandomAccessFile这个API,它的开始结束为止用以下代码确定:

conn.setRequestProperty(“Range”, “bytes=” + start + “-” + end)

最后就是断点续传了,只需要才程序停止下载的时候记录下最后的下载位置就好了,当下次下载的时候从当前停止的位置开始下载。

MultiThreadActivity

在MultiThreadActivity 中创建listview展示列表,创建一个Receiver来接受下载进度并通知adapter来更新进度条

package com.bourne.httprequest.multiThreadDownload;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.ListView;
import android.widget.Toast;import com.bourne.httprequest.FileInfo;
import com.bourne.httprequest.R;import java.util.ArrayList;
import java.util.List;public class MultiThreadActivity extends AppCompatActivity {private ListView listView;private List<FileInfo> mFileList;private FileAdapter mAdapter;private String urlone = "http://dldir1.qq.com/qqmi/TencentVideo_V5.5.2.11955_848.apk";private String urltwo = "http://file.ws.126.net/opencourse/netease_open_androidphone.apk";private String urlthree = "http://downloads.jianshu.io/apps/haruki/JianShu-2.2.3-17040111.apk";private String urlfour = "http://codown.youdao.com/note/youdaonote_android_5.9.1_youdaoweb.apk";private UIRecive mRecive;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_multi_thread);// 初始化控件listView = (ListView) findViewById(R.id.list_view);mFileList = new ArrayList<FileInfo>();// 初始化文件对象FileInfo fileInfo1 = new FileInfo(0, urlone, getfileName(urlone), 0, 0);FileInfo fileInfo2 = new FileInfo(1, urltwo, getfileName(urltwo), 0, 0);FileInfo fileInfo3 = new FileInfo(2, urlthree, getfileName(urlthree), 0, 0);FileInfo fileInfo4 = new FileInfo(3, urlfour, getfileName(urlfour), 0, 0);mFileList.add(fileInfo1);mFileList.add(fileInfo2);mFileList.add(fileInfo3);mFileList.add(fileInfo4);mAdapter = new FileAdapter(this, mFileList);listView.setAdapter(mAdapter);mRecive = new UIRecive();//创建Receiver来接收下载进度,然后更新adapterIntentFilter intentFilter = new IntentFilter();intentFilter.addAction(DownloadService.ACTION_UPDATE);intentFilter.addAction(DownloadService.ACTION_FINISHED);intentFilter.addAction(DownloadService.ACTION_START);registerReceiver(mRecive, intentFilter);}@Overrideprotected void onDestroy() {unregisterReceiver(mRecive);super.onDestroy();}// 从URL地址中获取文件名,即/后面的字符private String getfileName(String url) {return url.substring(url.lastIndexOf("/") + 1);}// 从DownloadTadk中获取广播信息,更新进度条class UIRecive extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {if (DownloadService.ACTION_UPDATE.equals(intent.getAction())) {// 更新进度条的时候int finished = intent.getIntExtra("finished", 0);int id = intent.getIntExtra("id", 0);mAdapter.updataProgress(id, finished);} else if (DownloadService.ACTION_FINISHED.equals(intent.getAction())){// 下载结束的时候FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");mAdapter.updataProgress(fileInfo.getId(), 0);Toast.makeText(MultiThreadActivity.this, mFileList.get(fileInfo.getId()).getFileName() + "下载完毕", Toast.LENGTH_SHORT).show();} else if (DownloadService.ACTION_START.equals(intent.getAction())){}}}}

FileAdapter

点击开始和暂停的时候通知DownloadService

package com.bourne.httprequest.multiThreadDownload;import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;import com.bourne.httprequest.FileInfo;
import com.bourne.httprequest.R;import java.util.List;public class FileAdapter extends BaseAdapter {private Context mContext = null;private List<FileInfo> mFilelist = null;private LayoutInflater layoutInflater;public FileAdapter(Context mContext, List<FileInfo> mFilelist) {super();this.mContext = mContext;this.mFilelist = mFilelist;layoutInflater = LayoutInflater.from(mContext);}@Overridepublic int getCount() {return mFilelist.size();}@Overridepublic Object getItem(int position) {return mFilelist.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder viewHolder = null;final FileInfo mFileInfo = mFilelist.get(position);if(convertView == null) {convertView = layoutInflater.inflate(R.layout.item, null);viewHolder = new ViewHolder();viewHolder.textview = (TextView) convertView.findViewById(R.id.file_textview);viewHolder.startButton = (Button) convertView.findViewById(R.id.start_button);viewHolder.stopButton = (Button) convertView.findViewById(R.id.stop_button);viewHolder.progressBar = (ProgressBar) convertView.findViewById(R.id.progressBar2);viewHolder.textview.setText(mFileInfo.getFileName());viewHolder.progressBar.setMax(100);viewHolder.startButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent(mContext, DownloadService.class);intent.setAction(DownloadService.ACTION_START);intent.putExtra("fileInfo", mFileInfo);mContext.startService(intent);}});viewHolder.stopButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent(mContext, DownloadService.class);intent.setAction(DownloadService.ACTION_STOP);intent.putExtra("fileInfo", mFileInfo);mContext.startService(intent);}});convertView.setTag(viewHolder);} else{viewHolder = (ViewHolder) convertView.getTag();}viewHolder.progressBar.setProgress(mFileInfo.getFinished());return convertView;}/*** 更新列表中的进度条**/public void updataProgress(int id , int progress) {FileInfo info = mFilelist.get(id);info.setFinished(progress);notifyDataSetChanged();}static class ViewHolder{TextView textview;Button startButton;Button stopButton;ProgressBar progressBar;}}

DownloadService

在DownloadService创建一个InitThread来获取文件长度,获取完毕之后启动一个MultiDownloadTask来下载任务。

注意获取获取文件长度的InitThread不是直接new一个了,而是用线程池来进行操作

   InitThread initThread = new InitThread(fileInfo);MultiDownloadTask.sExecutorService.execute(initThread);

完整代码

package com.bourne.httprequest.multiThreadDownload;import android.app.Service;
import android.content.Intent;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;import com.bourne.httprequest.FileInfo;import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.Map;/*** 下载任务类,用于执行下载任务,并且将下载进度传到Activity中*/
public class DownloadService extends Service {public static final String ACTION_START = "ACTION_START";public static final String ACTION_STOP = "ACTION_STOP";public static final String ACTION_UPDATE = "ACTION_UPDATE";public static final String ACTION_FINISHED = "ACTION_FINISHED";// 文件的保存路径public static final String DownloadPath = Environment.getExternalStorageDirectory().getAbsolutePath()+ "/download/";public static final int MSG_INIT = 0;private Map<Integer, MultiDownloadTask> mTasks = new LinkedHashMap<Integer, MultiDownloadTask>();@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {super.onCreate();}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {// 获得Activity传来的参数if (ACTION_START.equals(intent.getAction())) {FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");Log.i("test", "START" + fileInfo.toString());InitThread initThread = new InitThread(fileInfo);MultiDownloadTask.sExecutorService.execute(initThread);} else if (ACTION_STOP.equals(intent.getAction())) {FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");MultiDownloadTask task = mTasks.get(fileInfo.getId());if (task != null) {// 停止下载任务task.mIsPause = true;}}return super.onStartCommand(intent, flags, startId);}// 从InitThread线程中获取FileInfo信息,然后开始下载任务Handler mHandler = new Handler() {public void handleMessage(android.os.Message msg) {switch (msg.what) {case MSG_INIT:FileInfo fileInfo = (FileInfo) msg.obj;Log.i("test", "INIT:" + fileInfo.toString());// 获取FileInfo对象,开始下载任务MultiDownloadTask task = new MultiDownloadTask(DownloadService.this, fileInfo, 3);task.download();// 把下载任务添加到集合中mTasks.put(fileInfo.getId(), task);// 发送启动下载的通知Intent intent = new Intent(ACTION_START);intent.putExtra("fileInfo", fileInfo);sendBroadcast(intent);break;}};};// 初始化下载进程,获得下载文件的信息class InitThread extends Thread {private FileInfo mFileInfo = null;public InitThread(FileInfo mFileInfo) {super();this.mFileInfo = mFileInfo;}@Overridepublic void run() {HttpURLConnection conn = null;RandomAccessFile raf = null;try {URL url = new URL(mFileInfo.getUrl());conn = (HttpURLConnection) url.openConnection();conn.setConnectTimeout(5 * 1000);conn.setRequestMethod("GET");int code = conn.getResponseCode();int length = -1;if (code == HttpURLConnection.HTTP_OK) {length = conn.getContentLength();}//如果文件长度为小于0,表示获取文件失败,直接返回if (length <= 0) {return;}// 判断下载的文件是否存在,不存在则创建File dir = new File(DownloadPath);if (!dir.exists()) {dir.mkdir();}// 创建本地文件File file = new File(dir, mFileInfo.getFileName());raf = new RandomAccessFile(file, "rwd");raf.setLength(length);// 设置文件长度mFileInfo.setLength(length);// 将FileInfo对象传给給HandlerMessage msg = Message.obtain();msg.obj = mFileInfo;msg.what = MSG_INIT;mHandler.sendMessage(msg);
//              msg.setTarget(mHandler);} catch (Exception e) {e.printStackTrace();} finally {if (conn != null) {conn.disconnect();}try {if (raf != null) {raf.close();}} catch (IOException e) {e.printStackTrace();}}super.run();}}}

MultiDownloadTask

package com.bourne.httprequest.multiThreadDownload;import android.content.Context;
import android.content.Intent;
import android.util.Log;import com.bourne.httprequest.FileInfo;
import com.bourne.httprequest.multiThreadDownload.db.MultiDAOImple;
import com.bourne.httprequest.multiThreadDownload.db.MultiThreadDAO;
import com.bourne.httprequest.ThreadInfo;import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class MultiDownloadTask {private Context mComtext = null;private FileInfo mFileInfo = null;private MultiThreadDAO mDao = null;private int mFinished = 0;private int mThreadCount = 1;public boolean mIsPause = false;private List<DownloadThread> mThreadlist = null;public static ExecutorService sExecutorService = Executors.newCachedThreadPool();public MultiDownloadTask(Context comtext, FileInfo fileInfo, int threadCount) {super();this.mThreadCount = threadCount;this.mComtext = comtext;this.mFileInfo = fileInfo;this.mDao = new MultiDAOImple(mComtext);}public void download() {// 从数据库中获取下载的信息List<ThreadInfo> list = mDao.queryThreads(mFileInfo.getUrl());if (list.size() == 0) {int length = mFileInfo.getLength();int block = length / mThreadCount;for (int i = 0; i < mThreadCount; i++) {// 划分每个线程开始下载和结束下载的位置int start = i * block;int end = (i + 1) * block - 1;if (i == mThreadCount - 1) {end = length - 1;}ThreadInfo threadInfo = new ThreadInfo(i, mFileInfo.getUrl(), start, end, 0);list.add(threadInfo);}}mThreadlist = new ArrayList<DownloadThread>();for (ThreadInfo info : list) {DownloadThread thread = new DownloadThread(info);
//          thread.start();// 使用线程池执行下载任务MultiDownloadTask.sExecutorService.execute(thread);mThreadlist.add(thread);// 如果数据库不存在下载信息,添加下载信息mDao.insertThread(info);}}public synchronized void checkAllFinished() {boolean allFinished = true;for (DownloadThread thread : mThreadlist) {if (!thread.isFinished) {allFinished = false;break;}}if (allFinished == true) {// 下載完成后,刪除数据库信息mDao.deleteThread(mFileInfo.getUrl());// 通知UI哪个线程完成下载Intent intent = new Intent(DownloadService.ACTION_FINISHED);intent.putExtra("fileInfo", mFileInfo);mComtext.sendBroadcast(intent);}}class DownloadThread extends Thread {private ThreadInfo threadInfo = null;// 标识线程是否执行完毕public boolean isFinished = false;public DownloadThread(ThreadInfo threadInfo) {this.threadInfo = threadInfo;}@Overridepublic void run() {HttpURLConnection conn = null;RandomAccessFile raf = null;InputStream is = null;try {URL url = new URL(mFileInfo.getUrl());conn = (HttpURLConnection) url.openConnection();conn.setConnectTimeout(5 * 1000);conn.setRequestMethod("GET");int start = threadInfo.getStart() + threadInfo.getFinished();// 设置下载文件开始到结束的位置conn.setRequestProperty("Range", "bytes=" + start + "-" + threadInfo.getEnd());File file = new File(DownloadService.DownloadPath, mFileInfo.getFileName());raf = new RandomAccessFile(file, "rwd");raf.seek(start);mFinished += threadInfo.getFinished();Intent intent = new Intent();intent.setAction(DownloadService.ACTION_UPDATE);int code = conn.getResponseCode();if (code == HttpURLConnection.HTTP_PARTIAL) {is = conn.getInputStream();byte[] bt = new byte[1024];int len = -1;// 定义UI刷新时间long time = System.currentTimeMillis();while ((len = is.read(bt)) != -1) {raf.write(bt, 0, len);// 累计整个文件完成进度mFinished += len;// 累加每个线程完成的进度threadInfo.setFinished(threadInfo.getFinished() + len);// 设置为500毫米更新一次if (System.currentTimeMillis() - time > 1000) {time = System.currentTimeMillis();// 发送已完成多少intent.putExtra("finished", mFinished * 100 / mFileInfo.getLength());// 表示正在下载文件的idintent.putExtra("id", mFileInfo.getId());Log.i("test", mFinished * 100 / mFileInfo.getLength() + "");// 发送广播給ActivitymComtext.sendBroadcast(intent);}if (mIsPause) {mDao.updateThread(threadInfo.getUrl(), threadInfo.getId(), threadInfo.getFinished());return;}}}// 标识线程是否执行完毕isFinished = true;// 判断是否所有线程都执行完毕checkAllFinished();} catch (Exception e) {e.printStackTrace();} finally {if (conn != null) {conn.disconnect();}try {if (is != null) {is.close();}if (raf != null) {raf.close();}} catch (IOException e) {e.printStackTrace();}}super.run();}}
}

全文的重点就在这个类了

1、用了一个线程池ExecutorService来管理所有的下载任务

// 一个没有限制最大线程数的线程池public static ExecutorService sExecutorService = Executors.newCachedThreadPool();

2、将文件的大小切割为几个等分,然后每个等分都开启一个线程来进行下载

public void download() {// 从数据库中获取下载的信息List<ThreadInfo> list = mDao.queryThreads(mFileInfo.getUrl());if (list.size() == 0) {int length = mFileInfo.getLength();int block = length / mThreadCount;for (int i = 0; i < mThreadCount; i++) {// 划分每个线程开始下载和结束下载的位置int start = i * block;int end = (i + 1) * block - 1;if (i == mThreadCount - 1) {end = length - 1;}ThreadInfo threadInfo = new ThreadInfo(i, mFileInfo.getUrl(), start, end, 0);list.add(threadInfo);}}mThreadlist = new ArrayList<DownloadThread>();for (ThreadInfo info : list) {DownloadThread thread = new DownloadThread(info);// 使用线程池执行下载任务MultiDownloadTask.sExecutorService.execute(thread);mThreadlist.add(thread);// 如果数据库不存在下载信息,添加下载信息mDao.insertThread(info);}}

《即拿即用-Android单线程断点下载》

《断点下载神器-RandomAccessFile》

《即拿即用-Android多线程断点下载》

完整代码地址

https://github.com/mocn26169/HttpRequest-Demo

参考

  • Android实战:多线程多文件断点续传下载+通知栏控制

即拿即用-Android多线程断点下载相关推荐

  1. android 多线程断点下载,listview 模式 开始 暂停等功能

    android 多线程断点下载,listview 模式 代码依次如下: 布局: <?xml version="1.0" encoding="utf-8"? ...

  2. Android 多线程断点下载demo实现

    先来一张效果图: 主要实现思路: 每一个下载都是通过RandomAccessFile对下载资源的总长进行切割之后,根据我们设置的线程多少进行计算之后开启多线程下载的.而每一个任务都是一个AsyncTa ...

  3. Android多线程断点下载

    到华为后,信息管理特别严格,文件不能外发.所以好久都没写博客了,今天周日,老婆非要我学习.就闲来无事,写一篇博客,呵呵-- 前段时间,项目中提到了断点下载apk并静默安装的需求.本打算用应用市场成熟的 ...

  4. Android 多线程断点下载(非原创)

    1.服务器的CPU分配给每条线程的时间片相同,服务器带宽平均分配给每条线程,所以客户端开启的线程越多,就能抢占到更多的服务器资源,这里在客户端开启多个线程来从服务器下载资源 2.fragment_ma ...

  5. android学习笔记---31_多线程断点下载器,下载原理实现

    1.1.31_多线程断点下载器 ----------------------- 1.软件界面:   文件下载路径              text框   button 下载   点击后,下面显示下载 ...

  6. 安卓客户端的多线程断点下载(SharedPreferences版)

    题记:从百度百科上面我们知道,SharedPreferences是不支持多线程的,但是这次使用SharedPreferences实现了多线程断点下载.点解? 服务器端: 使用的是tomcat服务器,在 ...

  7. iOS开发网络篇—多线程断点下载

    iOS开发网络篇-多线程断点下载 说明:本文介绍多线程断点下载.项目中使用了苹果自带的类,实现了同时开启多条线程下载一个较大的文件.因为实现过程较为复杂,所以下面贴出完整的代码. 实现思路:下载开始, ...

  8. Java多线程断点下载

    多线程下载已经提高了下载的效率,但是当一些特殊情况发生的时候,我们需要对程序进行处理,这样效率会更高.比如,断电断网等造成下载中断,那么我们下一次又要重新开始下载,这样效率底下,所以我们可以考虑使用断 ...

  9. android 多线程断点续传下载 三

    android 多线程断点续传下载 三 转载于:https://www.cnblogs.com/zhujiabin/p/5660093.html

最新文章

  1. 对每个 IP 访问量实时监控。
  2. mysql创建用户以及授权
  3. AtCoder AGC001E BBQ Hard (DP、组合计数)
  4. Event Tracing for Windows
  5. s5-14 链路状态路由选择
  6. 图像的灰度级和动态范围(转)
  7. node工程默认url_node 爬虫入门实例,简单易懂
  8. JAVA锁之可重入锁和递归锁及示例代码
  9. 命令关闭手机蓝牙_iPhone手机这些实用的便捷功能,你知道多少?
  10. 基于USB armory 制作一个USB恶意软件分析器
  11. Tushare 基础用法
  12. android 照片加水印,Android实现为图片添加水印
  13. 求正方形和长方形的个数
  14. 利用ADO连接数据库时,Rs.recordcount总是返回-1,解决办法
  15. 通过 ANE(Adobe Native Extension) 启动Andriod服务 推送消息(三)
  16. R语言技巧:读取excel文件
  17. python3 规则引擎_Ilog、Drools、Jess规则引擎的Rule Language 对比
  18. 数据可视化图表使用场景大全 !
  19. C语言——一个正整数如果等于组成它的各位数字的阶乘之和,该整数称为阶乘和数。
  20. 上海科技大学计算机本科2020,上海科技大学2020年本科招生简章

热门文章

  1. 南京市软件行业协会程序员分会(筹)会员的权利
  2. 使用Ajax实现简单的增删查改前端Ajax传的值,后端如何获取
  3. 一种用QT实现即时通信软件表情发送与接收的思路
  4. 收益率曲线形态的动力学:实证证据、经济学解释和理论基础
  5. [ActionScript3.0] AS3利用ExternalInterface与js通信
  6. 非三族非五族元素在硅和锗中的作用
  7. 哈密顿圈问题是NP完全的
  8. 区块链+战“疫”企业专访特辑之爱接力科技|链塔智库
  9. 2018双11倒计时 | 双11十年牧码梦
  10. 终于有人把智能制造与工业4.0讲明白了