赵雅智_android多线程下载带进度条

Posted gavanwanggw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了赵雅智_android多线程下载带进度条相关的知识,希望对你有一定的参考价值。

progressBar说明

在某些操作的进度中的可视指示器,为用户呈现操作的进度,还它有一个次要的进度条,用来显示中间进度,如在流媒体播放的缓冲区的进度。

一个进度条也可不确定其进度。在不确定模式下,进度条显示循环动画。这样的模式经常使用于应用程序使用任务的长度是未知的。

 

XML重要属性

android:progressBarStyle:默认进度条样式

android:progressBarStyleHorizontal:水平样式

 

progressBar重要方法

getMax():返回这个进度条的范围的上限

getProgress():返回进度

getSecondaryProgress():返回次要进度

incrementProgressBy(int diff):指定添加的进度

isIndeterminate():指示进度条是否在不确定模式下

setIndeterminate(boolean indeterminate):设置不确定模式下

setVisibility(int v):设置该进度条是否可视

 

progressBar重要事件

onSizeChanged(int w, int h, int oldw, int oldh):当进度值改变时引发此事件

 

项目实现步骤分析

1.给进度调设置最大值

2.不管哪一个线程下载都要记录进度条当前位置,并设置值

3.给textview设置显示当前下载值

4.创建暂时文件把曾经下载好的位置记录起来

5.当线程下载完后删除暂时保存进度条位置的文件

 

主要代码:

加入权限

<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"/>

布局文件 

 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android_download"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />
	 <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"/>
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.android_download.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

StreamTools工具

android线程下载的工具一样http://blog.csdn.net/zhaoyazhi2129/article/details/27189465

Activity主要代码

package com.example.android_download;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;

import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.text.TextUtils;
import android.view.View;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import com.example.util.StreamTools;

public class MainActivity extends Activity {
	private int threadNum = 3;// 线程开启的数量
	private int threadRunning = 3;// 正在执行的线程
	private TextView tv_pb;
	private EditText et_url;
	private ProgressBar pb_download;
	private int currentPb;//当前值
	

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

		File sdDir = Environment.getExternalStorageDirectory();
		File pdFile = new File(sdDir, "pb.txt");
		InputStream is = null;
		try {
			// 推断文件是否存在
			if (pdFile.exists()) {
				is = new FileInputStream(pdFile);
			}

		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if (is != null) {
			String value = StreamTools.streamToStr(is);
			//拆分
			String arr[] = value.split(";");

			pb_download.setMax(Integer.valueOf(arr[0]));// 最大值
			currentPb = Integer.valueOf(arr[1]);// 当前值
			pb_download.setProgress(currentPb);
			tv_pb.setText("当前的进度是:"+arr[2]);
		}
	}

	/**
 * 
 */
	public void findView() {
		tv_pb = (TextView) findViewById(R.id.tv_pb);
		et_url = (EditText) findViewById(R.id.et_url);
		pb_download = (ProgressBar) findViewById(R.id.pb_download);
	}

