本文介绍项目中引入okhttp-okgo开源框架里的OkDownload部分,实现了RecyclerView列表的下载功能.

  • 引入OKDownload

需求不仅是要支持断点续传,而且还要支持队列下载和任务恢复,这样单单导入okgo的包是不够的,还需要额外导入下面这个okserver的包.然后在APPlication里初始化OkGo

//初始化OkGo
OkGo.getInstance().init(this);
//设置下载路径
OkDownload.getInstance().setFolder(Constants.DOWNLOAD_APK_PATH);
  • 单个任务的下载

上篇博文已经介绍了三个任务下载的基本使用,现在这里直接粘贴代码

   GetRequest<File> request = OkGo.get(itemsBean.getUrl());
  DownloadTask task = OkDownload.request(itemsBean.getUrl(), request) .save().register(new ListDownloadListener(itemsBean.getUrl(), mHolder));
  task.start();

根据下载url得到一个GetRequest对象,然后调用OkDownload的静态方法,把一个保证下载任务唯一性的key(String类型)和这个GetRequest对象传递进去就可以创建一个下载任务了.

  • 列表下载

okdownload库中维护了一个由线程池控制的下载队列,可以通过下面这个方式来设定允许同时下载的任务数目(默认是3个)

OkDownload.getInstance().getThreadPool().setCorePoolSize(2);

然后就是状态控制了,我们都知道一个文件下载伴随有这几种状态

 public static final int NONE = 0;         //无状态
public static final int WAITING = 1; //等待
public static final int LOADING = 2; //下载中
public static final int PAUSE = 3; //暂停
public static final int ERROR = 4; //错误
public static final int FINISH = 5; //完成

可以根据它提供的一个Progress的status,表示当前状态的字段,来更新UI,给用户反馈.

于是,我们可以获取到当前item的任务之后,让下载的点击事件这样响应:

  Progress progress = task.progress;
if (progress.status == Progress.LOADING || progress.status == Progress.PAUSE) {
flikerProgressBar.toggle();
}
switch (progress.status) {
case Progress.PAUSE:
case Progress.NONE:
case Progress.ERROR:
task.start();
break;
case Progress.LOADING:
task.pause();
break;
case Progress.FINISH:
if (ApkUtils.isAvailable(context, new File(progress.filePath))) {
ApkUtils.startAPP(context, ApkUtils.getPackageName(context, progress.filePath));
} else {
ApkUtils.install(context, new File(progress.filePath));
}
break;
}
refresh(progress);
  • 判断和获取指定tag的下载任务
  DownloadTask task;

if (!OkDownload.getInstance().hasTask(itemsBean.getUrl())) {

GetRequest<File> request = OkGo.get(itemsBean.getUrl());

task = OkDownload.request(itemsBean.getUrl(), request)

.save().register(new ListDownloadListener(itemsBean.getUrl(), mHolder));

} else {

task = OkDownload.getInstance().getTask(itemsBean.getUrl());

}

如果下载队列里不包含当前tag的任务,通过OkDownload.getInstance().hasTask(String tag)判断,那么就对它新建一个下载任务,因为我们要对他进行下载操作.

如果下载队列里已经包含当前任务,那么我们通过OkDownload.getInstance().getTask(String tag)方法来获取这个task;

然后对这个task进行上面的步骤.

如此,一个下载队列的功能就完成了.

下面贴出完整代码

