Android 使用AsyncTask实现多任务多线程断点续传下载

这篇博客是AsyncTask下载系列的最后一篇文章,前面写了关于断点续传的和多线程下载的博客,这篇是在前两篇的基础上面实现的,有兴趣的可以去看下。

  一、AsyncTask实现断点续传

  二、AsyncTask实现多线程断点续传

  这里模拟应用市场app下载实现了一个Demo,因为只有一个界面,所以没有将下载放到Service中,而是直接在Activity中创建。在正式的项目中,下载都是放到Service中,然后通过BroadCast通知界面更新进度。

  上代码之前,先看下demo的运行效果图吧。

  下面我们看代码,这里每一个文件的下载定义一个Downloador来管理下载该文件的所有线程(暂停、下载等)。Downloador创建3个DownloadTask(这里定义每个文件分配3个线程下载)来下载该文件特定的起止位置。这里要通过文件的大小来计算每个线程所下载的起止位置,详细可以参考《AsyncTask实现多线程断点续传》。

  1、Downloador类

package com.bbk.lling.multitaskdownload.downloador;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
import com.bbk.lling.multitaskdownload.beans.AppContent;
import com.bbk.lling.multitaskdownload.beans.DownloadInfo;
import com.bbk.lling.multitaskdownload.db.DownloadFileDAO;
import com.bbk.lling.multitaskdownload.db.DownloadInfoDAO;
import com.bbk.lling.multitaskdownload.utils.DownloadUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
 * @Class: Downloador
 * @Description: 任务下载器
 * @author: lling(www.cnblogs.com/liuling)
 * @Date: 2015/10/13
 */