	// 下载文件(得到server端的文件大小 )
	public void downLoadFile(View v) {
		// 获取下载路径
		final String spec = et_url.getText().toString();
		if (TextUtils.isEmpty(spec)) {
			Toast.makeText(this, "下载地址不能为空", 0).show();
		} else {
			new Thread() {
				@Override
				public void run() {
					// 訪问网络地址
					try {
						// 依据下载的地址构建url对象
						URL url = new URL(spec);
						// 通过URL对象的openConnection()方法打开连接,返回一个连接对象
						HttpURLConnection httpURLConnection = (HttpURLConnection) url
								.openConnection();
						// 设置请求头
						httpURLConnection.setRequestMethod("GET");
						httpURLConnection.setConnectTimeout(5000);
						httpURLConnection.setReadTimeout(5000);

						// 推断是否响应成功
						if (httpURLConnection.getResponseCode() == 200) {
							/**
							 * 第一步:得到server下载文件的大小,然后在本地设置一个暂时文件和server端文件大小一致
							 */
							// 获取文件长度
							int fileLength = httpURLConnection
									.getContentLength();
							//给进度条设置最大值
							pb_download.setMax(fileLength);
							//推断SD卡是否可用
							if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
								// 外部存储设备的路径
								File sdFile = Environment
										.getExternalStorageDirectory();
								//获取文件名
								String fileName = spec.substring(spec.lastIndexOf("/")+1);
								// 随机訪问文件的读取与写入RandomAccessFile(file, mode)
								RandomAccessFile accessFile = new RandomAccessFile(
										new File(sdFile, fileName), "rwd");
								// 设置暂时文件与server文件大小一致
								accessFile.setLength(fileLength);
								// 关闭暂时文件
								accessFile.close();

								/**
								 * 第二步:计算出每一个线程下载的大小(開始位置,结束位置)
								 */
								// 计算出每一个线程下载的大小
								int threadSize = fileLength / threadNum;
								// for循环,计算出每一个线程的開始和结束位置
								for (int threadId = 1; threadId <= 3; threadId++) {
									int startIndex = (threadId - 1) * threadSize;// 開始位置
									int endIndex = threadId * threadSize - 1;// 结束位置
									if (threadId == threadNum) {// 最后一个 线程
										endIndex = fileLength - 1;
									}

									System.out.println("当前线程--" + threadId
											+ "-----開始位置" + startIndex + "----结束位置"
											+ endIndex + "-----线程大小" + threadSize);
									/**
									 * 第三步:每创建好一次就要开启线程下载
									 */
									new DownLoadThread(threadId, startIndex,
											endIndex, spec,fileName).start();

								}
							}else {
								runOnUiThread(new Runnable() {
									
									@Override
									public void run() {
										// TODO Auto-generated method stub
										Toast.makeText(MainActivity.this, "SD卡不存在", 1).show();
									}
								});
							}
						} else {
							runOnUiThread(new Runnable() {
								
								@Override
								public void run() {
									// TODO Auto-generated method stub
									Toast.makeText(MainActivity.this, "server端返回错误", 1).show();
								}
							});
						}

					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}.start();

		}
	}

	/**
	 * 每创建好一次就要开启线程下载
	 * 
	 * @author zhaoyazhi
	 * 
	 */
	class DownLoadThread extends Thread {
		// 成员变量
		private int threadId;
		private int startIndex;
		private int endIndex;
		private String path;
		private String fileName;

		File sdFile = Environment.getExternalStorageDirectory();

		/**
		 * 
		 * @param threadId
		 *            线程的序号
		 * @param startIndex
		 *            线程下载開始位置
		 * @param endIndex
		 *            线程下载结束位置
		 * @param path
		 *            线程下载保存文件的路径
		 */
		public DownLoadThread(int threadId, int startIndex, int endIndex,
				String path,String fileName) {
			super();
			this.threadId = threadId;
			this.startIndex = startIndex;
			this.endIndex = endIndex;
			this.path = path;
			this.fileName = fileName;
		}

		@Override
		public void run() {
			// 能够通过每一个线程去下载文件
			try {
				/**
				 * 第四步:从本地文件上读取已经下载文件的開始位置
				 */
				File recordFile = new File(sdFile, threadId + ".txt");
				if (recordFile.exists()) {
					// 读取文件
					InputStream is = new FileInputStream(recordFile);
					// 利用工具类转换
					String value = StreamTools.streamToStr(is);
					// 获取记录的位置
					int recordIndex = Integer.parseInt(value);
					// 把记录的位置付给開始位置
					startIndex = recordIndex;
				}
				// 通过path对象构造URL 对象
				URL url = new URL(path);
				// 通过URL对象openConnection
				HttpURLConnection httpURLConnection = (HttpURLConnection) url
						.openConnection();
				// 设置请求头
				httpURLConnection.setRequestMethod("GET");
				httpURLConnection.setConnectTimeout(5000);
				// 设置下载文件的開始位置和结束位置
				httpURLConnection.setRequestProperty("Range", "bytes="
						+ startIndex + "-" + endIndex);
				// 获取状态码
				int code = httpURLConnection.getResponseCode();
				// System.out.println(code);
				// 推断是否成功 仅仅要设置"Range"头,返回的状态码就是206
				if (code == 206) {
					// 获取每一个线程返回的流对象
					InputStream is = httpURLConnection.getInputStream();
					// 创建随机訪问的对象
					RandomAccessFile accessFile = new RandomAccessFile(
							new File(sdFile, fileName), "rwd");
					// 指定開始位置
					accessFile.seek(startIndex);
					// 定义读取的长度
					int len = 0;
					// 定义缓冲区
					byte buffer[] = new byte[1024*1024];
					int total = 0;
					// 循环读取
					while ((len = is.read(buffer)) != -1) {
						System.out.println("当前线程--" + threadId
								+ "-----当前下载的位置是" + (startIndex + total));
						// 保存每一个线程的下载位置
						RandomAccessFile threadFile = new RandomAccessFile(
								new File(sdFile, threadId + ".txt"), "rwd");
						// 记录每次下载位置
						threadFile.writeBytes((startIndex + total) + "");
						threadFile.close();
						accessFile.write(buffer, 0, len);
						total += len;// 已经下载大小
						
						/**
						 * 当程序有多个线程訪问一个变量。能够用synchronized解决
						 */
						synchronized (MainActivity.this) {
							//进度条当前进度
							currentPb += len;
							pb_download.setProgress(currentPb);
							final String percent= currentPb*100l/pb_download.getMax()+"%";
							runOnUiThread(new Runnable() {
								
								@Override
								public void run() {
									//计算百分比操作
									
									tv_pb.setText("当前的进度是:"+percent);
								}
							});
							//创建保存当前 进度和百分比
							RandomAccessFile pbFile = new RandomAccessFile(
									new File(sdFile,  "pb.txt"), "rwd");
							pbFile.writeBytes(pb_download.getMax()+";"+currentPb+";"+percent);
							pbFile.close();
						}
						
					}
					accessFile.close();
					is.close();

					runOnUiThread(new Runnable() {
						
						@Override
						public void run() {
							// TODO Auto-generated method stub
							Toast.makeText(MainActivity.this, "当前线程" + threadId + "---完成下载", 1).show();
						}
					});
					/**
					 * 第五步:当你的n个线程都完成下载 的时候我才进行删除记录下载位置的缓存文件
					 */
					deleteRecordFile();
				} else {
					runOnUiThread(new Runnable() {
						
						@Override
						public void run() {
							// TODO Auto-generated method stub
							Toast.makeText(MainActivity.this, "server端返回错误", 1).show();
						}
					});
				}
				// 设置你下载文件
			} catch (Exception e) {
				e.printStackTrace();
			}

		}

	}

	/**
	 * synchronized避免线程同步 下载完删除存储文件下载位置的暂时文件
	 */
	public synchronized void deleteRecordFile() {
		// 外部存储设备的路径
		File sdFile = Environment.getExternalStorageDirectory();
		// 线程下载完就减去
		threadRunning--;
		// 当没有正在执行的线程
		if (threadRunning == 0) {
			for (int i = 1; i <= 3; i++) {
				File recordFile = (new File(sdFile, i + ".txt"));
				if (recordFile.exists()) {
					recordFile.delete();
				}
				File pbFile = (new File(sdFile, "pb.txt"));
				if (pbFile.exists()) {
					pbFile.delete();
				}
			}
			
		}
	}

	
}

补充知识点

不用在给进度条设置值的时候考虑同步。由于android定义progressBar的时候已经 设置了同步

