-
2022-03-09 17:00:51更多相关内容
-
手把手教你打造Android多任务下载框架
2018-03-14 17:04:05一、什么是多任务下载框架多任务框架就是指,同一时间内,支持多个资源下载。支持下载队列、进度更新、下载取消暂停等。包括:网络下载请求,下载任务执行、下载任务调度、UI进度更新、任务状态变化、文件的存储。二...多任务下载在android app中很多应用场景,比如应用市场下载app,图书下载、音乐下载、电影下载等资源类型下载。
一、什么是多任务下载框架
多任务框架就是指,同一时间内,支持多个资源下载。支持下载队列、进度更新、下载取消暂停等。
包括:网络下载请求,下载任务执行、下载任务调度、UI进度更新、任务状态变化、文件的存储。
二、框架流程
三、框架代码:
下面着重分析下DownloadTask和TaskDispatcher部分代码:
四、任劳任怨的下载器--DownloadTask
DownloadTask实现了Runnable接口,定义了任务状态和网络service。具体参考代码:
/** * <The trouble with the world is that the stupid are sure and the intelligent are full of doubt.> * <p> * HappyBaby * <p> * 作者:Jacky.Ao on 2018/2/23 17:08 * <p> * 邮箱: jiazhi.ao@gmail.com */ public class DownloadTask implements Runnable, ProgressListener { //更新任务进度消息 private static final int UPDATE_PROGRESS_ID = 0x100; //下载网络服务 private APIService.DownloadApiService downloadApiService; //上传网络服务 private APIService.UploadApiService uploadApiService; //下载任务状态 private STATE state; //下载实体类,使用object基类,方便统一获取 private Object downloadObject; //网络服务请求参数列表 private List<RequestParameter> parameterList; //网络下载请求对象 private Call<File> downloadCall; //网络上传请求对象 private Call<BaseEntity> uploadCall; //下载保存文件对象 private File downloadFile = null; //下载任务进度监听器 private OnProgressListener onProgressListener; private DownloadTask mySelf; //是否是下载,区分当前任务是下载还是上传 private boolean isDownload; @Override public void run() { start(); } @Override public void onProgress(long addedBytes, long contentLenght, boolean done) { sendUpdateProgressMessage(addedBytes, contentLenght, false); } public enum STATE { IDLE, PENDING, LOADING, FAILED, FINISHED, UNKNOWN, } private void sendUpdateProgressMessage(long addedBytes, long contentLenght, boolean done) { Message message = handler.obtainMessage(UPDATE_PROGRESS_ID); message.obj = done; message.arg1 = (int) addedBytes; message.arg2 = (int) contentLenght; handler.sendMessage(message); } private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == UPDATE_PROGRESS_ID) { if (onProgressListener != null) { onProgressListener.onProgress(msg.arg1, msg.arg2, (Boolean) msg.obj); } } } }; public DownloadTask(final Object object, List<RequestParameter> list, final boolean download) { downloadObject = object; parameterList = list; isDownload = download; if (isDownload) { downloadApiService = HttpApiService.getDownloadApiService(this); } else { uploadApiService = HttpApiService.getUploadApiService(this); } state = STATE.IDLE; mySelf = this; } public void start() { if (state == STATE.LOADING) { return; } state = STATE.LOADING; if (isDownload) { download(); } else { upload(); } } private void download() { if (parameterList != null && parameterList.size() > 1 && downloadApiService != null) { //change state pending or idle to loading, notify ui to update. sendUpdateProgressMessage(0, 0, false); String downloadFilename = parameterList.get(0).getValue(); String saveFilename = parameterList.get(1).getValue(); downloadCall = downloadApiService.httpDownloadFile(downloadFilename, saveFilename); downloadCall.enqueue(new Callback<File>() { @Override public void onResponse(Call<File> call, Response<File> response) { Log.i(response.toString()); if (response.code() == 200) { mySelf.downloadFile = response.body(); if (mySelf.downloadFile != null && !mySelf.downloadFile.getPath().endsWith(".tmp")) { sendUpdateProgressMessage(100, 100, true); mySelf.state = STATE.FINISHED; TaskDispatcher.getInstance().finished(mySelf); } else { mySelf.state = STATE.FAILED; sendUpdateProgressMessage(0, 0, false); } } } @Override public void onFailure(Call<File> call, Throwable t) { mySelf.state = STATE.FAILED; sendUpdateProgressMessage(0, 0, false); } }); } } private void upload() { if (parameterList != null && parameterList.size() > 1 && uploadApiService != null) { File file = new File(parameterList.get(0).getValue()); String uid = parameterList.get(1).getValue(); RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file); MultipartBody.Part body = MultipartBody.Part.createFormData("image", file.getName(), requestFile); uploadCall = uploadApiService.upLoad(uid, body); // "34" uploadCall.enqueue(new Callback<BaseEntity>() { @Override public void onResponse(Call<BaseEntity> call, Response<BaseEntity> response) { Log.i(response.body().toString()); if (response.code() == 200 && response.body().status.equals("2000")) { sendUpdateProgressMessage(100, 100, true); mySelf.state = STATE.FINISHED; TaskDispatcher.getInstance().finished(mySelf); } else { mySelf.state = STATE.FAILED; sendUpdateProgressMessage(0, 0, false); } } @Override public void onFailure(Call<BaseEntity> call, Throwable t) { Log.e(t.getMessage()); } }); } } public void cancel() { if (downloadCall != null) { downloadCall.cancel(); } handler.removeMessages(UPDATE_PROGRESS_ID); } public void setState(final STATE state) { this.state = state; } public STATE getState() { return state; } public Object getDownloadObject() { return downloadObject; } public void setDownloadObject(Object downloadObject) { this.downloadObject = downloadObject; } public File getDownloadFile() { return downloadFile; } public boolean isDownload() { return isDownload; } public void setOnProgressListener(final OnProgressListener listener) { onProgressListener = listener; } public interface OnProgressListener { void onProgress(long addedBytes, long contentLenght, boolean done); } }
上面省略了一些无关代码,代码写的还是比较简洁,很容易读懂。构造函数中实例了下载服务,并注册了进度更新的监听器,监听器同handler消息更新UI,方便在当前线程更新UI。具体下载参考downloadApiService.httpDownloadFile接口,返回一个Call,下载完成通过OnResponse把保存的文件对象回传回来,保存在downloadFile对象中并修改任务状态为Finished。五、忠于职守的巡查官--任务调度器
TaskDispatcher主要实现下载任务的调度,具体参考代码:
/** * <The trouble with the world is that the stupid are sure and the intelligent are full of doubt.> * <p> * HappyBaby * <p> * 作者:Jacky.Ao on 2018/2/24 15:55 * <p> * 邮箱: jiazhi.ao@gmail.com */ public class TaskDispatcher { //最大下载任务数量 private static final int DOWNLOAD_MAX = 3; //下载任务线程池 private ExecutorService executorService; //正在下载的任务队列 private List<DownloadTask> queueTaskList = Collections.synchronizedList(new ArrayList<>()); //已经完成下载任务队列 private List<DownloadTask> downloadedList = Collections.synchronizedList(new ArrayList<>()); //上传任务队列 private List<DownloadTask> uploadList = Collections.synchronizedList(new ArrayList<>()); //单例对象 private static TaskDispatcher instance; //任务是否中断 private boolean taskAbort; private TaskDispatcher() { } /** *线程安全单例模式 */ public static TaskDispatcher getInstance() { if (instance == null) { synchronized (TaskDispatcher.class) { if (instance == null) { instance = new TaskDispatcher(); } } } return instance; } /** * 初始化线程池 */ private ExecutorService getExecutorService() { if (executorService == null) { executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), threadFactory("happybaby download dispatcher", false)); } return executorService; } /** * 任务入列下载 */ public synchronized boolean enqueue(DownloadTask task) { if (queueTaskList.contains(task) || uploadList.contains(task)) { return false; } if (task != null && task.isDownload()) { if (queueTaskList.size() < DOWNLOAD_MAX) { queueTaskList.add(task); getExecutorService().execute(task); } else { task.setState(DownloadTask.STATE.PENDING); queueTaskList.add(task); } return true; } else { if (uploadList.size() < DOWNLOAD_MAX) { uploadList.add(task); getExecutorService().execute(task); } else { task.setState(DownloadTask.STATE.PENDING); uploadList.add(task); } return true; } } /** * 任务下载完成 */ public synchronized void finished(DownloadTask task) { if (task != null && task.getState() == DownloadTask.STATE.FINISHED) { if (task.isDownload()) { if (queueTaskList.remove(task)) { downloadedList.add(task); promoteSyncTask(); } } else { uploadList.remove(task); promoteSyncUploadTask(); } } } /** * 删除下载任务,是否删除文件 */ public synchronized void deleteTask(DownloadTask task, boolean isDeleteFile) { if (task != null) { if (task.getState() != DownloadTask.STATE.FINISHED) { if (task.isDownload()) { queueTaskList.remove(task); if (task.getState() == DownloadTask.STATE.LOADING) { task.cancel(); } promoteSyncTask(); } else { uploadList.remove(task); if (task.getState() == DownloadTask.STATE.LOADING) { task.cancel(); } promoteSyncUploadTask(); } return; } downloadedList.remove(task); if (isDeleteFile) { task.getDownloadFile().delete(); } } } /** *失败任务重新下载 */ public synchronized void promoteSyncFailedTask() { if (taskAbort && queueTaskList.size() > 0) { for (Iterator<DownloadTask> it = queueTaskList.iterator(); it.hasNext(); ) { DownloadTask task = it.next(); if (task.getState() == DownloadTask.STATE.FAILED) { getExecutorService().execute(task); } } } if (taskAbort && uploadList.size() > 0) { for (Iterator<DownloadTask> it = uploadList.iterator(); it.hasNext(); ) { DownloadTask task = it.next(); if (task.getState() == DownloadTask.STATE.FAILED) { getExecutorService().execute(task); } } } } /** * 调度上传任务 */ private synchronized void promoteSyncUploadTask() { for (Iterator<DownloadTask> it = uploadList.iterator(); it.hasNext();) { DownloadTask task = it.next(); if (task.getState() == DownloadTask.STATE.PENDING) { getExecutorService().execute(task); return; } } } /** * 调度pending状态的任务,开始下载 */ private synchronized void promoteSyncTask() { for (Iterator<DownloadTask> it = queueTaskList.iterator(); it.hasNext();) { DownloadTask task = it.next(); if (task.getState() == DownloadTask.STATE.PENDING) { getExecutorService().execute(task); return; } } } public List<DownloadTask> getQueueTaskList() { return queueTaskList; } public List<DownloadTask> getDownloadedList() { return downloadedList; } public List<DownloadTask> getUploadList() { return uploadList; } /** * 取消所有任务 */ public synchronized void cancelAll() { for (DownloadTask task : queueTaskList) { if (task.getState() == DownloadTask.STATE.LOADING) { task.cancel(); } } for (DownloadTask task : uploadList) { if (task.getState() == DownloadTask.STATE.LOADING) { task.cancel(); } } } public void setTaskAbort(boolean taskAbort) { this.taskAbort = taskAbort; } private ThreadFactory threadFactory(final String name, final boolean daemon) { return new ThreadFactory() { @Override public Thread newThread(@NonNull Runnable r) { Thread thread = new Thread(r, name); thread.setDaemon(daemon); return thread; } }; } }
上面是TaskDispatcher部分代码,重点看下enqueue函数,该函数是个入列操作,根据传入的task, 加入下载队列,如果当前下载队列超出最大任务数量,将任务状态修改为pending状态,等待任务 完成即finished接口,将当前下载完成的task添加到下载完成队列,并调用promoteSyncTask接口, 检查是否有pending的任务,如果有则开始任务。
六、测试示例:
RequestParameter parameter = new RequestParameter("name", fileName); RequestParameter parameter1 = new RequestParameter("savename", filePath + fileName); List<RequestParameter> parameterList = new ArrayList<>(); parameterList.add(parameter); parameterList.add(parameter1); DownloadTask downloadTask = new DownloadTask(musicEntity, parameterList,true); downloadTask.setDownloadObject(musicEntity); downloadTask.setOnProgressListener(new DownloadTask.OnProgressListener() { @Override public void onProgress(long addedBytes, long contentLenght, boolean done) { if (contentLenght > 0) { holder.progress.setProgress((int) (1.0f * addedBytes / contentLenght * 100)); } if (done) { Log.i(entity.getName() + " finished..."); notifyItemRemoved(position); } } }); TaskDispatcher.getInstance().enqueue(downloadTask);
七、效果图
原创不易,如果您觉得好,可以分享此公众号给你更多的人。
-
更好的Android多线程下载框架
2017-03-30 15:28:52*/概述为什么是更好的Android多线程下载框架呢,原因你懂的,广告法嘛!本篇我们我们就来聊聊多线程下载框架,先聊聊我们框架的特点: 多线程 多任务 断点续传 支持大文件 可以自定义下载数据库/** * 作者:Pich * 原文链接:http://me.woblog.cn/ * QQ群:129961195 * Github:https://github.com/lifengsofts */
概述
为什么是更好的Android多线程下载框架呢,原因你懂的,广告法嘛!
本篇我们我们就来聊聊多线程下载框架,先聊聊我们框架的特点:
- 多线程
- 多任务
- 断点续传
- 支持大文件
- 可以自定义下载数据库
- 高度可配置,像超时时间这类
- 业务数据和下载数据分离
下面我们在说下该框架能实现那些的应用场景:
- 该框架可以很方便的下载单个文件,并且显示各种状态,包括开始下载,下载中,下载失败,删除等状态。
- 也可以实现常见的需要下载功能应用,比如:某某手机助手,在该应用内可以说是下载是核心功能,所以对框架的稳定性,代码可靠性,框架扩展性依赖很大,所以该框架真是从这种出发点而生的。通常这类应用的表示形式分三个页面需要用到下载功能,一个列表用来显示来自业务数据的列表,在该列表右边可以点击单个条目,或者多选实现下载,点击每个条目进入详情,同时还有个一个下载管理,包括大概两个界面,正在下载,下载完成的,在这几个界面都需要一个核心的功能就是都可以暂停,恢复,删除并且能显示下载进度。在列表一个最重要的问题就是界面刷新,如果每次更新都刷新整个列表,那么这将是异常灾难,而我们这个框架正好解决了该问题,采用了回调单个条目并更新该条目的进度和状态。
该项目状态
该项目的雏形始于14年的公司项目需要用到多线程下载,但当时实现的单线程多任务断点续传,后面不断完善,在这之间遇到过很多坑,也对一个下载框架有了更深的认识,所以在16年又重写了该框架。
项目的Github地址:https://github.com/lifengsofts/AndroidDownloader
项目的官网地址:http://i.woblog.cn/AndroidDownloader
项目还处于发展状态,但已经趋于稳定,并且有一定的编码规范,同时采用了多个开源项目的质量控制方案以保证每次代码提交的可靠性。
下面上几张框架Demo的截图,这样用户在心中有一个自己的概念,但是推荐各位还是讲Demo下载到本地亲自,运行一下。
截图
第一个界面是单独下载一个文件。
第二个界面是应用中最常用的一个界面,该界面来自业务数据。
第三个页面是离线管理中的下载中的界面。
第四个页面是离线管理中的下载完成的界面。
可以看到他们在每个界面都能暂停下载,继续下载,以及删除,并且都能拿到进度,状态等信息。
下面就来看看这么强大的下载框架那该如何来使用呢?
添加权限
我相信这一步任何一个项目都已经添加了,但是还是不得不提一下。
该框架需要网络访问权限,如果你是讲文件下载到存储卡,那相应的需要添加存储卡访问权限。
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
配置Service
因为该框架采用才service中下载一个文件的。这样做的目的是下载任务一般都需要在后头下载,如果在Activity中来做这类任务,我想任何一个新手都知道这样不行。
<service android:name="cn.woblog.android.downloader.DownloadService"> <intent-filter> <action android:name="cn.woblog.android.downloader.DOWNLOAD_SERVICE" /> </intent-filter> </service>
添加依赖
我们提供了多种集成方式,比如:gradle,maven,jar。选择适合你自己的就行了。
Gradle
在module目录下面的build.gradle文件中添加如下内容:
compile 'cn.woblog.android:downloader:1.0.1'
Maven
或者你使用的Maven依赖管理工具。那道理其实是一样的,在pom文件中添加:
<dependency> <groupId>cn.woblog.android</groupId> <artifactId>downloader</artifactId> <version>1.0.0</version> </dependency>
或者你也可以参考该链接使用Snapshots版本。
混淆配置
如果你的项目使用了混淆规则,那么一定要加上。
-keep public class * implements cn.woblog.android.downloader.db.DownloadDBController -keep class cn.woblog.android.downloader.domain.** { *; }
创建下载管理器
现在万事俱备只欠东风了,接下来只需要创建一个下载管理器,该框架所有的操作都是通过该来实现的:
downloadManager = DownloadService.getDownloadManager(context.getApplicationContext());
或者你可以使用更详细的来配置该框架:
Config config = new Config(); //set database path. // config.setDatabaseName("/sdcard/a/d.db"); // config.setDownloadDBController(dbController); //set download quantity at the same time. config.setDownloadThread(3); //set each download info thread number config.setEachDownloadThread(2); // set connect timeout,unit millisecond config.setConnectTimeout(10000); // set read data timeout,unit millisecond config.setReadTimeout(10000); downloadManager = DownloadService.getDownloadManager(this.getApplicationContext(), config);
下载一个文件
//create download info set download uri and save path. final DownloadInfo downloadInfo = new DownloadInfo.Builder().setUrl("http://example.com/a.apk") .setPath("/sdcard/a.apk") .build(); //set download callback. downloadInfo.setDownloadListener(new DownloadListener() { @Override public void onStart() { tv_download_info.setText("Prepare downloading"); } @Override public void onWaited() { tv_download_info.setText("Waiting"); bt_download_button.setText("Pause"); } @Override public void onPaused() { bt_download_button.setText("Continue"); tv_download_info.setText("Paused"); } @Override public void onDownloading(long progress, long size) { tv_download_info .setText(FileUtil.formatFileSize(progress) + "/" + FileUtil .formatFileSize(size)); bt_download_button.setText("Pause"); } @Override public void onRemoved() { bt_download_button.setText("Download"); tv_download_info.setText(""); downloadInfo = null; } @Override public void onDownloadSuccess() { bt_download_button.setText("Delete"); tv_download_info.setText("Download success"); } @Override public void onDownloadFailed(DownloadException e) { e.printStackTrace(); tv_download_info.setText("Download fail:" + e.getMessage()); } }); //submit download info to download manager. downloadManager.download(downloadInfo);
下载一个文件时直接创建一个DownloadInfo,然后设置下载链接和下载路径。再添加一个监听。就可以提交到下载框架了。
通过下载监听器我们可以获取到很多状态。开始下载,等待中,暂停完成,下载中,删除成功,下载成功,下载失败等状态。
在列表控件使用
我们这里演示如何在RecyclerView这类列表控件使用。当然如果你用的是ListView那道理是一样的。
class ViewHolder extends RecyclerView.ViewHolder { private final ImageView iv_icon; private final TextView tv_size; private final TextView tv_status; private final ProgressBar pb; private final TextView tv_name; private final Button bt_action; private DownloadInfo downloadInfo; public ViewHolder(View view) { super(view); iv_icon = (ImageView) view.findViewById(R.id.iv_icon); tv_size = (TextView) view.findViewById(R.id.tv_size); tv_status = (TextView) view.findViewById(R.id.tv_status); pb = (ProgressBar) view.findViewById(R.id.pb); tv_name = (TextView) view.findViewById(R.id.tv_name); bt_action = (Button) view.findViewById(R.id.bt_action); } @SuppressWarnings("unchecked") public void bindData(final MyDownloadInfo data, int position, final Context context) { Glide.with(context).load(data.getIcon()).into(iv_icon); tv_name.setText(data.getName()); // Get download task status downloadInfo = downloadManager.getDownloadById(data.getUrl().hashCode()); // Set a download listener if (downloadInfo != null) { downloadInfo .setDownloadListener(new MyDownloadListener(new SoftReference(ViewHolder.this)) { // Call interval about one second @Override public void onRefresh() { if (getUserTag() != null && getUserTag().get() != null) { ViewHolder viewHolder = (ViewHolder) getUserTag().get(); viewHolder.refresh(); } } }); } refresh(); // Download button bt_action.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (downloadInfo != null) { switch (downloadInfo.getStatus()) { case DownloadInfo.STATUS_NONE: case DownloadInfo.STATUS_PAUSED: case DownloadInfo.STATUS_ERROR: //resume downloadInfo downloadManager.resume(downloadInfo); break; case DownloadInfo.STATUS_DOWNLOADING: case DownloadInfo.STATUS_PREPARE_DOWNLOAD: case STATUS_WAIT: //pause downloadInfo downloadManager.pause(downloadInfo); break; case DownloadInfo.STATUS_COMPLETED: downloadManager.remove(downloadInfo); break; } } else { // Create new download task File d = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), "d"); if (!d.exists()) { d.mkdirs(); } String path = d.getAbsolutePath().concat("/").concat(data.getName()); downloadInfo = new Builder().setUrl(data.getUrl()) .setPath(path) .build(); downloadInfo .setDownloadListener(new MyDownloadListener(new SoftReference(ViewHolder.this)) { @Override public void onRefresh() { if (getUserTag() != null && getUserTag().get() != null) { ViewHolder viewHolder = (ViewHolder) getUserTag().get(); viewHolder.refresh(); } } }); downloadManager.download(downloadInfo); } } }); } private void refresh() { if (downloadInfo == null) { tv_size.setText(""); pb.setProgress(0); bt_action.setText("Download"); tv_status.setText("not downloadInfo"); } else { switch (downloadInfo.getStatus()) { case DownloadInfo.STATUS_NONE: bt_action.setText("Download"); tv_status.setText("not downloadInfo"); break; case DownloadInfo.STATUS_PAUSED: case DownloadInfo.STATUS_ERROR: bt_action.setText("Continue"); tv_status.setText("paused"); try { pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize())); } catch (Exception e) { e.printStackTrace(); } tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil .formatFileSize(downloadInfo.getSize())); break; case DownloadInfo.STATUS_DOWNLOADING: case DownloadInfo.STATUS_PREPARE_DOWNLOAD: bt_action.setText("Pause"); try { pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize())); } catch (Exception e) { e.printStackTrace(); } tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil .formatFileSize(downloadInfo.getSize())); tv_status.setText("downloading"); break; case STATUS_COMPLETED: bt_action.setText("Delete"); try { pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize())); } catch (Exception e) { e.printStackTrace(); } tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil .formatFileSize(downloadInfo.getSize())); tv_status.setText("success"); break; case STATUS_REMOVED: tv_size.setText(""); pb.setProgress(0); bt_action.setText("Download"); tv_status.setText("not downloadInfo"); case STATUS_WAIT: tv_size.setText(""); pb.setProgress(0); bt_action.setText("Pause"); tv_status.setText("Waiting"); break; } } } }
关键代码就是bindData方法中先通过业务的id,我们这里使用的url来获取该业务数据是否有对应的下载任务。如果有,则从新绑定监听器,也就是这段代码
downloadInfo .setDownloadListener(new MyDownloadListener(new SoftReference(ViewHolder.this)) { // Call interval about one second @Override public void onRefresh() { if (getUserTag() != null && getUserTag().get() != null) { ViewHolder viewHolder = (ViewHolder) getUserTag().get(); viewHolder.refresh(); } } });
其中要注意到的是缓存每个条目我们使用了SoftReference,这样做的目的内容在吃紧的情况下而已及时的是否这些条目。
接下来又一个重要的点是,设置按钮的点击事件,通常在这样的列表中有一个或多个按钮控制下载状态。
if (downloadInfo != null) { switch (downloadInfo.getStatus()) { case DownloadInfo.STATUS_NONE: case DownloadInfo.STATUS_PAUSED: case DownloadInfo.STATUS_ERROR: //resume downloadInfo downloadManager.resume(downloadInfo); break; case DownloadInfo.STATUS_DOWNLOADING: case DownloadInfo.STATUS_PREPARE_DOWNLOAD: case STATUS_WAIT: //pause downloadInfo downloadManager.pause(downloadInfo); break; case DownloadInfo.STATUS_COMPLETED: downloadManager.remove(downloadInfo); break; } } else { // Create new download task File d = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), "d"); if (!d.exists()) { d.mkdirs(); } String path = d.getAbsolutePath().concat("/").concat(data.getName()); downloadInfo = new Builder().setUrl(data.getUrl()) .setPath(path) .build(); downloadInfo .setDownloadListener(new MyDownloadListener(new SoftReference(ViewHolder.this)) { @Override public void onRefresh() { if (getUserTag() != null && getUserTag().get() != null) { ViewHolder viewHolder = (ViewHolder) getUserTag().get(); viewHolder.refresh(); } } }); downloadManager.download(downloadInfo); }
关键点就是如果没有下载任务就创建一个下载任务,如果已有下载任务就根据任务现在的状态执行相应的操作,比如当前是没有下载,点击就是创建一个下载任务。
接下还有一个重点就是,我们在回调监听中调用了refresh方法,在该方法中根据状态显示进度和相应的操作按钮。这样做的好处上面已经提到了。
private void refresh() { if (downloadInfo == null) { tv_size.setText(""); pb.setProgress(0); bt_action.setText("Download"); tv_status.setText("not downloadInfo"); } else { switch (downloadInfo.getStatus()) { case DownloadInfo.STATUS_NONE: bt_action.setText("Download"); tv_status.setText("not downloadInfo"); break; case DownloadInfo.STATUS_PAUSED: case DownloadInfo.STATUS_ERROR: bt_action.setText("Continue"); tv_status.setText("paused"); try { pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize())); } catch (Exception e) { e.printStackTrace(); } tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil .formatFileSize(downloadInfo.getSize())); break; case DownloadInfo.STATUS_DOWNLOADING: case DownloadInfo.STATUS_PREPARE_DOWNLOAD: bt_action.setText("Pause"); try { pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize())); } catch (Exception e) { e.printStackTrace(); } tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil .formatFileSize(downloadInfo.getSize())); tv_status.setText("downloading"); break; case STATUS_COMPLETED: bt_action.setText("Delete"); try { pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize())); } catch (Exception e) { e.printStackTrace(); } tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil .formatFileSize(downloadInfo.getSize())); tv_status.setText("success"); break; case STATUS_REMOVED: tv_size.setText(""); pb.setProgress(0); bt_action.setText("Download"); tv_status.setText("not downloadInfo"); case STATUS_WAIT: tv_size.setText(""); pb.setProgress(0); bt_action.setText("Pause"); tv_status.setText("Waiting"); break; } } }
到这里改下框架的核心使用方法就介绍完了。
支持
如有任何问题可以在加我们的QQ群或者在Github上提Issue,另外请提Issue或者的PR的一定要看下项目的贡献代码的方法以及一要求,因为如果要保证一个开源项目的质量就必须在各方面都规范化。
-
android 牛逼的下载框架
2017-11-24 11:31:25最新在开发的过程碰到一个问题,就是apk的下载成功率一直不高,用的下载器是android系统的下载器DownloadManager,保持在50%多成功率,因为我们一些apk包很大,很多都是300,400M的游戏包,下载成功率低从而也会导致...我们公司是做视频广告sdk的,就是播放完一个视频后,用户点击下载apk.最新在开发的过程碰到一个问题,就是apk的下载成功率一直不高,用的下载器是android系统的下载器DownloadManager,保持在50%多成功率,因为我们一些apk包很大,很多都是300,400M的游戏包,下载成功率低从而也会导致安装率低,自然而然也会导致我们的利润低。所以优化下载成功率成了迫在眉睫的任务。
经过一个星期的设计,一个星期的编码,最终实现了自己搭建的下载框架,当然借鉴了一些开源框架的设计思路,根据我们自己的逻辑业务实现了自己的下载器。这两天进行全面升级,结果超出我们的预期,下载成功率从55%提升到77%,一下提高了20个百分点。无图无真相
29版本就是带了最新下载方式的版本,28版本就是用的DownloadManager的下载方式。29版本,1582下载,1219个下载成功。28版本889下载,532个下载成功。
当然安装率也提高了不少。我们对这个结果还是相当满意的。
好的东西要分享(当然是有偿的
),有需要联系我240752003@qq.com
-
Android 多线程多任务下载框架的实现(一)
2015-01-04 19:18:32Android多线程多任务下载框架 -
如何下载Spring框架
2018-11-09 19:53:08首先进入Spring官网: https://spring.io/ 点击PROJECTS 找到SPRING FRAMEWORK 并点击 ...然后点击一个连接进行下载即可 ( 中智软件科技学校 ) -
Android-- 基于http的文件下载开源框架file-downloader
2016-11-04 18:15:17FileDownloader(https://github.com/wlfcolin/file-downloader)是开源的一个安卓Http文件下载框架,是根据自己的经验总结的一套非常轻量级的安卓通用Http文件下载管理器。我的目标是让文件下载越简单越好,尽... -
Spring框架下载方法
2018-03-03 22:07:36spring-framework-4.3.2.RELEASE-dist.zip 下载解压即可获得jar包api等等文件。 ps:直接获取方法。URL为最Spring最新4.3.2版本 地址,获取其他版本只需修改下面链接的4.3.2的版本号信息成想要的版本即可。 ... -
ssm框架demo
2017-11-07 11:35:54一个ssm框架小demo,框架的包目录结构,包含登录,增删查改,分页,jsp文件放在WEN-INF下等的简单实现,有数据库文件,源代码,结果视频。 -
android下载框架FileDownloader的简单使用
2018-05-21 09:57:19首先,依赖: ...很不错的一个下载框架,性能很好; 更多的使用可以去github上去看文档,有中英文的 github地址: https://github.com/lingochamp/FileDownloader -
Intelij IDEA无法下载框架
2016-05-23 00:52:40IDEA2016 创建项目的时候添加struts2和hibernate框架,需要下载相应库,但是搭了梯子也是下载失败,如何将所有的库都下载为离线使用?或者国内的镜像下载站点怎么配置 -
SpringMVC框架实现文件的上传和下载
2017-04-05 13:50:56SpringMVC框架实现文件的上传和下载项目案例源码 -
SSM框架完整版下载
2017-07-28 14:52:47完整搭建完的ssm框架项目,使用之前可以先测试下 -
下载spring框架
2020-10-10 13:30:52下载spring框架的步骤 进入官网点击projects选择springframework 点击小猫图标进入GitHub 往下滑动找到Access to Binaries一栏,点击蓝色连接 同样的滑动到页面底部找到图中链接并点击 点击图中图标 按图中步骤... -
SSM框架项目实例
2018-03-09 17:13:42一个SSM框架整合的小系统,叫做在线考试系统,简单来说就是实现教师(管理员)发布考试和学生(普通用户)参加考试的功能,当然其中还有一些稍显复杂的东西。 项目经反馈存在一些问题,如有需要,可加QQ群了解,有... -
c# DEV Winform 框架
2017-09-08 11:46:00c# DEV Winform 框架c# DEV Winform 框架c# DEV Winform 框架c# DEV Winform 框架c# DEV Winform 框架c# DEV Winform 框架c# DEV Winform 框架 -
SSM框架纯净版
2017-12-17 15:05:51SSM框架纯净版,各位需要参考搭建Spring+SpringMVC+Mybatis的同学可以参考这个Demo -
Spring+SpringMVC+Mybatis框架整合例子(SSM) 下载
2014-07-21 12:06:07本资源对应博文:http://blog.csdn.net/zhshulin/article/details/37956105,可以通过博文进行学习,不建议下载完整源码,博文有详细教程,以及代码。 -
android studio 引入从github下载的开源框架
2016-05-04 09:55:43引入框架有3种方式: 1.直接配置远程访问的步骤:在开源框架的首页,copy首页上的 gradle配置信息到本地的gradle文件里,然后保存即可!(推荐的方式,因为很简单!) 比如引入sqlite的开源框架【基于orm的... -
js问题!如何运用react框架生成下载pdf文件。。。。急!
2017-09-05 09:30:51如何运用react框架生成下载pdf文件。。。。急!现在项目中需要下载这种文件格式,可是之前没有做过,而且是react中搞 -
最新的Spring官网如何下载Spring框架
2019-11-27 15:44:22最新的Spring官网如何下载Spring框架。特此写一篇简单的教程,供大家分享。 1. 首先打开Spring官网 https://spring.io/ 2. 点击页头 PROGECTS 选项 3. 打开 SPRING FRAMEWORK 一项 4. 点击页面... -
华为gms框架app下载-华为gms框架2020版下载最新版-乐游网安卓下载
2021-01-13 10:24:17《华为gms框架2020版》是一款非常好用的手机游戏辅助插件,这款软件为用户提供了最好的辅助工具和辅助模式,操作简单,功能强大,给你最好的游戏体验和游戏魅力,感兴趣就来试试吧!软件简介华为谷歌服务框架安装器... -
SSH框架搭建源码
2015-10-19 21:46:09里面是SSH框架搭建登录的demo,清楚的描述了SSH框架搭建过程的各种配置,另附博客地址:http://blog.csdn.net/u010539352 -
基于SSM框架的完整项目 Spring+SpringMVC+Mybatis+mysql+bootstrap下载
2017-02-15 09:13:42基于SSM框架的完整项目 Spring+SpringMVC+Mybatis+mysql 前段bootstrap 简单的一个后台管理系统 -
Vue Element UI 完整版前端框架
2019-07-31 11:13:49Element UI 完整版 源码 可用 vue+Element-UI 前端框架 -
composer下载laravel框架
2020-01-08 11:31:05下载并且运行 Composer-Setup.exe,它将安装最新版本的 Composer ,并设置好系统的环境变量,因此你可以在任何目录下直接使用 composer 命令。 一直下一步,结束安装! 设置环境变量 composer安装完成! 在cmd... -
google服务框架下载官方版
2021-05-28 01:38:46google服务框架最新版(google services framework)是一款对于手机来说非常重要的应用程序,有不少朋友会觉得它没用把它误删,但现在有很多的软件和游戏需要谷歌服务的支持,所以有需要的朋友可以到当易网下载使用!... -
Spring框架下载教程
2020-02-10 11:08:406、点击Artifacts 7、然后找到菜单下的 libs-release-local选项 8、找到需要的版本,鼠标右键选择Natlve Browser 9、点击第一个进行下载 写在最后:还有一个简单的方法,直接在浏览器输入:... 欢迎查看我的github博客... -
google play服务框架下载安装
2021-06-04 14:37:56谷歌play服务框架2021最新版本是一款为谷歌软件及游戏提供呼出服务的软件,如果手机中缺少这个软件,可能会导致一些软件或游戏无法正常运行,出现秒退或打不开等情况,遇到这种情况可以下载这款软件安装google play... -
下载vue模板框架并使用
2019-03-21 19:23:18首先你需要安装上node 和git 在cmd命令中测试是否安装成功 ...下载模板 cd 到你想存放模板的路径 在cmd中键入命令 git clone github 地址 第二步:下载成功后 cd到package.json所在的路径 命令:...