public class BaseAppsDownloadAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private List<AppBean.ResultBean.ItemsBean> mList;
private LayoutInflater inflater;
private Context context;
private List<DownloadTask> values;
private boolean comeFromRank; public BaseAppsDownloadAdapter(Context context, List<AppBean.ResultBean.ItemsBean> mList, boolean comeFromRank) {
this.comeFromRank = comeFromRank;
this.mList = mList;
this.context = context;
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
} public void updateData() {
//这里是将数据库的数据恢复
values = OkDownload.restore(DownloadManager.getInstance().getAll());
notifyDataSetChanged();
} @Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.home_innovate_item, parent, false);
return new ViewHolder(view);
} @Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (mList == null || mList.size() == 0) {
return;
}
AppBean.ResultBean.ItemsBean itemsBean = mList.get(position);
if (itemsBean == null) {
return;
}
ViewHolder mHolder = (ViewHolder) holder;
// 判断布局中是否需要显示排行
if (comeFromRank) {
mHolder.tv_rank.setVisibility(View.VISIBLE);
mHolder.tv_rank.setText("" + (position + 1));
} else {
mHolder.tv_rank.setVisibility(View.GONE);
}
mHolder.bind(mHolder, itemsBean);
mHolder.flikerProgressBar.setVisibility(View.INVISIBLE);
mHolder.ib_download.setVisibility(View.VISIBLE);
if (TextUtils.isEmpty(itemsBean.getUrl())) {
return;
}
if (OkDownload.getInstance().hasTask(itemsBean.getUrl())) {
DownloadTask task = OkDownload.getInstance().getTask(itemsBean.getUrl()).register(new ListDownloadListener(itemsBean.getUrl(), mHolder));
mHolder.setTask(task);
mHolder.setTag(itemsBean.getUrl());
mHolder.flikerProgressBar.setVisibility(View.VISIBLE);
mHolder.ib_download.setVisibility(View.INVISIBLE);
mHolder.refresh(task.progress);
} else {
mHolder.flikerProgressBar.setVisibility(View.INVISIBLE);
mHolder.ib_download.setVisibility(View.VISIBLE);
mHolder.setTask(null);
mHolder.setTag(null);
}
} public void notifySetDataListChanged(List<AppBean.ResultBean.ItemsBean> appsData) {
mList = appsData;
notifyDataSetChanged();
} public void unRegister() {
Map<String, DownloadTask> taskMap = OkDownload.getInstance().getTaskMap();
for (DownloadTask task : taskMap.values()) {
task.unRegister(task.progress.tag);
}
} @Override
public int getItemCount() {
return mList == null ? 0 : mList.size();
} public class ViewHolder extends RecyclerView.ViewHolder {
private final ImageView iv_icon;
private final TextView tv_name;
private final TextView tv_size;
private final TextView tv_rank;
private final TextView tv_des;
private final TextView ib_download;
private FlickerProgressBar flikerProgressBar;
private DownloadTask task;
private String tag; public ViewHolder(View itemView) {
super(itemView);
iv_icon = ((ImageView) itemView.findViewById(R.id.app_icon));
tv_name = ((TextView) itemView.findViewById(R.id.tv_app_name));
tv_size = ((TextView) itemView.findViewById(R.id.tv_app_size));
tv_rank = ((TextView) itemView.findViewById(R.id.tv_app_rank));
tv_des = ((TextView) itemView.findViewById(R.id.tv_app_des));
ib_download = ((TextView) itemView.findViewById(R.id.ib_download));
flikerProgressBar = ((FlickerProgressBar) itemView.findViewById(R.id.btn_progress));
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
context.startActivity(new Intent(context, AppDetailActivity.class)
.putExtra("appid", mList.get(getAdapterPosition()).getId())
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
});
} public void setTask(DownloadTask task) {
this.task = task;
} public void bind(final ViewHolder mHolder, final AppBean.ResultBean.ItemsBean itemsBean) {
mHolder.tv_name.setText(itemsBean.getName());
if (TextUtils.isEmpty(itemsBean.getImageUrl())) {
mHolder.iv_icon.setImageResource(R.mipmap.icon_default);
} else {
Glide.with(context).load(itemsBean.getImageUrl()).placeholder(R.mipmap.icon_default).into(mHolder.iv_icon);
}
mHolder.tv_des.setText("" + itemsBean.getAbstractString());
mHolder.tv_size.setText(FileUtil.bytes2kb(itemsBean.getSize()));
mHolder.flikerProgressBar.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (TextUtils.isEmpty(itemsBean.getUrl())) {
Toast.makeText(v.getContext(), "下载链接不存在", Toast.LENGTH_SHORT).show();
return;
}
DownloadTask task;
if (!OkDownload.getInstance().hasTask(itemsBean.getUrl())) {
GetRequest<File> request = OkGo.get(itemsBean.getUrl());
task = OkDownload.request(itemsBean.getUrl(), request)
.save().register(new ListDownloadListener(itemsBean.getUrl(), mHolder));
} else {
task = OkDownload.getInstance().getTask(itemsBean.getUrl());
}
mHolder.setTask(task);
mHolder.setTag(itemsBean.getUrl());
mHolder.start();
}
});
} public void refresh(Progress progress) {
flikerProgressBar.setProgress((int) (progress.fraction * 100));
switch (progress.status) {
case Progress.NONE:
flikerProgressBar.setText("下载");
break;
case Progress.PAUSE:
flikerProgressBar.setText("继续");
break;
case Progress.ERROR:
flikerProgressBar.setText("出错");
break;
case Progress.WAITING:
flikerProgressBar.setText("等待");
break;
case Progress.FINISH:
if (ApkUtils.isAppInstalled(context, ApkUtils.getPackageName(context, progress.filePath))) {
flikerProgressBar.setText("打开");
} else {
flikerProgressBar.setText("安装");
}
break;
case Progress.LOADING:
flikerProgressBar.setText((int) (progress.fraction * 100 + 0.5f) + "%");
break;
}
} public void start() {
Progress progress = task.progress;
if (progress.status == Progress.LOADING || progress.status == Progress.PAUSE) {
flikerProgressBar.toggle();
}
switch (progress.status) {
case Progress.PAUSE:
case Progress.NONE:
case Progress.ERROR:
task.start();
break;
case Progress.LOADING:
task.pause();
break;
case Progress.FINISH:
if (ApkUtils.isAvailable(context, new File(progress.filePath))) {
ApkUtils.startAPP(context, ApkUtils.getPackageName(context, progress.filePath));
} else {
ApkUtils.install(context, new File(progress.filePath));
}
break;
}
refresh(progress);
} public void setTag(String tag) {
this.tag = tag;
} public String getTag() {
return tag;
}
} private class ListDownloadListener extends DownloadListener { private ViewHolder holder; ListDownloadListener(Object tag, ViewHolder holder) {
super(tag);
this.holder = holder;
} @Override
public void onStart(Progress progress) {
} @Override
public void onProgress(Progress progress) {
if (tag == holder.getTag()) {
holder.refresh(progress);
}
} @Override
public void onError(Progress progress) {
Throwable throwable = progress.exception;
if (throwable != null) throwable.printStackTrace();
} @Override
public void onFinish(File file, Progress progress) {
Toast.makeText(context, "下载完成:" + progress.filePath, Toast.LENGTH_SHORT).show();
} @Override
public void onRemove(Progress progress) {
}
}

