多线程下载 HttpURLConnection
Activity
/**实际开发涉及文件上传、下载都不会自己写这些代码,一般会使用第三方库(如xUtils)或Android提供的DownloadManager下载*/public class HttpDownloadActivity extends ListActivity {private TextView tv_info;private LinearLayout ll_pbs;public static final String PATH_URL_SMALL = "http://f2.market.xiaomi.com/download/AppStore/0b6c25446ea80095219649f646b8d67361b431127/com.wqk.wqk.apk";public static final String PATH_URL_BIG = "http://f3.market.xiaomi.com/download/AppChannel/099d2b4f6006a4c883059f459e0025a3e1f25454e/com.pokercity.bydrqp.mi.apk";public static final String PATH_FILE = Environment.getExternalStorageDirectory().getPath() + File.separator + "bqt_download" + File.separator;/**下载完毕后安装下载的APK*/public static final int MSG_WHAT_DOWNLOAD_OK = 1;/**下载过程中更新信息*/public static final int MSG_WHAT_DOWNLOAD_INFO = 2;@SuppressLint("HandlerLeak")private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_WHAT_DOWNLOAD_OK:Toast.makeText(HttpDownloadActivity.this, "下载完毕,请安装", Toast.LENGTH_SHORT).show();tv_info.append("\n路径为:" + (String) msg.obj);Intent intent = new Intent(Intent.ACTION_VIEW);intent.setDataAndType(Uri.parse("file://" + (String) msg.obj), "application/vnd.android.package-archive");startActivity(intent);break;case MSG_WHAT_DOWNLOAD_INFO:tv_info.append((String) msg.obj);break;}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);String[] array = { "使用HttpURLConnection单线程下载文件", "使用HttpURLConnection多线程下载文件", "使用开源框架下载文件" };tv_info = new TextView(this);tv_info.setTextColor(Color.BLUE);tv_info.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);tv_info.setPadding(20, 10, 20, 10);getListView().addFooterView(tv_info);ll_pbs = new LinearLayout(this);ll_pbs.setOrientation(LinearLayout.VERTICAL);getListView().addFooterView(ll_pbs);setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, new ArrayList<String>(Arrays.asList(array))));File directory = new File(PATH_FILE);if (!directory.exists()) directory.mkdirs();//必须有这一步}@Overrideprotected void onListItemClick(ListView l, View v, int position, long id) {switch (position) {case 0://使用HttpURLConnection单线程下载文件tv_info.setText("下载过程信息:");new Thread() {@Overridepublic void run() {HttpDownloadFilesUtils.simpleDownLoad(PATH_URL_SMALL, PATH_FILE, false, mHandler);}}.start();break;case 1://使用HttpURLConnection多线程下载文件tv_info.setText("下载过程信息:");ll_pbs.removeAllViews();//清空掉旧的进度条final ArrayList<ProgressBar> pbs = new ArrayList<ProgressBar>();//ProgressBar、SeekBar、ProgressDialog 这些都是可以在子线程直接更新进度的for (int j = 0; j < HttpDownloadFilesUtils.THREAD_COUNT; j++) {ProgressBar progressBar = new ProgressBar(this, null, android.R.attr.progressBarStyleHorizontal);ll_pbs.addView(progressBar);//添加到布局中pbs.add(progressBar);//添加到集合中}new Thread() {@Overridepublic void run() {HttpDownloadFilesUtils.mutileThreadDownload(PATH_URL_BIG, PATH_FILE, false, mHandler, pbs);}}.start();break;case 2:Toast.makeText(this, "请引用第三个库或jar包后自行测试", Toast.LENGTH_SHORT).show();break;}}}
工具类
/** 下传文件工具类*/public class HttpDownloadFilesUtils {/**直接使用URLConnection.openStream()打开网络输入流,然后将流写入到文件中*/public static void simpleDownLoad(String fileUrl, String filePath, boolean isUseUrlName, Handler mHandler) {String fileName;if (isUseUrlName) fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1);//截取文件名及后缀名else fileName = new SimpleDateFormat("yyyy.MM.dd HH-mm-ss", Locale.CHINA).format(new Date()) + fileUrl.substring(fileUrl.lastIndexOf("."));fileName = fileName == null ? "bqt" : fileName;try {InputStream inputStream = new URL(fileUrl).openStream();OutputStream outputStream = new FileOutputStream(new File(filePath + fileName));byte[] buffer = new byte[1024];int len = 0;while ((len = inputStream.read(buffer)) > 0) {outputStream.write(buffer, 0, len);}inputStream.close();outputStream.close();mHandler.sendMessage(Message.obtain(mHandler, HttpDownloadActivity.MSG_WHAT_DOWNLOAD_OK, filePath + fileName));} catch (MalformedURLException e) {e.printStackTrace();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}//*****************************************************************************************************************// 多线程下载//*****************************************************************************************************************public static final int THREAD_COUNT = 3;// 线程的数量public static long blocksize;// 每个下载区块的大小public static int runningTHREAD_COUNT;// 正在运行的线程的数量/*** 多线程下载* 方式1:使用多线程分别下载文件的不同部分,最后把【合并】成一个文件(效率高)。方式2:使用java提供的【RandomAccessFile】类实现多线程的下载(简单)。* @param fileUrl 服务器路径* @param filePath 保存本地路径* @param isUseUrlName 是否使用服务器路径中的文件名,设为false则使用当前时间作为文件名* @param mHandler 通过mHandler和UI线程通讯* @param pbs 在子线程直接更新各线程下载进度,设为null则不考虑*/public static void mutileThreadDownload(String fileUrl, String filePath, boolean isUseUrlName, Handler mHandler, ArrayList<ProgressBar> pbs) {try {HttpURLConnection conn = (HttpURLConnection) new URL(fileUrl).openConnection();//获取连接conn.setRequestMethod("GET");conn.setConnectTimeout(5000);if (conn.getResponseCode() == 200) {// 1、在本地创建一个大小跟服务器一模一样的空白文件long fileSize = conn.getContentLength();// 得到服务端文件的大小String info = "\n服务端文件的大小:" + fileSize + " ( " + fileSize / 1024 / 1024 + "M )";mHandler.sendMessage(Message.obtain(mHandler, HttpDownloadActivity.MSG_WHAT_DOWNLOAD_INFO, info));String fileName;if (isUseUrlName) fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1);//截取文件名及后缀名else fileName = new SimpleDateFormat("yyyy.MM.dd HH-mm-ss", Locale.CHINA).format(new Date()) + fileUrl.substring(fileUrl.lastIndexOf("."));fileName = fileName == null ? "bqt" : fileName;RandomAccessFile raf = new RandomAccessFile(filePath + fileName, "rw");//可以从指定位置开始读、写文件;模式:r、rw、rws、rwdraf.setLength(fileSize);//设定大小raf.close();// 2、开启若干个子线程分别去下载对应的资源blocksize = fileSize / THREAD_COUNT; // 每个下载区块的大小runningTHREAD_COUNT = THREAD_COUNT; //运行的线程数量for (int i = 1; i <= THREAD_COUNT; i++) {long startIndex = (i - 1) * blocksize;//开始位置,从0开始long endIndex = i * blocksize - 1;//结束位置if (i == THREAD_COUNT) endIndex = fileSize - 1;// 最后一个线程的结束位置为 size - 1,若值比它大,也不会有异常,实际下载大小也是 size - 1info = "\n开启线程 " + i + " ,下载范围:" + startIndex + "~" + endIndex;mHandler.sendMessage(Message.obtain(mHandler, HttpDownloadActivity.MSG_WHAT_DOWNLOAD_INFO, info));//ProgressBar、SeekBar、ProgressDialog 这些都是可以在子线程直接更新进度的if (pbs != null && pbs.get(i - 1) != null) pbs.get(i - 1).setMax((int) (endIndex - startIndex));//设置各个线程的进度条的最大值//3、调用下面的逻辑完成多线程下载new DownloadThread(fileUrl, filePath + fileName, i, startIndex, endIndex, mHandler, pbs).start();}}conn.disconnect();//取消连接} catch (MalformedURLException e) {e.printStackTrace();} catch (ProtocolException e) {e.printStackTrace();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}private static class DownloadThread extends Thread {//线程Thread的子类private String fileUrl;private String filePath;private int threadId;private long startIndex;private long endIndex;private Handler mHandler;private ArrayList<ProgressBar> pbs;/**定义一个记录当前线程已下载文件的大小的临时文件,若文件不存在则从头下载,否则从记录的位置继续下载;写入时则将其封装为RandomAccessFile*/private File positionFile;/**** @param fileUrl 服务器路径* @param filePath 缓存文件保存路径* @param threadId 线程id,请使用0、1、2、3……形式,并请按顺序命名* @param startIndex 当前线程开始下载的位置* @param endIndex 当前线程结束下载的位置* @param mHandler 通过mHandler和UI线程通讯* @param pbs 在子线程直接更新各线程下载进度,设为null则不考虑*/public DownloadThread(String fileUrl, String filePath, int threadId, long startIndex, long endIndex, Handler mHandler, ArrayList<ProgressBar> pbs) {this.fileUrl = fileUrl;this.filePath = filePath;this.threadId = threadId;this.startIndex = startIndex;this.endIndex = endIndex;this.mHandler = mHandler;this.pbs = pbs;}public void run() {String info;try {// 1、记录当前线程已下载的总大小int total = 0;// 初始值设为0,若已下载部分文件,则获取已下载部分文件的大小并重新赋值positionFile = new File(filePath + "-" + threadId);if (positionFile.exists() && positionFile.length() > 0) {BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(positionFile)));String totalstr = bufferedReader.readLine();// 获取当前线程上次下载的总【大小】是多少total = Integer.valueOf(totalstr);info = "\n上次线程" + threadId + "下载的总大小:" + total;mHandler.sendMessage(Message.obtain(mHandler, HttpDownloadActivity.MSG_WHAT_DOWNLOAD_INFO, info));startIndex += total;//每个线程继续下载时的开始【位置】注意startIndex的值为开始下载的【位置】,是一个索引;total的值为实际下载的文件的【大小】bufferedReader.close();}//2、获取连接,设置连接的参数信息HttpURLConnection conn = (HttpURLConnection) new URL(fileUrl).openConnection();conn.setRequestMethod("GET");conn.setConnectTimeout(5000);conn.setRequestProperty("RANGE", "bytes=" + startIndex + "-" + endIndex);//指定每条线程从文件的什么位置开始下载,下载到什么位置为止// 注意,下载服务器中某一部分内容返回码是206,可以用【code/100==2】来判断InputStream inputStream = conn.getInputStream();//返回服务端返回的流//3、将流中的数据写入文件中RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "rw");randomAccessFile.seek(startIndex);// 指定文件开始写的位置(指针偏移量)。info = "\n第 " + threadId + " 个线程:写文件的开始位置:" + String.valueOf(startIndex);mHandler.sendMessage(Message.obtain(mHandler, HttpDownloadActivity.MSG_WHAT_DOWNLOAD_INFO, info));int len = 0;byte[] buffer = new byte[1024];while ((len = inputStream.read(buffer)) != -1) {randomAccessFile.write(buffer, 0, len);//向randomAccessFile中写入读取到的流中的内容//将此文件封装成为一个RandomAccessFile,并采用采用rwd模式,即使断电也不会丢失信息!RandomAccessFile rf = new RandomAccessFile(positionFile, "rwd");total += len;//记录下载的总大小rf.write(String.valueOf(total).getBytes());rf.close();if (pbs != null && pbs.get(threadId - 1) != null) pbs.get(threadId - 1).setProgress(total);//设置当前进度条的进程值}inputStream.close();randomAccessFile.close();} catch (Exception e) {} finally {//4、 所有的线程都下载完毕后删除记录文件synchronized (HttpDownloadFilesUtils.class) {//线程安全问题info = "\n线程 " + threadId + " 下载完毕了";mHandler.sendMessage(Message.obtain(mHandler, HttpDownloadActivity.MSG_WHAT_DOWNLOAD_INFO, info));runningTHREAD_COUNT--;if (runningTHREAD_COUNT < 1) {info = "\n所有线程已下载完毕,删除临时文件";mHandler.sendMessage(Message.obtain(mHandler, HttpDownloadActivity.MSG_WHAT_DOWNLOAD_INFO, info));for (int i = 1; i <= THREAD_COUNT; i++) {File temFile = new File(filePath + "-" + i);info = "\n删除临时文件 " + i + " ,状态: " + temFile.delete();//删除记录文件mHandler.sendMessage(Message.obtain(mHandler, HttpDownloadActivity.MSG_WHAT_DOWNLOAD_INFO, info));}mHandler.sendMessage(Message.obtain(mHandler, HttpDownloadActivity.MSG_WHAT_DOWNLOAD_OK, filePath));}}}}}}
多线程下载 HttpURLConnection的更多相关文章
- 使用HttpURLConnection实现多线程下载
HttpURLConnection继承了URLConnection,因此也可用于向指定网站发送GET请求.POST请求,而且它在URLConnection基础上提供了如下便捷方法: 实现多线程下载的步 ...
- java 多线程下载文件 以及URLConnection和HttpURLConnection的区别
使用 HttpURLConnection 实现多线程下载文件 注意GET大写//http public class MultiThreadDownload { public static void m ...
- 使用HttpURLConnection多线程下载
1 import java.io.IOException; 2 import java.io.InputStream; 3 import java.io.RandomAccessFile; 4 imp ...
- 【Java EE 学习 22 下】【单线程下载】【单线程断点下载】【多线程下载】
一.文件下载简述 1.使用浏览器从网页上下载文件,Servlet需要增加一些响应头信息 (1)response.setContentType("application/force-downl ...
- Java--使用多线程下载,断点续传技术原理(RandomAccessFile)
一.基础知识 1.什么是线程?什么是进程?它们之间的关系? 可以参考之前的一篇文章:java核心知识点学习----并发和并行的区别,进程和线程的区别,如何创建线程和线程的四种状态,什么是线程计时器 简 ...
- android 多线程下载 断点续传
来源:网易云课堂Android极客班第八次作业练习 练习内容: 多线程 asyncTask handler 多线程下载的原理 首先获取到目标文件的大小,然后在磁盘上申请一块空间用于保存目标文件,接着把 ...
- 无废话Android之smartimageview使用、android多线程下载、显式意图激活另外一个activity,检查网络是否可用定位到网络的位置、隐式意图激活另外一个activity、隐式意图的配置,自定义隐式意图、在不同activity之间数据传递(5)
1.smartimageview使用 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android&q ...
- Java 仿迅雷多线程下载
package net.webjoy.jackluo.android_json; /** * 1.http Range "bytes="+ start+end * 2.Random ...
- android程序---->android多线程下载(一)
多线程下载是加快下载速度的一种方式,通过开启多个线程去执行一个任务,可以使任务的执行速度变快.多线程的任务下载时常都会使用得到断点续传下载,就是我们在一次下载未结束时退出下载,第二次下载时会接着第一次 ...
随机推荐
- ubuntu下 使用AB做压力测试
1最近刚开始接触apache大数据下数据优化,讲一下apache 下ab压力测试工具. 程序“ab”尚未安装. 您可以使用以下命令安装: apt-get install apache2-utils 以 ...
- 【结构型】Flyweight模式
享元模式的主要目的.意图是为对象的大量使用提供一种共享机制.该模式的思想重在复用.共享复用.像文字.列表中的格子等这类多数都是需要考虑复用技术,否则必将大量耗费内存空间而使资源以及性能等大量耗费.该模 ...
- PreparedStatement 和 Statment区别
PreparedStatement vs Statment 1)语法不同:PreparedStatement可以使用预编译的sql,而Statment只能使用静态的sql 2)效率不同: Prepar ...
- mac 常用的开发工具
http://www.oschina.net/news/53946/mac-dev-tools 要清楚的认识到,我们寻找的不是开始按钮,而是程序入口,任何一个操作系统,用户要做的事情并不是找到开始菜单 ...
- oldboy第二天学习
一.上课体验及感受 第二天上课了,从循环到队列,感觉都可以接受,但是当循环遇到队列之后感觉脑袋就有点不够用了.不知道是因为萌新的问题.每个人都这样,还是个人能力不行.总而言之加油努力吧!! 二.循环, ...
- HEX和BIN文件的区别
以下的内容是从网上转载来的,原文地址:http://blog.csdn.net/zhangliang_571/article/details/8519469 在这里感谢原作者. 1,是在keil中编 ...
- [LeetCode 112 113] - 路径和I & II (Path Sum I & II)
问题 给出一棵二叉树及一个和值,检查该树是否存在一条根到叶子的路径,该路径经过的所有节点值的和等于给出的和值. 例如, 给出以下二叉树及和值22: 5 / \ 4 8 ...
- virtualbox端口转发
端口转发:setting->network->adapter:attached to NAT.port forwarding rules->name protocol ...
- MFC界面更新实现方法
1.更新窗口 即采用UpdateWindow()函数立即发送WM_PAINT消息更新整个窗口. void CEditTestDlg::OnBnClickedBtnSysUpdate() { CStri ...
- JavaScript方法undefined/null原因探究及闭包简单实现
昨天一个刚写前端不久的同学发消息问这个问题(如下图): HTML代码为(省略部分代码): <head> <script src="test.js">< ...