多线程下载 HttpURLConnection
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程下载 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;
以上是关于多线程下载 HttpURLConnection的主要内容,如果未能解决你的问题,请参考以下文章