上面的代码,之所以可以有效的避免RecyclerView滚动item布局混乱的问题.是因为我们对每一个item的各项数据都做了赋值操作.

更新下载进度的ListDownloadListener 只与该任务的tag有关,而tag又是itemBean.getUrl() , 所以只要下载链接唯一,那么这个监听是不会响应到别的iteam上的.

 

 

OkDownload项目实战的更多相关文章

  1. Asp.Net Core 项目实战之权限管理系统(4) 依赖注入、仓储、服务的多项目分层实现

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

  2. 给缺少Python项目实战经验的人

    我们在学习过程中最容易犯的一个错误就是:看的多动手的少,特别是对于一些项目的开发学习就更少了! 没有一个完整的项目开发过程,是不会对整个开发流程以及理论知识有牢固的认知的,对于怎样将所学的理论知识应用 ...

  3. 【腾讯Bugly干货分享】React Native项目实战总结

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/577e16a7640ad7b4682c64a7 “8小时内拼工作,8小时外拼成长 ...

  4. Asp.Net Core 项目实战之权限管理系统(0) 无中生有

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

  5. Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

  6. Asp.Net Core 项目实战之权限管理系统(2) 功能及实体设计

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

  7. Asp.Net Core 项目实战之权限管理系统(3) 通过EntityFramework Core使用PostgreSQL

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

  8. Asp.Net Core 项目实战之权限管理系统(5) 用户登录

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

  9. Asp.Net Core 项目实战之权限管理系统(6) 功能管理

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

随机推荐

  1. PAT L3-005. 垃圾箱分布

    最短路. 枚举垃圾箱放哪里,然后算最短路. #include<map> #include<set> #include<ctime> #include<cmat ...

  2. Linux前后台进程切换

    (1).Linux前台进程与后台进程的区别 前台进程:是在终端中运行的命令,那么该终端就为进程的控制终端,一旦这个终端关闭,这个进程也随之消失. 后台进程:也叫守护进程(Daemon),是运行在后台的 ...

  3. ubuntu问题集锦

    我使用的是ubuntu 14.04 用UltraIOS 制作镜像安装的  ubuntu 问题1:闪屏问题以及文字显示不全 解决方案:重装显卡驱动 解决过程:http://my.oschina.net/ ...

  4. JZYZOJ1539[haoi2015]T2 树链剖分

    http://172.20.6.3/Problem_Show.asp?id=1539 在学校的OJ又写了一次,RE了好多次,原来haoi的时候这道题需要开栈+快读,裸数据结构30分,加上快读50分.o ...

  5. hdu 5592 ZYB's Premutation (权值线段树)

    最近在线段树的世界里遨游,什么都能用线段树做,这不又一道权值线段树了么. ZYB's Premutation Time Limit: 2000/1000 MS (Java/Others)    Mem ...

  6. 【序列莫队+二分答案+树状数组】POJ2104-K-th Number

    [题目大意] 给出一个长度为n的序列和m组查询(i,j,k),输出[i,j]中的第k大数. [思路] 先离散化然后莫队分块.用树状数组来维护当前每个值的个数,然后对于每次询问二分答案即可. 又一次实力 ...

  7. 【左偏树+延迟标记+拓扑排序】BZOJ4003-城池攻占

    [题目大意] 有n个城市构成一棵树,除1号城市外每个城市均有防御值h和战斗变化参量a和v. 现在有m个骑士各自来刷副本,每个其实有一个战斗力s和起始位置c.如果一个骑士的战斗力s大于当前城市的防御值h ...

  8. 【动态规划技巧题】POJ2229-Sumsets

    [题目大意] 把一个数n分成2的指数幂相加的形式,问有几种情况. [思路] 如果当前i为奇数,则必定有至少一个1,可以看作i-1的情形再加上一个1.即f[i]=f[i-1]. 如果当前i为偶数,假设没 ...

  9. [bzoj1006](HNOI2008)神奇的国度(弦图最小染色)【太难不会】

    Description K国是一个热衷三角形的国度,连人的交往也只喜欢三角原则. 他们认为三角关系:即AB相互认识,BC相互认识,CA相互认识,是简洁高效的.为了巩固三角关系,K国禁止四边关系,五边关 ...

  10. moment.js 日期包装类 (说明示例)

    moment.js 日期包装类 Moment.js 1创建时间对象 moment();                                                          ...