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

当筵意气临九霄,星离雨散不终朝。这篇文章主要讲述赵雅智_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 = https://www.songbingjia.com/android/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 = https://www.songbingjia.com/android/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的时候已经  设置了同步
 
赵雅智_android多线程下载带进度条

文章图片

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.当文件下载时,进度条显示和当前进度显示
 
赵雅智_android多线程下载带进度条

文章图片

2.当又一次载入时继续上次下载
 
赵雅智_android多线程下载带进度条

文章图片

【赵雅智_android多线程下载带进度条】3.当文件进行下载时,暂时存储下载文件位置的pb.txt被创建
 
赵雅智_android多线程下载带进度条

文章图片

4.当线程完成下载后,在主线程Toast完成下载
 
赵雅智_android多线程下载带进度条

文章图片

5.当下在完毕后进度条走满,当前进度为100%
 
赵雅智_android多线程下载带进度条

文章图片

6.下载完后,暂时文件销毁
赵雅智_android多线程下载带进度条

文章图片




源码下载地址:http://download.csdn.net/detail/zhaoyazhi2129/7406731
转发请标明出处:http://blog.csdn.net/zhaoyazhi2129/article/details/27192169
 







    推荐阅读