public class Downloador {
 public static final String TAG = "Downloador";
 private static final int THREAD_POOL_SIZE = 9; //线程池大小为9
 private static final int THREAD_NUM = 3; //每个文件3个线程下载
 private static final int GET_LENGTH_SUCCESS = 1;
 public static final Executor THREAD_POOL_EXECUTOR = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
 private List<DownloadTask> tasks;
 private InnerHandler handler = new InnerHandler();
 private AppContent appContent; //待下载的应用
 private long downloadLength; //下载过程中记录已下载大小
 private long fileLength;
 private Context context;
 private String downloadPath;
 public Downloador(Context context, AppContent appContent) {
 this.context = context;
 this.appContent = appContent;
 this.downloadPath = DownloadUtils.getDownloadPath();
 }
 /**
 * 开始下载
 */
 public void download() {
 if(TextUtils.isEmpty(downloadPath)) {
 Toast.makeText(context, "未找到SD卡", Toast.LENGTH_SHORT).show();
 return;
 }
 if(appContent == null) {
 throw new IllegalArgumentException("download content can not be null");
 }
 new Thread() {
 @Override
 public void run() {
 //获取文件大小
 HttpClient client = new DefaultHttpClient();
 HttpGet request = new HttpGet(appContent.getUrl());
 HttpResponse response = null;
 try {
  response = client.execute(request);
  fileLength = response.getEntity().getContentLength();
 } catch (Exception e) {
  Log.e(TAG, e.getMessage());
 } finally {
  if (request != null) {
  request.abort();
  }
 }
 //计算出该文件已经下载的总长度
 List<DownloadInfo> lists = DownloadInfoDAO.getInstance(context.getApplicationContext())
  .getDownloadInfosByUrl(appContent.getUrl());
 for (DownloadInfo info : lists) {
  downloadLength += info.getDownloadLength();
 }
 //插入文件下载记录到数据库
 DownloadFileDAO.getInstance(context.getApplicationContext()).insertDownloadFile(appContent);
 Message.obtain(handler, GET_LENGTH_SUCCESS).sendToTarget();
 }
 }.start();
 }
 /**
 * 开始创建AsyncTask下载
 */
 private void beginDownload() {
 Log.e(TAG, "beginDownload" + appContent.getUrl());
 appContent.setStatus(AppContent.Status.WAITING);
 long blockLength = fileLength / THREAD_NUM;
 for (int i = 0; i < THREAD_NUM; i++) {
 long beginPosition = i * blockLength;//每条线程下载的开始位置
 long endPosition = (i + 1) * blockLength;//每条线程下载的结束位置
 if (i == (THREAD_NUM - 1)) {
 endPosition = fileLength;//如果整个文件的大小不为线程个数的整数倍,则最后一个线程的结束位置即为文件的总长度
 }
 DownloadTask task = new DownloadTask(i, beginPosition, endPosition, this, context);
 task.executeOnExecutor(THREAD_POOL_EXECUTOR, appContent.getUrl());
 if(tasks == null) {
 tasks = new ArrayList<DownloadTask>();
 }
 tasks.add(task);
 }
 }
 /**
 * 暂停下载
 */
 public void pause() {
 for (DownloadTask task : tasks) {
 if (task != null && (task.getStatus() == AsyncTask.Status.RUNNING || !task.isCancelled())) {
 task.cancel(true);
 }
 }
 tasks.clear();
 appContent.setStatus(AppContent.Status.PAUSED);
 DownloadFileDAO.getInstance(context.getApplicationContext()).updateDownloadFile(appContent);
 }
 /**
 * 将已下载大小归零
 */
 protected synchronized void resetDownloadLength() {
 this.downloadLength = 0;
 }
 /**
 * 添加已下载大小
 * 多线程访问需加锁
 * @param size
 */
 protected synchronized void updateDownloadLength(long size){
 this.downloadLength += size;
 //通知更新界面
 int percent = (int)((float)downloadLength * 100 / (float)fileLength);
 appContent.setDownloadPercent(percent);
 if(percent == 100 || downloadLength == fileLength) {
 appContent.setDownloadPercent(100); //上面计算有时候会有点误差,算到percent=99
 appContent.setStatus(AppContent.Status.FINISHED);
 DownloadFileDAO.getInstance(context.getApplicationContext()).updateDownloadFile(appContent);
 }
 Intent intent = new Intent(Constants.DOWNLOAD_MSG);
 if(appContent.getStatus() == AppContent.Status.WAITING) {
 appContent.setStatus(AppContent.Status.DOWNLOADING);
 }
 Bundle bundle = new Bundle();
 bundle.putParcelable("appContent", appContent);
 intent.putExtras(bundle);
 context.sendBroadcast(intent);
 }
 protected String getDownloadPath() {
 return downloadPath;
 }
 private class InnerHandler extends Handler {
 @Override
 public void handleMessage(Message msg) {
 switch (msg.what) {
 case GET_LENGTH_SUCCESS :
  beginDownload();
  break;
 }
 super.handleMessage(msg);
 }
 }
}

 2、DownloadTask类

package com.bbk.lling.multitaskdownload.downloador;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import com.bbk.lling.multitaskdownload.beans.DownloadInfo;
import com.bbk.lling.multitaskdownload.db.DownloadInfoDAO;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicHeader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.MalformedURLException;
/**
 * @Class: DownloadTask
 * @Description: 文件下载AsyncTask
 * @author: lling(www.cnblogs.com/liuling)
 * @Date: 2015/10/13
 */
