赵雅智_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.当文件下载时,进度条显示和当前进度显示
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemhhb3lhemhpMjEyOQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
2.当又一次载入时继续上次下载
3.当文件进行下载时,暂时存储下载文件位置的pb.txt被创建
4.当线程完成下载后,在主线程Toast完成下载
5.当下在完毕后进度条走满,当前进度为100%
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemhhb3lhemhpMjEyOQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
6.下载完后,暂时文件销毁
源码下载地址:http://download.csdn.net/detail/zhaoyazhi2129/7406731
转发请标明出处:http://blog.csdn.net/zhaoyazhi2129/article/details/27192169
赵雅智_android多线程下载带进度条的更多相关文章
- 赵雅智_Android的getResources()资源引用
今天做一个Android的刮刮乐项目.里面用到非常多的地方用到了getResources. <span style="font-size:12px;"> // 获得图片 ...
- 赵雅智_Android编码规范
凝视 导入mycodetemplates.xml统一凝视样式 须要加凝视的地方 类凝视(必加) 方法凝视(必加) 块凝视主要是数据结构和算法的描写叙述(必加) 类成员变量和常量凝视(选择性加入) 单行 ...
- 赵雅智_Android案例_刮刮乐
实现效果 主要代码 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns ...
- 赵雅智_android获取本机运营商,手机号部分能获取
手机号码不是全部的都能获取.仅仅是有一部分能够拿到. 这个是因为移动运营商没有把手机号码的数据写入到sim卡中.SIM卡仅仅有唯一的编号.供网络与设备 识别那就是IMSI号码,手机的信号也能够说是通过 ...
- Java POI 3.17导出EXCEL并下载(带进度条提示)
导出数据 共4590条 只需要 5 秒左右,性能还算可以 我们再来测试一下 50000 条的性能...
- 赵雅智:android教学大纲
带下划线为详细内容链接地址.点击后可跳转.希望给大家尽一些微薄之力.眼下还在整理中 教学章节 教学内容 学时安排 备注 1 Android高速入门 2 Android模拟器与常见命令 3 Androi ...
- android:异步任务asyncTask介绍及异步任务下载图片(带进度条)
为什么要用异步任务? 在android中仅仅有在主线程才干对ui进行更新操作.而其他线程不能直接对ui进行操作 android本身是一个多线程的操作系统,我们不能把全部的操作都放在主线程中操作 .比方 ...
- Android -- 自定义带进度条的按钮
1. 实现了一个带进度条的按钮,完成后显示提示信息,并设置按钮为不可再次被点击
- atitit.文件上传带进度条的实现原理and组件选型and最佳实践总结O7
atitit.文件上传带进度条的实现原理and组件选型and最佳实践总结O7 1. 实现原理 1 2. 大的文件上传原理::使用applet 1 3. 新的bp 2 1. 性能提升---分割小文件上传 ...
随机推荐
- Android之等比例显示图片
在android中,由于密度的影响,如果想得到图片的宽高是不行的,具体为什么我就大概说一下,具体的请搜索度娘或者古哥吧. 原因是如果你把图片放在drawable-mdpi里,而手机是属于drawabl ...
- 使用StringBuilder或StringBuffer简单优化
使用StringBuilder或StringBuffer // join(["a", "b", "c"]) -> "a an ...
- MySql_34道经典Sql试题
MySql_34道经典Sql试题 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/xiaouncle/article/details/799390 ...
- 【BZOJ】【2850】【Violet 0】巧克力王国
KD-Tree 问平面内在某条直线下方的点的权值和 我一开始yy的是:直接判这个矩形最高的两个点(y坐标的最大值)是否在这条直线下方就可以了~即判$A*x+B*y<C$... 然而这并不对啊…… ...
- 用java解析在OpenStreetMap上下载的地图数据
采用dom4j解析下载的xml文件,java程序如下: package gao.map.preprocess; import java.io.BufferedWriter; import java.i ...
- 网络编程socket之listen函数
摘要:listen函数使用主动连接套接口变为被连接套接口,使得一个进程可以接受其它进程的请求,从而成为一个服务器进程.在TCP服务器编程中listen函数把进程变为一个服务器,并指定相应的套接字变为被 ...
- OTL翻译(5) -- otl_stream流相关绑定变量
声明绑定变量 本章节将详细的说明如何在otl_stream流里面声明绑定变量. SQL语句.SQL语句块或存储过程在程序里面使用的时候总是带有占位符.OTL里面带有一个小的解析器用来解析这些占位符,并 ...
- Android硬件入门-照相机
学习Android不能不学习照相机,现在各种美容相机,微信朋友圈发图,现在升级之后直接下拉就可以照相了,各种艳照的的源头也是照相机,扯远了,有点邪恶了,还是简单学习一下Android中照相机的使用,A ...
- 一键切换皮肤的解决思想及iframe嵌套时寻找下级iframe的方法
项目中有个一键切换皮肤的功能,感觉还不错,记录下,就是各颜色样式设置起来太复杂了,不知道有没有更简便的方法: 1.切换皮肤结构层 <li title="<s:text name= ...
- 【BZOJ2662】【BeiJing wc2012】冻结 分层图 裸的!
我都不好意思发题解了,看这篇博吧.(飞行路线的,基本一样) http://blog.csdn.net/vmurder/article/details/40075989 同学做了好久.我害怕题里有坑,又 ...