您的位置:首页 > 移动开发 > Android开发

Android模拟多线程的断点下载

2015-06-23 21:31 465 查看
       在本代码当中,是模拟多线程的断点下载,在代码中只是使用了简单的使用一个txt文件来保存每个线程的下载进度

     (其实最好是将进度保存到我们Android自带的sqlite数据库当中)。

ok,不多说,看看模拟的多线程断点下载的代码:
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">1.Android应用的主配置文件manifest:</span>pan>


<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.mutildownload"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="14" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.mutildownload.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>
2.XML配置文件activity_main:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >

<EditText
android:id="@+id/et"
android:hint="请输入下载地址"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="http://dl.360safe.com/360NmGameAccSetup.exe"
android:ems="10" >
</EditText>

<ProgressBar
android:id="@+id/pb"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />

<Button
android:onClick="download"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下载" />
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="@+id/tv_progress"/>

</LinearLayout>

3.主程序MainActivity:

package com.example.mutildownload;

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.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.view.View;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {
private EditText et;
private ProgressBar pb;
private TextView tv_progress;

private static final int ThreadCount = 3;
private static final int PATH_ERROR = 1;
private static final int SERVER_ERROR = 2;
private static final int DOWNLOAD_FINSH = 4;
private static final int PROGRESS_UPDATE = 5;
public static final int REDOWNLOAD = 6;
private static int RunningThread = ThreadCount;
private static int CurrentProgress = 0;

Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case PATH_ERROR:
Toast.makeText(getApplicationContext(), "下载路径错误", 0).show();
break;
case SERVER_ERROR:
Toast.makeText(getApplicationContext(), "服务器错误", 0).show();
break;
case DOWNLOAD_FINSH:
Toast.makeText(getApplicationContext(), "下载完成", 0).show();
break;
case PROGRESS_UPDATE:
tv_progress.setText("下载的进度:"+pb.getProgress()*100 / pb.getMax()+"%");
break;
case REDOWNLOAD:
Toast.makeText(getApplicationContext(), "下载失败,再次重新下载", 0).show();
break;
}
};
};

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

et = (EditText) this.findViewById(R.id.et);
pb = (ProgressBar) this.findViewById(R.id.pb);
tv_progress = (TextView) this.findViewById(R.id.tv_progress);
}

public void download(View view) {
// 连接服务器,获取一个文件,知道文件的长度,并在本地创建一个文件和服务器文件大小一样的临时文件
// String path = "http://dl.360safe.com/360DrvMgrInstaller_beta.exe"
final String path = et.getText().toString().trim();
if (TextUtils.isEmpty(path)) {
Toast.makeText(getApplicationContext(), "下载路径不能为空", 0).show();
return;
}
new Thread() {
public void run() {
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url
.openConnection();
conn.setRequestMethod("GET");// 设置get请求
conn.setReadTimeout(5000);// 设置超时时间

int code = conn.getResponseCode();// 得到响应码
if (code == 200) {
// 读取到的文件的长度实,际上就是文件的长度
int length = conn.getContentLength();
// System.out.println("文件的长度:"+length);
//设置进度条的最大值
pb.setMax(length);

// 读取到服务器文件的大小,就利用RandomAccessFile随机文件访问类在本地客户端创建一个和服务器相同大小的文件
RandomAccessFile ras = new RandomAccessFile(
"/sdcard/360.exe", "rwd");
// 设置要创建文件的长度
ras.setLength(length);
ras.close();

int blockSize = length / ThreadCount;
// 假设有3个线程去下载服务器中的文件
// 就要计算每个线程下载文件的哪一个部分,也就是每个线程应该下载的文件的大小
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 DownLoadThread(path, threadId, startIndex,
endIndex).start();
}
} else {
System.out.println("读取失败");
Message msg = new Message();
msg.what = SERVER_ERROR;
handler.sendMessage(msg);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("路径有问题,下载失败");
Message msg = new Message();
msg.what = PATH_ERROR;
handler.sendMessage(msg);
}

};
}.start();
}