public class DownloadTask extends AsyncTask<String, Integer , Long> {
 private static final String TAG = "DownloadTask";
 private int taskId;
 private long beginPosition;
 private long endPosition;
 private long downloadLength;
 private String url;
 private Downloador downloador;
 private DownloadInfoDAO downloadInfoDAO;
 public DownloadTask(int taskId, long beginPosition, long endPosition, Downloador downloador,
  Context context) {
 this.taskId = taskId;
 this.beginPosition = beginPosition;
 this.endPosition = endPosition;
 this.downloador = downloador;
 downloadInfoDAO = DownloadInfoDAO.getInstance(context.getApplicationContext());
 }
 @Override
 protected void onPreExecute() {
 Log.e(TAG, "onPreExecute");
 }
 @Override
 protected void onPostExecute(Long aLong) {
 Log.e(TAG, url + "taskId:" + taskId + "executed");
// downloador.updateDownloadInfo(null);
 }
 @Override
 protected void onProgressUpdate(Integer... values) {
 //通知downloador增加已下载大小
// downloador.updateDownloadLength(values[0]);
 }
 @Override
 protected void onCancelled() {
 Log.e(TAG, "onCancelled");
// downloador.updateDownloadInfo(null);
 }
 @Override
 protected Long doInBackground(String... params) {
 //这里加判断的作用是:如果还处于等待就暂停了,运行到这里已经cancel了,就直接退出
 if(isCancelled()) {
 return null;
 }
 url = params[0];
 if(url == null) {
 return null;
 }
 HttpClient client = new DefaultHttpClient();
 HttpGet request = new HttpGet(url);
 HttpResponse response;
 InputStream is;
 RandomAccessFile fos = null;
 OutputStream output = null;
 DownloadInfo downloadInfo = null;
 try {
 //本地文件
 File file = new File(downloador.getDownloadPath() + File.separator + url.substring(url.lastIndexOf("/") + 1));
 //获取之前下载保存的信息
 downloadInfo = downloadInfoDAO.getDownloadInfoByTaskIdAndUrl(taskId, url);
 //从之前结束的位置继续下载
 //这里加了判断file.exists(),判断是否被用户删除了,如果文件没有下载完,但是已经被用户删除了,则重新下载
 if(file.exists() && downloadInfo != null) {
 if(downloadInfo.isDownloadSuccess() == 1) {
  //下载完成直接结束
  return null;
 }
 beginPosition = beginPosition + downloadInfo.getDownloadLength();
 downloadLength = downloadInfo.getDownloadLength();
 }
 if(!file.exists()) {
 //如果此task已经下载完,但是文件被用户删除,则需要重新设置已下载长度,重新下载
 downloador.resetDownloadLength();
 }
 //设置下载的数据位置beginPosition字节到endPosition字节
 Header header_size = new BasicHeader("Range", "bytes=" + beginPosition + "-" + endPosition);
 request.addHeader(header_size);
 //执行请求获取下载输入流
 response = client.execute(request);
 is = response.getEntity().getContent();
 //创建文件输出流
 fos = new RandomAccessFile(file, "rw");
 //从文件的size以后的位置开始写入
 fos.seek(beginPosition);
 byte buffer [] = new byte[1024];
 int inputSize = -1;
 while((inputSize = is.read(buffer)) != -1) {
 fos.write(buffer, 0, inputSize);
 downloadLength += inputSize;
 downloador.updateDownloadLength(inputSize);
 //如果暂停了,需要将下载信息存入数据库
 if (isCancelled()) {
  if(downloadInfo == null) {
  downloadInfo = new DownloadInfo();
  }
  downloadInfo.setUrl(url);
  downloadInfo.setDownloadLength(downloadLength);
  downloadInfo.setTaskId(taskId);
  downloadInfo.setDownloadSuccess(0);
  //保存下载信息到数据库
  downloadInfoDAO.insertDownloadInfo(downloadInfo);
  return null;
 }
 }
 } catch (MalformedURLException e) {
 Log.e(TAG, e.getMessage());
 } catch (IOException e) {
 Log.e(TAG, e.getMessage());
 } finally{
 try{
 if (request != null) {
  request.abort();
 }
 if(output != null) {
  output.close();
 }
 if(fos != null) {
  fos.close();
 }
 } catch(Exception e) {
 e.printStackTrace();
 }
 }
 //执行到这里,说明该task已经下载完了
 if(downloadInfo == null) {
 downloadInfo = new DownloadInfo();
 }
 downloadInfo.setUrl(url);
 downloadInfo.setDownloadLength(downloadLength);
 downloadInfo.setTaskId(taskId);
 downloadInfo.setDownloadSuccess(1);
 //保存下载信息到数据库
 downloadInfoDAO.insertDownloadInfo(downloadInfo);
 return null;
 }
}

Downloador和DownloadTask只这个例子的核心代码,下面是关于数据库的,因为要实现断点续传必须要在暂停的时候将每个线程下载的位置记录下来,方便下次继续下载时读取。这里有两个表,一个是存放每个文件的下载状态的,一个是存放每个文件对应的每个线程的下载状态的。

  3、DBHelper

