多线程下载,断点下载





package com.example.lenovo.duoxianchengduandixiazai;

import android.util.Log;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
* 多线程文件下载工具类
* @author 马佳兴
*
*/




public class DownloadFileUtils {

private final String TAG = "DownloadFileUtils";
private String url;//下载地址
private long fileSize;//下载的文件大小
private long totalReadSize;//已读取的文件大小
private long block;//每条线程下载的长度
private int threadCount;//下载的线程数
private final int threadPoolNum = 5;//线程池的大小
private final int bufferSize = 1024 * 100;//缓冲区大小
private String fileName;//存储在本地的文件名称
private String filePath;//存储的路径
private HttpURLConnection urlConnection;
private RandomAccessFile randomAccessFile;//根据指定位置写入数据
private URL uri;
private DownloadFileCallback callback;//下载的回调接口
private ExecutorService executorService;//固定大小的线程池
private volatile boolean error = false;//全局变量,使用volatile同步,下载产生异常时改变
public DownloadFileUtils(String url,String filePath,String fileName,
int threadCount,DownloadFileCallback callback){
this.url = url;
this.filePath = filePath;
this.fileName = fileName;
this.threadCount = threadCount;
this.callback = callback;
}


public long getFileSize() {
return fileSize;
}

public long getTotalReadSize() {
return totalReadSize;
}

/**
* 文件下载
* @return true 下载成功 false 下载失败
*/

public boolean downloadFile(){
try {
uri = new URL(url);
urlConnection = (HttpURLConnection) uri.openConnection();
urlConnection.setRequestMethod("GET");
if(urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK){
fileSize = urlConnection.getContentLength();//获取文件的长度
block = fileSize / threadCount + 1;//为了避免文件长度缺失每条线程下载长度增加1
File file = new File(filePath,fileName);
if(!file.getParentFile().exists())
file.getParentFile().mkdirs();
executorService = Executors.newFixedThreadPool(threadPoolNum);
CountDownLatch countDownLatch = new CountDownLatch(threadCount);//线程计数器
for(int i = 0; i < threadCount; i++){
long startPosition = i * block;//每条线程的开始读取位置
long endPosition = (i+1) * block - 1;//每条线程的读取结束位置
randomAccessFile = new RandomAccessFile(file, "rwd");
randomAccessFile.seek(startPosition);
executorService.execute(new DownloadThread(i+1, startPosition, endPosition, randomAccessFile,countDownLatch));
}
countDownLatch.await();//阻塞线程,直到countDownLatch线程数为零
executorService.shutdown();
callback.downloadSuccess(null);//下载成功时的回调
Log.i(TAG, "下载成功。。。");
return true;
}
} catch (Exception e) {
callback.downloadError(e, "");//下载失败的回调
e.printStackTrace();
return false;
}
return false;
}


class DownloadThread implements Runnable {
private int threadId;
private long startPosition;
private long endPosition;
private RandomAccessFile randomAccessFile;
private CountDownLatch countDownLatch;

public DownloadThread(int threadId, long startPosition, long endPosition, RandomAccessFile randomAccessFile, CountDownLatch countDownLatch) {
this.threadId = threadId;
this.startPosition = startPosition;
this.endPosition = endPosition;
this.randomAccessFile = randomAccessFile;
this.countDownLatch = countDownLatch;
}


@Override
public void run() {
try {
HttpURLConnection connection = (HttpURLConnection) uri.openConnection();
connection.setRequestMethod("GET");//以GET方式连接
connection.setRequestProperty("Connection", "Keep-Alive");//维持长连接
connection.setConnectTimeout(5 * 60 * 1000);//设置连接超时
connection.setAllowUserInteraction(true);//允许用户交互
connection.setRequestProperty("Range", "bytes="+startPosition+"-"+endPosition);//设置每条线程开始下载的位置
InputStream inputStream = new BufferedInputStream(connection.getInputStream(), bufferSize);//使用缓冲区读取文件
byte[] b = new byte[bufferSize];
int len = 0;
while(!error && (len = inputStream.read(b)) != -1){
randomAccessFile.write(b,0,len);
totalReadSize += len;
}
if(!error)
Log.d(TAG, "线程"+threadId+"下载完成。。。");
else
Log.e(TAG, "线程"+threadId+"下载失败。。。");
inputStream.close();
randomAccessFile.close();
connection.disconnect();
countDownLatch.countDown();//每条线程执行完之后减一
} catch (Exception e) {
Log.e(TAG, "线程"+threadId+"下载失败。。。");
error = true;
e.printStackTrace();
callback.downloadError(e, "");//下载失败的回调
}
}

}
}

