概述
本应用实现的是输入文件的网络的地址,点击按钮开始下载,下载过程中有进度条和后面的文本提示进度,
下载过程中按钮不可点击,防止重复的下载,下载完毕后会进行Toast的提示显示,
并且回复按钮的可点击性,进度条也会清空,当然如果下载中途结束应用进程就会进行进度的保存,
下次下载同样的文件时就会从进度记录进行下载,节省流量和时间
应用需要的应用权限:
访问网络权限
<uses-permission android:name="android.permission.INTERNET"/>
外部储存的写入权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
布局文件代码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >
<EditText
android:id="@+id/et_path"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="请输入下载文件的地址"
android:singleLine="true"
android:text="http://172.22.64.193:8080/test.exe" >
</EditText>
<TableLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<ProgressBar
android:id="@+id/pb"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="8" />
<TextView
android:id="@+id/tv_progressNumber"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="2"
android:gravity="center"
android:text="0%" />
</TableRow>
</TableLayout>
<Button
android:id="@+id/bt_startDownlode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="startDownlode"
android:text="开始下载" />
</LinearLayout>
核心代码
package com.examp.mutildownloader;
import java.io.File;
import java.io.FileInputStream;
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.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
/**
* Android下的多线程下载,断点下载
*
* @author MartinDong
*
*/
public class MainActivity extends Activity {
// sd卡的路径
private static final File sd = Environment.getExternalStorageDirectory();
private static final int DOWNLODE_ERROR = 1;
private static final int DOWNLODE_SUCCESS = 2;
private static final int DOWNLODE_DELETE_TEMP = 3;
public static final int PROGRESS_NUMBER_CHANGE = 4;
// 定义线程个数
private static int threadCount = 3;
// 定义当前存货的线程个数
private static int runningThread = 3;
// 组件的获取
private EditText et_path;
private ProgressBar pb;
private Button bt_startDownlode;
private TextView tv_progressNumber;
// 定义存储的文件名称
private String filename;
// 设置进度条的进度
// 进度条的数据
public int progressTemp = 0;
// 定义消息处理
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case DOWNLODE_ERROR:
Toast.makeText(getApplicationContext(), "下载失败....",
Toast.LENGTH_SHORT).show();
break;
case DOWNLODE_SUCCESS:
// 设置按钮可用
bt_startDownlode.setEnabled(true);
// 清空进度
progressTemp = 0;
pb.setProgress(progressTemp);
// 文本清0
tv_progressNumber.setText("0%");
Toast.makeText(getApplicationContext(), "下载成功....",
Toast.LENGTH_SHORT).show();
break;
case DOWNLODE_DELETE_TEMP:
Toast.makeText(getApplicationContext(), "删除进度文件....",
Toast.LENGTH_SHORT).show();
break;
case PROGRESS_NUMBER_CHANGE:
tv_progressNumber.setText(pb.getProgress() * 100 / pb.getMax()
+ "%");
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_path = (EditText) findViewById(R.id.et_path);
// 获取组件
pb = (ProgressBar) findViewById(R.id.pb);
bt_startDownlode = (Button) findViewById(R.id.bt_startDownlode);
tv_progressNumber = (TextView) findViewById(R.id.tv_progressNumber);
}
public void startDownlode(View view) {
// 设置按钮不可点击//防止重复提交
bt_startDownlode.setEnabled(false);
// 从控件中获取下载的路径
final String path = et_path.getText().toString().trim();
// 获取输入地址的最后一个"/"出现的位置
int lastIndex = path.lastIndexOf("/");
// 获取文件名称及格式
filename = path.substring(lastIndex + 1, path.length());
System.out.println("文件名称为===============" + filename);
// 判断路径是否有效
if (TextUtils.isEmpty(path)) {
Toast.makeText(this, "请输入有效的下载路径......", Toast.LENGTH_SHORT).show();
return;
}
// 为了避免与主线程冲突,开启子线程完成,避免anr问题
new Thread() {
public void run() {
try {
// 1,连接到服务器,获取一个文件,获取文件的大小跟服务器的文件一样的临时文件
// String path = "http://172.22.64.193:8080/tomcat.css";
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url
.openConnection();
// 设置超时
conn.setConnectTimeout(5000);
// 设置请求方式
conn.setRequestMethod("GET");
// 获取服务器的返回码
int code = conn.getResponseCode();
// 判断返回码
if (code == 200) {
// 获取返回的长度
int length = conn.getContentLength();
// 设置进度条的最大值
pb.setMax(length);
System.out.println("文件总长度:" + length);
// 在客户端创建出一个跟服务器大小一致的临时文件
RandomAccessFile raf = new RandomAccessFile(sd + "/"
+ filename, "rwd");
// 指定临时文件的大小
raf.setLength(length);
// 释放资源
raf.close();
// 平均每一个线程的文件大小
int blockSize = length / threadCount;
// 设置活跃的线程
runningThread = threadCount;
for (int threadId = 1; threadId <= threadCount; threadId++) {
// 线程开始的下载位置
int startIndex = (threadId - 1) * blockSize;
// 线程的结束位置
int endIndex = threadId * blockSize - 1;
// 判断是否是最后一个线程
if (threadId == threadCount) {
// 设置结束的位置为到文件的最后
endIndex = length;
}
System.out.println("线程:" + threadId
+ "下载:开始位置>>>>>>>>" + startIndex
+ "结束>>>>>>>>>>" + endIndex);
new DownlodeThread(path, threadId, startIndex,
endIndex).start();
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
};
}.start();
}
/**
* 下载文件的子线程类,每一个线程下载对应位置文件数据
*
* @author MartinDong
*
*/
public class DownlodeThread extends Thread {
private String path;
private int threadId;
private int startIndex;
private int endIndex;
/**
*
* @param path
* 文件的下载路径
* @param threadId
* 线程id
* @param startIndex
* 线程开始的位置
* @param endIndex
* 线程结束的位置
*/
public DownlodeThread(String path, int threadId, int startIndex,
int endIndex) {
this.path = path;
this.threadId = threadId;
this.startIndex = startIndex;
this.endIndex = endIndex;
}
@Override
public void run() {
try {
// 检查是否存在下载历史的文件
File tempFile = new File(sd + "/" + threadId + ".txt");// =========================断点记录操作===============================
if (tempFile.exists() && tempFile.length() > 0) {
// 文件输入流
FileInputStream fis = new FileInputStream(tempFile);
// 中间变量,缓存的作用
byte[] tempBuffer = new byte[1024];
// 获取进度文件的数据大小
int length = fis.read(tempBuffer);
// 获取进度文件的数据
String historyData = new String(tempBuffer, 0, length);
// 将进度数据装换为整型
int historyDataInt = Integer.parseInt(historyData);
// 如果是断点传送,初始化进度条的进度
// 获取每个线程已经下载了的进度
int temp = historyDataInt - startIndex;
// 为进度重新赋值
progressTemp += temp;
// 修改真正的下载位置
startIndex = historyDataInt;
fis.close();
}// =========================断点记录操作===============================
// 将地址转换为URL
URL url = new URL(path);
// 获取http连接
HttpURLConnection conn = (HttpURLConnection) url
.openConnection();
// 设置连接的请求方式
conn.setRequestMethod("GET");
// 重要:请求服务器下载部分的文件,指定文件的位置
conn.setRequestProperty("Range", "bytes=" + startIndex + "-"
+ endIndex);
System.out
.println("线程:" + threadId + "真实开始的下载进度:" + startIndex);
// 设置超时时间
conn.setReadTimeout(5000);
// 得到服务器的状态码,200表示请求的全部资源得到响应=== ok,206请求的部分资源得到响应=== ok
int code = conn.getResponseCode();
System.out.println("code:" + code);
if (code == 206) {
// 返回的是指定位置的文件流
InputStream is = conn.getInputStream();
// 创建一个临时的文件
RandomAccessFile raf = new RandomAccessFile(sd + "/"
+ filename, "rwd");
// 移动指针,到指定的文件位置,
raf.seek(startIndex);
// 创建中间缓冲字节数组
byte[] buffer = new byte[1024];
// 读取文件的大小
int length = 0;
// 定义已经下载的数据长度,用作断点下载的记录=========================断点记录操作===============================
int downlodeTotal = 0;
// 循环写入
while ((length = is.read(buffer)) != -1) {
// 定义一个记录线程的记录文件=========================断点记录操作===============================
RandomAccessFile historyFile = new RandomAccessFile(sd
+ "/" + threadId + ".txt", "rwd");
// 向文件中写入数据
raf.write(buffer, 0, length);
// 记录已经下载的文件长度
downlodeTotal += length;
// 将已经下载的文件长度和开始的读取位置相加,得到已经读取的文件位置
historyFile.write((downlodeTotal + startIndex + "")
.getBytes());
historyFile.close();// =========================断点记录操作===============================
// 保持同步的更新
synchronized (MainActivity.this) {
// 将每个线程的读取文件的大小累加到下载进度上
progressTemp += length;
// 更新界面上的Progress的进度条的长度
pb.setProgress(progressTemp);
// 特殊情况ProgressBar ProgressDialog
// 是可以直接在子线程中跟新ui的,内部代码有过特殊的处理
// 使用Message.obtain();减少Message对象的创建
Message msg = Message.obtain();
msg.what = PROGRESS_NUMBER_CHANGE;
handler.sendMessage(msg);
}
}
is.close();
raf.close();
System.out.println("线程:" + threadId + "下载完毕............");
} else {
System.out.println("线程:" + threadId
+ "下载失败请重新下载............");
// 向消息处理器发送信息
Message msg = new Message();
msg.what = DOWNLODE_ERROR;
handler.sendMessage(msg);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
synchronized (MainActivity.this) {
// 进行线程数量的变化操作
runningThread--;
// 如果当前存活的线程为0,执行进度文件统一销毁的操作
if (runningThread == 0) {
// 如果下载完毕,清除进度文件
for (int threadIndex = 1; threadIndex <= threadCount; threadIndex++) {
// 这里创建的是与线程文件对应的文件,文件名可以自定义,方便起见是采用的是线程的ID表示
File temp = new File(sd + "/" + threadIndex
+ ".txt");
// 执行文件删除的操作
temp.delete();
}
System.out.println("下载完毕,删除进度文件.............");
// 向消息处理器发送信息
// 向消息处理器发送信息
Message msg = new Message();
msg.what = DOWNLODE_SUCCESS;
handler.sendMessage(msg);
}
}
}
}
}
}
Demo下载地址:
http://download.csdn.net/detail/u011936142/7428611
最后
以上就是忧郁路人为你收集整理的Android多线程文件下载器的全部内容,希望文章能够帮你解决Android多线程文件下载器所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复