package com.bbk.lling.multitaskdownload.db;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
/**
 * @Class: DBHelper
 * @Description: 数据库帮助类
 * @author: lling(www.cnblogs.com/liuling)
 * @Date: 2015/10/14
 */
public class DBHelper extends SQLiteOpenHelper {
 public DBHelper(Context context) {
 super(context, "download.db", null, 1);
 }
 @Override
 public void onCreate(SQLiteDatabase db) {
 db.execSQL("create table download_info(_id INTEGER PRIMARY KEY AUTOINCREMENT, task_id INTEGER, "
 + "download_length INTEGER, url VARCHAR(255), is_success INTEGER)");
 db.execSQL("create table download_file(_id INTEGER PRIMARY KEY AUTOINCREMENT, app_name VARCHAR(255), "
 + "url VARCHAR(255), download_percent INTEGER, status INTEGER)");
 }
 @Override
 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
 }
}

4、DownloadFileDAO,文件下载状态的数据库操作类

package com.bbk.lling.multitaskdownload.db;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.text.TextUtils;
import android.util.Log;
import com.bbk.lling.multitaskdownload.beans.AppContent;
import java.util.ArrayList;
import java.util.List;
/**
 * @Class: DownloadFileDAO
 * @Description: 每个文件下载状态记录的数据库操作类
 * @author: lling(www.cnblogs.com/liuling)
 * @Date: 2015/10/13
 */
public class DownloadFileDAO {
 private static final String TAG = "DownloadFileDAO";
 private static DownloadFileDAO dao=null;
 private Context context;
 private DownloadFileDAO(Context context) {
 this.context=context;
 }
 synchronized public static DownloadFileDAO getInstance(Context context){
 if(dao==null){
 dao=new DownloadFileDAO(context);
 }
 return dao;
 }
 /**
 * 获取数据库连接
 * @return
 */
 public SQLiteDatabase getConnection() {
 SQLiteDatabase sqliteDatabase = null;
 try {
 sqliteDatabase= new DBHelper(context).getReadableDatabase();
 } catch (Exception e) {
 Log.e(TAG, e.getMessage());
 }
 return sqliteDatabase;
 }
 /**
 * 插入数据
 * @param appContent
 */
 public void insertDownloadFile(AppContent appContent) {
 if(appContent == null) {
 return;
 }
 //如果本地已经存在,直接修改
 if(getAppContentByUrl(appContent.getUrl()) != null) {
 updateDownloadFile(appContent);
 return;
 }
 SQLiteDatabase database = getConnection();
 try {
 String sql = "insert into download_file(app_name, url, download_percent, status) values (?,?,?,?)";
 Object[] bindArgs = { appContent.getName(), appContent.getUrl(), appContent.getDownloadPercent()
  , appContent.getStatus().getValue()};
 database.execSQL(sql, bindArgs);
 } catch (Exception e) {
 Log.e(TAG, e.getMessage());
 } finally {
 if (null != database) {
 database.close();
 }
 }
 }
 /**
 * 根据url获取下载文件信息
 * @param url
 * @return
 */
 public AppContent getAppContentByUrl(String url) {
 if(TextUtils.isEmpty(url)) {
 return null;
 }
 SQLiteDatabase database = getConnection();
 AppContent appContent = null;
 Cursor cursor = null;
 try {
 String sql = "select * from download_file where url=?";
 cursor = database.rawQuery(sql, new String[] { url });
 if (cursor.moveToNext()) {
 appContent = new AppContent(cursor.getString(1), cursor.getString(2));
 appContent.setDownloadPercent(cursor.getInt(3));
 appContent.setStatus(AppContent.Status.getByValue(cursor.getInt(4)));
 }
 } catch (Exception e) {
 Log.e(TAG, e.getMessage());
 } finally {
 if (null != database) {
 database.close();
 }
 if (null != cursor) {
 cursor.close();
 }
 }
 return appContent;
 }
 /**
 * 更新下载信息
 * @param appContent
 */
 public void updateDownloadFile(AppContent appContent) {
 if(appContent == null) {
 return;
 }
 SQLiteDatabase database = getConnection();
 try {
 Log.e(TAG, "update download_file,app name:" + appContent.getName() + ",url:" + appContent.getUrl()
  + ",percent" + appContent.getDownloadPercent() + ",status:" + appContent.getStatus().getValue());
 String sql = "update download_file set app_name=?, url=?, download_percent=?, status=? where url=?";
 Object[] bindArgs = {appContent.getName(), appContent.getUrl(), appContent.getDownloadPercent()
  , appContent.getStatus().getValue(), appContent.getUrl()};
 database.execSQL(sql, bindArgs);
 } catch (Exception e) {
 Log.e(TAG, e.getMessage());
 } finally {
 if (null != database) {
 database.close();
 }
 }
 }
 /**
 * 获取所有下载文件记录
 * @return
 */
 public List<AppContent> getAll() {
 SQLiteDatabase database = getConnection();
 List<AppContent> list = new ArrayList<AppContent>();
 Cursor cursor = null;
 try {
 String sql = "select * from download_file";
 cursor = database.rawQuery(sql, null);
 while (cursor.moveToNext()) {
 AppContent appContent = new AppContent(cursor.getString(1), cursor.getString(2));
 appContent.setDownloadPercent(cursor.getInt(3));
 appContent.setStatus(AppContent.Status.getByValue(cursor.getInt(4)));
 list.add(appContent);
 }
 } catch (Exception e) {
 Log.e(TAG, e.getMessage());
 } finally {
 if (null != database) {
 database.close();
 }
 if (null != cursor) {
 cursor.close();
 }
 }
 return list;
 }
 /**
 * 根据url删除记录
 * @param url
 */
 public void delByUrl(String url) {
 if(TextUtils.isEmpty(url)) {
 return;
 }
 SQLiteDatabase database = getConnection();
 try {
 String sql = "delete from download_file where url=?";
 Object[] bindArgs = { url };
 database.execSQL(sql, bindArgs);
 } catch (Exception e) {
 Log.e(TAG, e.getMessage());
 } finally {
 if (null != database) {
 database.close();
 }
 }
 }
}