package com.example.lenovo.duoxianchengduandixiazai;


/**
* 下载回调
* @author 马佳兴
*
*/
public interface DownloadFileCallback {

void downloadSuccess(Object obj);//下载成功
void downloadError(Exception e,String msg);//下载失败

}



package com.example.lenovo.duoxianchengduandixiazai;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.icu.text.DecimalFormat;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.support.annotation.Nullable;
import android.util.Log;
import android.widget.RemoteViews;

import java.util.Timer;
import java.util.TimerTask;

/**
* Created by Lenovo on 2017/8/20.
*/

public class DownloadFileService extends Service {

private DownloadFileUtils downloadFileUtils;//文件下载工具类
private String filePath;//保存在本地的路径
private NotificationManager notificationManager;//状态栏通知管理类
private Notification notification;//状态栏通知
private RemoteViews remoteViews;//状态栏通知显示的view
private final int notificationID = 1;//通知的id
private final int updateProgress = 1;//更新状态栏的下载进度
private final int downloadSuccess = 2;//下载成功
private final int downloadError = 3;//下载失败
private final String TAG = "DownloadFileService";
private Timer timer;//定时器,用于更新下载进度
private TimerTask task;//定时器执行的任务


@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}

@Override
public void onCreate() {
init();
}

private void init() {
filePath = Environment.getExternalStorageDirectory() + "/aaa";
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notification = new Notification();
notification.icon = R.mipmap.ic_launcher;//设置通知消息的图标
notification.tickerText = "正在下载。。。";//设置通知消息的标题
remoteViews = new RemoteViews(getPackageName(), R.layout.down_notification);
remoteViews.setImageViewResource(R.id.IconIV, R.mipmap.ic_launcher);
timer = new Timer();
task = new TimerTask() {

@Override
public void run() {
handler.sendEmptyMessage(updateProgress);
}
};
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {

@Override
public void run() {
downloadFileUtils = new DownloadFileUtils("http://192.168.2.255/apk/abc.apk", filePath, "abc.apk", 3,callback);
downloadFileUtils.downloadFile();
}
}).start();
timer.schedule(task, 0, 500);
return super.onStartCommand(intent, flags, startId);
}

@Override
public void onDestroy() {
Log.i(TAG, TAG + " is onDestory...");
super.onDestroy();
}


Handler handler = new Handler() {

@Override
public void handleMessage(Message msg) {
if (msg.what == updateProgress) {//更新下载进度
long fileSize = downloadFileUtils.getFileSize();
long totalReadSize = downloadFileUtils.getTotalReadSize();
if (totalReadSize > 0) {
float size = (float) totalReadSize * 100 / (float) fileSize;
DecimalFormat format = new DecimalFormat("0.00");
String progress = format.format(size);
remoteViews.setTextViewText(R.id.progressTv, "已下载" + progress + "%");
remoteViews.setProgressBar(R.id.progressBar, 100, (int) size, false);
notification.contentView = remoteViews;
notificationManager.notify(notificationID, notification);
}
} else if (msg.what == downloadSuccess) {//下载完成
remoteViews.setTextViewText(R.id.progressTv, "下载完成");
remoteViews.setProgressBar(R.id.progressBar, 100, 100, false);
notification.contentView = remoteViews;
notificationManager.notify(notificationID, notification);
if (timer != null && task != null) {
timer.cancel();
task.cancel();
}
stopService(new Intent(getApplicationContext(), DownloadFileService.class));//stop service
} else if (msg.what == downloadError) {//下载失败
if (timer != null && task != null) {
timer.cancel();
task.cancel();
}
notificationManager.cancel(notificationID);
stopService(new Intent(getApplicationContext(), DownloadFileService.class));//stop service
}
}

};
/**
* 下载回调
*/
DownloadFileCallback callback = new DownloadFileCallback() {

@Override
public void downloadSuccess(Object obj) {
handler.sendEmptyMessage(downloadSuccess);
}

@Override
public void downloadError(Exception e, String msg) {
handler.sendEmptyMessage(downloadError);
}
};


}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值