 技术分享

2.当程序有多个线程訪问一个变量,能够用synchronized解决

synchronized (MainActivity.this) {
							//进度条当前进度
							currentPb += len;
							pb_download.setProgress(currentPb);
							
							runOnUiThread(new Runnable() {
								
								@Override
								public void run() {
									tv_pb.setText("当前的进度是:"+currentPb*100/pb_download.getMax()+"%");
								}
							});


执行结果

1.当文件下载时,进度条显示和当前进度显示

 技术分享

2.当又一次载入时继续上次下载

 技术分享

3.当文件进行下载时,暂时存储下载文件位置的pb.txt被创建

 技术分享

4.当线程完成下载后,在主线程Toast完成下载

 技术分享

5.当下在完毕后进度条走满,当前进度为100%

 技术分享

6.下载完后,暂时文件销毁

技术分享


源码下载地址:http://download.csdn.net/detail/zhaoyazhi2129/7406731

转发请标明出处:http://blog.csdn.net/zhaoyazhi2129/article/details/27192169

 







以上是关于赵雅智_android多线程下载带进度条的主要内容,如果未能解决你的问题,请参考以下文章

赵雅智_Fragment生命周期

赵雅智_运用Bitmap和Canvas实现图片显示,缩小,旋转,水印

赵雅智_service生命周期

赵雅智_Swift_swift常量和变量

赵雅智_android获取本机运营商,手机号部分能获取

赵雅智_BroadcastReceiver