5、DownloadInfoDAO,每个线程对应下载状态的数据库操作类

package com.bbk.lling.multitaskdownload.db;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.text.TextUtils;
import android.util.Log;
import com.bbk.lling.multitaskdownload.beans.DownloadInfo;
import java.util.ArrayList;
import java.util.List;
/**
 * @Class: DownloadInfoDAO
 * @Description: 每个单独线程下载信息记录的数据库操作类
 * @author: lling(www.cnblogs.com/liuling)
 * @Date: 2015/10/13
 */
public class DownloadInfoDAO {
 private static final String TAG = "DownloadInfoDAO";
 private static DownloadInfoDAO dao=null;
 private Context context;
 private DownloadInfoDAO(Context context) {
 this.context=context;
 }
 synchronized public static DownloadInfoDAO getInstance(Context context){
 if(dao==null){
 dao=new DownloadInfoDAO(context);
 }
 return dao;
 }
 /**
 * 获取数据库连接
 * @return
 */
 public SQLiteDatabase getConnection() {
 SQLiteDatabase sqliteDatabase = null;
 try {
 sqliteDatabase= new DBHelper(context).getReadableDatabase();
 } catch (Exception e) {
 Log.e(TAG, e.getMessage());
 }
 return sqliteDatabase;
 }
 /**
 * 插入数据
 * @param downloadInfo
 */
 public void insertDownloadInfo(DownloadInfo downloadInfo) {
 if(downloadInfo == null) {
 return;
 }
 //如果本地已经存在,直接修改
 if(getDownloadInfoByTaskIdAndUrl(downloadInfo.getTaskId(), downloadInfo.getUrl()) != null) {
 updateDownloadInfo(downloadInfo);
 return;
 }
 SQLiteDatabase database = getConnection();
 try {
 String sql = "insert into download_info(task_id, download_length, url, is_success) values (?,?,?,?)";
 Object[] bindArgs = { downloadInfo.getTaskId(), downloadInfo.getDownloadLength(),
  downloadInfo.getUrl(), downloadInfo.isDownloadSuccess()};
 database.execSQL(sql, bindArgs);
 } catch (Exception e) {
 Log.e(TAG, e.getMessage());
 } finally {
 if (null != database) {
 database.close();
 }
 }
 }
 public List<DownloadInfo> getDownloadInfosByUrl(String url) {
 if(TextUtils.isEmpty(url)) {
 return null;
 }
 SQLiteDatabase database = getConnection();
 List<DownloadInfo> list = new ArrayList<DownloadInfo>();
 Cursor cursor = null;
 try {
 String sql = "select * from download_info where url=?";
 cursor = database.rawQuery(sql, new String[] { url });
 while (cursor.moveToNext()) {
 DownloadInfo info = new DownloadInfo();
 info.setTaskId(cursor.getInt(1));
 info.setDownloadLength(cursor.getLong(2));
 info.setDownloadSuccess(cursor.getInt(4));
 info.setUrl(cursor.getString(3));
 list.add(info);
 }
 } catch (Exception e) {
 e.printStackTrace();
 } finally {
 if (null != database) {
 database.close();
 }
 if (null != cursor) {
 cursor.close();
 }
 }
 return list;
 }
 /**
 * 根据taskid和url获取下载信息
 * @param taskId
 * @param url
 * @return
 */
 public DownloadInfo getDownloadInfoByTaskIdAndUrl(int taskId, String url) {
 if(TextUtils.isEmpty(url)) {
 return null;
 }
 SQLiteDatabase database = getConnection();
 DownloadInfo info = null;
 Cursor cursor = null;
 try {
 String sql = "select * from download_info where url=? and task_id=?";
 cursor = database.rawQuery(sql, new String[] { url, String.valueOf(taskId) });
 if (cursor.moveToNext()) {
 info = new DownloadInfo();
 info.setTaskId(cursor.getInt(1));
 info.setDownloadLength(cursor.getLong(2));
 info.setDownloadSuccess(cursor.getInt(4));
 info.setUrl(cursor.getString(3));
 }
 } catch (Exception e) {
 Log.e(TAG, e.getMessage());
 } finally {
 if (null != database) {
 database.close();
 }
 if (null != cursor) {
 cursor.close();
 }
 }
 return info;
 }
 /**
 * 更新下载信息
 * @param downloadInfo
 */
 public void updateDownloadInfo(DownloadInfo downloadInfo) {
 if(downloadInfo == null) {
 return;
 }
 SQLiteDatabase database = getConnection();
 try {
 String sql = "update download_info set download_length=?, is_success=? where task_id=? and url=?";
 Object[] bindArgs = { downloadInfo.getDownloadLength(), downloadInfo.isDownloadSuccess(),
  downloadInfo.getTaskId(), downloadInfo.getUrl() };
 database.execSQL(sql, bindArgs);
 } catch (Exception e) {
 Log.e(TAG, e.getMessage());
 } finally {
 if (null != database) {
 database.close();
 }
 }
 }
}

