使用多线程下载同一个文件

理解:将服务器的一个资源分成多个部分,每个线程负责下载其中一个部分,然后设置每个线程对同一个文件开始写入的位置,当所有线程下载完成后,本地文件便是一个完整的文件了。

 

需要的权限:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

 

activity_main.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <Button
        android:id="@+id/download"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="下载" />

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true" />

</RelativeLayout>


MainActivity.java :

package com.xie.app;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.Toast;

public class MainActivity extends Activity implements OnClickListener {

	// 下载按钮
	private Button download;
	// 下载进度条
	private ProgressBar progress;
	// 所有下载任务
	private List<MyDownloadTask> tasks;

	/** 下载地址 */
	public static final String URL_DOWNLOAD = "http://192.168.1.104:8080/WebServer/music/handin.ape";
	/** 线程数量 */
	public static final int TASK_COUNT = 3;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		download = (Button) findViewById(R.id.download);
		download.setOnClickListener(this);
		progress = (ProgressBar) findViewById(R.id.progressBar);
	}

	@Override
	public void onClick(View v) {
		if (v == download) { // 下载
			try {
				URL url = new URL(URL_DOWNLOAD);
				// 获取资源总长度
				int totalLength = url.openConnection().getContentLength();
				// 设置进度条总长度
				progress.setMax(totalLength);
				// 每条线程应该下载的长度
				int perLength = totalLength % TASK_COUNT == 0 ? totalLength
						/ TASK_COUNT : totalLength / TASK_COUNT + 1;
				if (Environment.MEDIA_MOUNTED.equals(Environment
						.getExternalStorageState()) && isHaveConnection()) { // 如果sd卡可用并且网络可用
					// 创建文件
					File file = new File(
							Environment.getExternalStorageDirectory(),
							"handin.ape");
					if (file.exists()) {
						file.delete();
					}
					file.createNewFile();
					// 初始化线程分支集合
					tasks = new ArrayList<MyDownloadTask>(TASK_COUNT);
					// 循环创建线程并下载
					for (int i = 0; i < TASK_COUNT; i++) {
						MyDownloadTask task = new MyDownloadTask(perLength * i,
								perLength * (i + 1), url, file);
						task.execute();
						// 添加进入任务列表中
						tasks.add(task);
					}
				} else {
					Toast.makeText(this, "内存卡不可用", Toast.LENGTH_SHORT).show();
				}
			} catch (MalformedURLException e) {
			} catch (IOException e) {
			}
		}
	}

	/**
	 * 判断网络是否可用
	 * 
	 * @return true可用 false不可用
	 */
	public boolean isHaveConnection() {
		ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo info = cm.getActiveNetworkInfo();
		if (info != null) {
			return info.isAvailable();
		}
		return false;
	}

	/**
	 * 下载任务
	 * 
	 * @author lx
	 */
	class MyDownloadTask extends AsyncTask<Void, Void, Void> {

		// 开始位置
		private int begin;
		// 结束位置
		private int end;
		// 下载的地址
		private URL url;
		// 保存的文件对象
		private File file;

		public MyDownloadTask(int begin, int end, URL url, File file) {
			super();
			this.begin = begin;
			this.end = end;
			this.url = url;
			this.file = file;
		}

		// 当前线程进度情况
		private int current;

		public int getCurrent() {
			return current;
		}

		@Override
		protected Void doInBackground(Void... params) {
			try {
				HttpURLConnection con = (HttpURLConnection) url
						.openConnection();
				// 设置下载区间
				con.setRequestProperty("Range", "bytes=" + begin + "-" + end);
				InputStream in = con.getInputStream();
				BufferedInputStream input = new BufferedInputStream(in);
				// 创建文件输出,"rwd":读写删除权限,其它类型看提示说明
				RandomAccessFile output = new RandomAccessFile(file, "rwd");
				// 设置开始写入的位置
				output.seek(begin);
				// 单次写的byte数组
				byte[] bs = new byte[1024 * 1024];
				// 单次写的长度
				int length = 0;
				while ((length = input.read(bs)) != -1) {
					output.write(bs, 0, length);
					// 当前分支进度增加
					current += length;
					publishProgress();
				}
				// 关闭,将缓冲区写入文件
				input.close();
				output.close();
			} catch (IOException e) {
			}
			return null;
		}

		@Override
		protected void onProgressUpdate(Void... values) {
			// 当前总进度
			int total = 0;
			for (MyDownloadTask task : tasks) { // 循环获取每一条分支的进度
				total += task.getCurrent();
			}
			progress.setProgress(total);
			super.onProgressUpdate(values);
		}

		@Override
		protected void onPostExecute(Void result) {
			if (progress.getProgress() == progress.getMax()) { // 判断进度条是否满
				Toast.makeText(MainActivity.this, "下载成功!", Toast.LENGTH_SHORT)
						.show();
			}
			super.onPostExecute(result);
		}

	}
}


 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值