1.对本地文件的操作, 利用了RandomAccessFile的可以对文件任意位置进行读写和修改的操作,RandomAccessFile的seek()可以由指定位置对文件进行写入数据操作
2. 对网络的操作,利用HttpURLConnection中的setRequestProperty()方法我们可以从指定的位置去下载数据
//设置下载起始位置
int start = threadBean.getStart() + threadBean.getFinished();
//传入当前进度给后台
connection.setRequestProperty("Range", "bytes=" + start + "-" + threadBean.getEnd());
TLog.log("第一次start" + start);
//设置写入位置
File file = new File(ConfigAppPath.downLoadPath, appBean.getAppName());
raf = new RandomAccessFile(file, "rwd");
raf.seek(start);
Intent pauseIntent = new Intent(context, DownloadService.class);
pauseIntent.setAction(DownloadService.ACTION_PAUSE);
pauseIntent.putExtra("PeriodBean", periodBean);
context.startService(pauseIntent);
HttpURLConnection connection = null;
RandomAccessFile randomAccessFile = null;
InputStream inputStream = null;
try {
URL url = new URL(appBean.getUrl());
connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(10000);
connection.setRequestMethod("GET");
connection.connect();
int fileLength = -1;
if(connection.getResponseCode() == HttpURLConnection.HTTP_OK){
fileLength = connection.getContentLength();
}
if(fileLength<=0) return;
if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
File dir0 = new File(ConfigPath.demsPath);
if (!dir0.exists()) {
dir0.mkdir();
}
File dir = new File(ConfigAppPath.downLoadPath);
if (!dir.exists()) {
dir.mkdir();
}
File file = new File(dir, appBean.getAppName());
randomAccessFile = new RandomAccessFile(file, "rwd");
randomAccessFile.setLength(fileLength);
}
//下载线程初始化完毕
AppInfoBean appBean = (AppInfoBean) eventMessage.getObject();
Log.w("AAA", "总length:" + appBean.getLength());
//开始下载
DownloadTask downloadTask = new DownloadTask(this, appBean, 3);
downloadTasks.add(downloadTask);
一个下载任务对应一个下载文件,在一个下载任务中我们可以创建多条线程进行下载,并用线程池管理,增加了下载的效率。假设我们这里创建了三条线程,我们先要查询数据库是否有对应的ThreadBean,每条线下下载的进度信息放入ThreadBean中,并存储到数据库中。
数据库中没有拿到ThreadBean信息,说明是刚开始下载,我们创建三个ThreadBean类分别供三条线程使用,每条线程下载整个文件的1/3大小,每个ThreadBean包含id,URL、开始、结束、完成位置,具体这几个字段会在DownloadThread中使用,初始化玩ThreadBean之后,将ThreadBean插入数据库。然后创建三条DownloadThread下载,用线程池管理
private void initDownThreads(int downloadThreadCount) {
//查询数据库中的下载线程信息
threads = dao.getThreads(periodBean.getUrl(), mPhone);
if (threads.size() == 0) {//如果列表没有数据 则为第一次下载
Log.w("AAA", "第一次下载");
//根据下载的线程总数平分各自下载的文件长度
int length = periodBean.getLength() / downloadThreadCount;
for (int i = 0; i < downloadThreadCount; i++) {
ThreadBean thread = new ThreadBean(i, periodBean.getUrl(), i * length,
(i + 1) * length - 1, 0, mPhone);
if (i == downloadThreadCount - 1) {
thread.setEnd(periodBean.getLength());
}
//将下载线程保存到数据库
dao.insertThread(thread);
threads.add(thread);
}
dao.insertPeriod(periodBean);
}
//创建下载线程开始下载
for (ThreadBean thread : threads) {
finishedProgress += thread.getFinished();
DownloadThread downloadThread = new DownloadThread(periodBean, thread, this);
DownloadService.executorService.execute(downloadThread);
downloadThreads.add(downloadThread);
}
Log.w("AAA", " 开始下载:" + finishedProgress);
}
这里开始就会用到本文开头介绍核心的地方,每次暂停时我们都会记录ThreadBean中finish位置(这个finish位置在进度条显示,下次开始下载的开始进度,下次写入文件的开始进度都会用到),我们开始下载时的start是初始化ThreadBean的start加上上次finish位置,例如如果是刚开始下载则finish为0,如果之前下载过一段,则finish有大小,下次开始的位置则需要加上上一次finish的大小,同理写入本地文件start同样操作。
下载通过while循环,获取的文件流不断读取字节方式进行,读取完成则表示该线程已下载完成,当点击暂停时,该任务会暂停所有线程,并保存当前进度,然后通过广播提示页面进度条显示。下次下载时会取出数据库中的ThreadBean重复步骤4。
//暂停下载
else if (intent.getAction().equals(ACTION_PAUSE)) {
AppInfoBean appBean = (AppInfoBean) intent.getSerializableExtra("appBean");
DownloadTask pauseTask = null;
for (DownloadTask downloadTask : downloadTasks) {
if (downloadTask.getFileBean().getId().equals(appBean.getId())) {
downloadTask.pauseDownload();
pauseTask = downloadTask;
break;
}
}
//将下载任务移除
downloadTasks.remove(pauseTask);
}
HttpURLConnection connection = null;
RandomAccessFile raf = null;
InputStream inputStream = null;
try {
URL url = new URL(threadBean.getUrl());
connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(10000);
connection.setRequestMethod("GET");
//设置下载起始位置
int start = threadBean.getStart() + threadBean.getFinished();
//传入当前进度给后台
connection.setRequestProperty("Range", "bytes=" + start + "-" + threadBean.getEnd());
TLog.log("第一次start" + start);
//设置写入位置
File file = new File(ConfigAppPath.downLoadPath, appBean.getAppName());
raf = new RandomAccessFile(file, "rwd");
raf.seek(start);
//开始下载
if (connection.getResponseCode() == HttpURLConnection.HTTP_PARTIAL) {
//获得文件流
inputStream = connection.getInputStream();
byte[] bytes = new byte[512];
int len = -1;
int count = 0;
int i = 0;
while ((len = inputStream.read(bytes)) != -1) {
//i++;
//TLog.log("count相加" + (count += len));
//写入文件
raf.write(bytes, 0, len);
//将加载的进度回调出去
callback.progressCallBack(len);
//TLog.log("len" + "i=" + i + "-" +(count += len));
//保存进度
threadBean.setFinished(threadBean.getFinished() + len);
//TLog.log("及时进度" + (threadBean.getFinished() + len));
//在下载暂停的时候将下载进度保存到数据库
if (isPause) {
callback.pauseCallBack(threadBean);
return;
}
}
//下载完成
callback.threadDownLoadFinished(threadBean);
}
} catch (Exception e) {
e.printStackTrace();
}
@Override
public void progressCallBack(int length) {
//i++;
//TLog.log("length" +"i=" + i + "-" + length);
finishedProgress += length;
//每100毫秒发送刷新进度事件
if (System.currentTimeMillis() - curTime > 100 || finishedProgress == periodBean.getLength()) {
TLog.log("finishedprogress", finishedProgress + "");
periodBean.setFinished(finishedProgress);
EventMessage message = new EventMessage(3, periodBean);
EventBus.getDefault().post(message);
curTime = System.currentTimeMillis();
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void getEventMessage(EventMessage eventMessage) {
switch (eventMessage.getType()) {
case 2://下载完成
break;
case 3://下载进度刷新
break;
case 4://点击暂停
break;
case 6://下载失败异常
break;
}
}
//合并mPeriods
private void refreshData() {
//获得数据库中的items
List<OfflinePeriodInfo> periodsData = mDao.getPeriods(SysCode.OFFLINE_DOWNLOAD_URL, mPhone);
if (periodsData != null && periodsData.size() != 0) {
for (int j = 0; j < mPeriods.size(); j++) {
for (int k = 0; k < periodsData.size(); k++) {
if (mPeriods.get(j).getId().equals(periodsData.get(k).getId())) {
mPeriods.get(j).setFinished(periodsData.get(k).getFinished());
mPeriods.get(j).setLength(periodsData.get(k).getLength());
}
}
}
}
}
final ViewHolder viewHolder;
//if (convertView == null || convertView.getTag() == null) {
convertView = getLayoutInflater(parent.getContext()).inflate(
R.layout.item_offline_download, null);
viewHolder = new ViewHolder(convertView);
/*} else {
viewHolder = (ViewHolder) convertView.getTag();
}*/
if (_data != null && _data.size() > 0) {
final OfflinePeriodInfo periodBean = (OfflinePeriodInfo) _data.get(position);
viewHolder.tvProject.setText(periodBean.getPeriodName());
int progress = (int) (periodBean.getFinished() * 1.0f / periodBean.getLength() * 100);
if (periodBean.getLength() != 0) {
viewHolder.progressBarDownload.setProgress(progress);
}
if (progress > 0 && progress < 100) {
viewHolder.tvDownload.setVisibility(View.VISIBLE);
viewHolder.tvPauseUpload.setVisibility(View.GONE);
viewHolder.tvDownload.setText("继续");
viewHolder.tvDownload.setTextColor(context.getResources().getColor(R.color.off_line_pause));
}
if (progress == 100) {
viewHolder.tvDownload.setVisibility(View.VISIBLE);
viewHolder.tvPauseUpload.setVisibility(View.GONE);
viewHolder.tvDownload.setText("完成");
viewHolder.progressBarDownload.setProgress(progress);
viewHolder.tvDownload.setTextColor(context.getResources().getColor(R.color.common_blue_color));
}
//点击下载,
viewHolder.tvDownload.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//判断网络
if (judgeInternet()) return;
periodBean.setPosition(position);
Intent startIntent = new Intent(context, DownloadService.class);
startIntent.setAction(DownloadService.ACTION_START);
startIntent.putExtra("PeriodBean", periodBean);
context.startService(startIntent);
}
});
//点击暂停
viewHolder.tvPauseDownload.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (judgeInternet()) return;
Intent pauseIntent = new Intent(context, DownloadService.class);
pauseIntent.setAction(DownloadService.ACTION_PAUSE);
pauseIntent.putExtra("PeriodBean", periodBean);
context.startService(pauseIntent);
}
});
}