具体的界面和使用代码我就不贴代码了,代码有点多。需要的可以下载Demo的源码看看。

  因为还没有花太多时间去测,里面难免会有些bug,如果大家发现什么问题,欢迎留言探讨,谢谢!  

源码下载:https://github.com/liuling07/MultiTaskAndThreadDownload

(0)

相关推荐

  • Android 使用AsyncTask实现断点续传

    之前公司里面项目的下载模块都是使用xUtils提供的,最近看了下xUtils的源码,它里面也是使用AsyncTask来执行异步任务的,它的下载也包含了断点续传的功能.这里我自己也使用AsyncTask也实现了简单的断点续传的功能. 首先说一说AsyncTask吧,先来看看AsyncTask的定义: public abstract class AsyncTask<Params, Progress, Result> 三种泛型类型分别代表"启动任务执行的输入参数"."后

  • Android 使用AsyncTask实现多线程断点续传

    前面一篇博客<AsyncTask实现断点续传>讲解了如何实现单线程下的断点续传,也就是一个文件只有一个线程进行下载.    对于大文件而言,使用多线程下载就会比单线程下载要快一些.多线程下载相比单线程下载要稍微复杂一点,本博文将详细讲解如何使用AsyncTask来实现多线程的断点续传下载. 一.实现原理 多线程下载首先要通过每个文件总的下载线程数(我这里设定5个)来确定每个线程所负责下载的起止位置. long blockLength = mFileLength / DEFAULT_POOL_S

  • Android多线程AsyncTask详解

    本篇随笔将讲解一下Android的多线程的知识,以及如何通过AsyncTask机制来实现线程之间的通信. 一.android当中的多线程 在Android当中,当一个应用程序的组件启动的时候,并且没有其他的应用程序组件在运行时,Android系统就会为该应用程序组件开辟一个新的线程来执行.默认的情况下,在一个相同Android应用程序当中,其里面的组件都是运行在同一个线程里面的,这个线程我们称之为Main线程.当我们通过某个组件来启动另一个组件的时候,这个时候默认都是在同一个线程当中完成的.当然

  • android使用AsyncTask实现多线程下载实例

    AsyncTask不仅方便我们在子线程中对UI进行更新操作,还可以借助其本身的线程池来实现多线程任务.下面是一个使用AsyncTask来实现的多线程下载例子. 01 效果图 02 核心类 - DownloadTask.class public class DownloadTask extends AsyncTask<String, Integer, Integer> { public static final int TYPE_SUCCESS = 0; public static final

  • Android开发笔记之:深入理解多线程AsyncTask

    Understanding AsyncTaskAsyncTask是Android 1.5 Cubake加入的用于实现异步操作的一个类,在此之前只能用Java SE库中的Thread来实现多线程异步,AsyncTask是Android平台自己的异步工具,融入了Android平台的特性,让异步操作更加的安全,方便和实用.实质上它也是对Java SE库中Thread的一个封装,加上了平台相关的特性,所以对于所有的多线程异步都强烈推荐使用AsyncTask,因为它考虑,也融入了Android平台的特性,

  • Android使用AsyncTask实现多线程下载的方法

    本文实例讲述了Android使用AsyncTask实现多线程下载的方法.分享给大家供大家参考,具体如下: public class MainActivity extends Activity implements OnClickListener { private Button btn1, btn2, btn3; private ProgressBar progressBar1, progressBar2, progressBar3; private ImageView img1, img2,

  • Android 使用AsyncTask实现多任务多线程断点续传下载

    这篇博客是AsyncTask下载系列的最后一篇文章,前面写了关于断点续传的和多线程下载的博客,这篇是在前两篇的基础上面实现的,有兴趣的可以去看下. 一.AsyncTask实现断点续传 二.AsyncTask实现多线程断点续传 这里模拟应用市场app下载实现了一个Demo,因为只有一个界面,所以没有将下载放到Service中,而是直接在Activity中创建.在正式的项目中,下载都是放到Service中,然后通过BroadCast通知界面更新进度. 上代码之前,先看下demo的运行效果图吧. 下面

  • Android编程开发实现多线程断点续传下载器实例

    本文实例讲述了Android编程开发实现多线程断点续传下载器.分享给大家供大家参考,具体如下: 使用多线程断点续传下载器在下载的时候多个线程并发可以占用服务器端更多资源,从而加快下载速度,在下载过程中记录每个线程已拷贝数据的数量,如果下载中断,比如无信号断线.电量不足等情况下,这就需要使用到断点续传功能,下次启动时从记录位置继续下载,可避免重复部分的下载.这里采用数据库来记录下载的进度. 效果图:   断点续传 1.断点续传需要在下载过程中记录每条线程的下载进度 2.每次下载开始之前先读取数据库

  • Android多线程断点续传下载功能实现代码

    原理 其实断点续传的原理很简单,从字面上理解,所谓断点续传就是从停止的地方重新下载. 断点:线程停止的位置. 续传:从停止的位置重新下载. 用代码解析就是: 断点:当前线程已经下载完成的数据长度. 续传:向服务器请求上次线程停止位置之后的数据. 原理知道了,功能实现起来也简单.每当线程停止时就把已下载的数据长度写入记录文件,当重新下载时,从记录文件读取已经下载了的长度.而这个长度就是所需要的断点. 续传的实现也简单,可以通过设置网络请求参数,请求服务器从指定的位置开始读取数据. 而要实现这两个功

  • Android多线程断点续传下载示例详解

    一.概述 在上一篇博文<Android多线程下载示例>中,我们讲解了如何实现Android的多线程下载功能,通过将整个文件分成多个数据块,开启多个线程,让每个线程分别下载一个相应的数据块来实现多线程下载的功能.多线程下载中,可以将下载这个耗时的操作放在子线程中执行,即不阻塞主线程,又符合Android开发的设计规范. 但是当下载的过程当中突然出现手机卡死,或者网络中断,手机电量不足关机的现象,这时,当手机可以正常使用后,如果重新下载文件,似乎不太符合大多数用户的心理期望,那如何实现当手机可以正

  • Android实现网络多线程断点续传下载功能

    我们编写的是Andorid的HTTP协议多线程断点下载应用程序.直接使用单线程下载HTTP文件对我们来说是一件非常简单的事.那么,多线程断点需要什么功能? 1.多线程下载 2.支持断点 使用多线程的好处:使用多线程下载会提升文件下载的速度 原理 多线程下载的原理就是将要下载的文件分成若干份,其中每份都使用一个单独的线程进行下载,这样对于文件的下载速度自然就提高了许多. 既然要分成若干部分分工下载,自然要知道各个线程自己要下载的起始位置,与要下载的大小.所以我们要解决线程的分配与各个线程定位到下载

  • Android FTP 多线程断点续传下载\上传的实例

    最近在给我的开源下载框架Aria增加FTP断点续传下载和上传功能,在此过程中,爬了FTP的不少坑,终于将功能实现了,在此把一些核心功能点记录下载. FTP下载原理 FTP单线程断点续传 FTP和传统的HTTP协议有所不同,由于FTP没有所谓的头文件,因此我们不能像HTTP那样通过设置header向服务器指定下载区间. 但是FTP协议提供了一个更好用的命令REST用于从指定位置恢复任务,同时FTP协议也提供了一个命令SIZE用于获取下载的文件大小,有了这两个命令,FTP断点续传也就没有什么问题.

  • Android实现网络多线程断点续传下载实例

    我们编写的是Andorid的HTTP协议多线程断点下载应用程序.直接使用单线程下载HTTP文件对我们来说是一件非常简单的事.那么,多线程断点需要什么功能? 1.多线程下载, 2.支持断点. 使用多线程的好处:使用多线程下载会提升文件下载的速度.那么多线程下载文件的过程是: (1)首先获得下载文件的长度,然后设置本地文件的长度. HttpURLConnection.getContentLength();//获取下载文件的长度 RandomAccessFile file = new RandomAc

  • Android多线程断点续传下载实现代码

    学习了多线程下载,而且可以断点续传的逻辑,线程数量可以自己选择,但是线程数量过多手机就承受不起,导致闪退,好在有断点续传. 步骤写在了代码的注释里.大概就是获取服务器文件的大小,在本地新建一个相同大小的文件用来申请空间,然后将服务器的文件读下来写到申请的文件中去.若开多线程,将文件分块,计算每个线程下载的开始位置和结束位置.若断点传输,则保存断开后下载的位置,下次将此位置赋给开始下载的位置即可.细节见代码. 下面是效果图: 布局文件activity_main.xml: <?xml version

  • Android实现多线程断点下载

    目录 QDownload 1.如何使用 1.1.导入依赖 1.2.初始化下载组件 1.3.核心控制器DownloadManager 1.4.监听下载进度 1.5.下载相关的操作 1.6.应用市场apk下载的一个场景 总结 QDownload QDownload是基于Android平台实现的下载框架.API简洁易上手,只需5分钟即可实现一个多任务.多线程.断点下载的功能 支持功能如下: 支持多个下载任务同时下载单个任务支持开多个线程下载支持断点下载,在断网.进程被划掉可恢复下载自动校验服务器文件服

随机推荐