public class DownLoadThread extends Thread {
private String path;
private int threadId;
private int startIndex;
private int endIndex;

/**
* @param path
*            在服务器上下载文件的路径
* @param threadId
*            下载线程的id
* @param startIndex
*            线程下载文件的起始地址
* @param endIndex
*            线程下载文件的结束地址
*/
public DownLoadThread(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("/sdcard/" + threadId + ".txt");
if (tempfile.exists() && tempfile.length() > 0) {
// 文件存在,将文件中记录的数据读取出来
FileInputStream fis = new FileInputStream(tempfile);
byte[] buffer = new byte[1024];
int leng = 0;
leng = fis.read(buffer);
String downloadlen = new String(buffer, 0, leng);
int downloadint = Integer.parseInt(downloadlen);

//如果当前线程下载完成,而文件还没删除,该线程被断后,再次执行就让他结束
if(downloadint == endIndex){
CurrentProgress --;
return;
}

synchronized (MainActivity.this) {
int CurrentThreadProgressint = downloadint - startIndex;
CurrentProgress += CurrentThreadProgressint;//获取到总的进度
}

startIndex = downloadint;// 重置开始读取数据的位置,保证从断点的位置开始下载
System.out.println("tempfile:" + threadId + "downloadin"
+ downloadint);
}

URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url
.openConnection();
// 设置下载服务器资源文件的部分内容,指定文件的位置
conn.setRequestProperty("Range", "bytes=" + startIndex + "-"
+ endIndex);

conn.setRequestMethod("GET");
conn.setReadTimeout(10000);

int code = conn.getResponseCode();// 响应码,从服务器请求全部资源:200
// ok,请求部分资源:206 ok
// System.out.println("responseCode:"+code);

InputStream is = conn.getInputStream();// 已经设置了文件的读取位置,返回的是当前位置文件对应的输入流
RandomAccessFile ras = new RandomAccessFile("/sdcard/360.exe",
"rwd");
ras.seek(startIndex);// 设置文件的定位

int len = 0;
int total = 0;// 记录已经读取了多少数据
byte[] buffer = new byte[1024];

// 在这里可以使用File,RandomAccessFile不容易损伤硬盘
while ((len = is.read(buffer)) != -1) {
RandomAccessFile raf = new RandomAccessFile("/sdcard/"
+ threadId + ".txt", "rwd");// 用来记录当前线程下载的位置
ras.write(buffer, 0, len);
total += len;

//加锁,同步执行
synchronized (MainActivity.this) {
CurrentProgress += len;//获取下载的进度及每个线程的下载量之和
pb.setProgress(CurrentProgress);//修改进度条
//特殊情况,progressBar和progressDialog进度条对话框可以在子线程当中修改UI,内部代码做过特殊的处理
//Message msg = new Message();//每次跟新界面都创建一个Message对象,显然效率会受到影响
Message msg = Message.obtain();//会在从消息池当中返回一个Message对象,避免重复创建
msg.what = PROGRESS_UPDATE;
handler.sendMessage(msg);
}

// 存储值:
// 1.采用已读数据(total)来记录下载了多少数据,这是不可行的,因为total每次接着断点下载时,被初始化为0
// 2.采用下载位置(startIndex+total)来记录当前线程下载的位置
raf.write((startIndex + total + "").getBytes());// 以字符串的方式将读取数据存放到文件当中,记录的就是下载的位置
raf.close();
}

is.close();
ras.close();
System.out.println("线程" + threadId + "下载完成");
} catch (Exception e) {
e.printStackTrace();
System.out.println("ThreadId"+threadId+"下载失败");

Message msg = new Message();
msg.what = REDOWNLOAD;
handler.sendMessage(msg);

//如果线程下载失败,再次重新下载
new DownLoadThread(path, threadId, startIndex,
endIndex).start();
RunningThread++;//finally必须要执行,但线程并没有下载完成,所以需要加回来
} finally {
threadFinsh();//线程执行完毕
}
}

//同步线程,保证将断点文件删除
private synchronized void threadFinsh() {
// 下载完成,将保存数据的文件删除
RunningThread--;// 记录了正在执行的线程,线程执行完毕,就将所有的记录文件删除

if (RunningThread == 0) {
for (int i = 1; i <= ThreadCount; i++) {
File deletefile = new File("/sdcard/"+i + ".txt");
deletefile.delete();
}
System.out.println("文件删除完");
Message msg = new Message();
msg.what = DOWNLOAD_FINSH;
handler.sendMessage(msg);
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: