Android网络编程之——文件断点下载(暂停/继续/重新下载)
来源:互联网 发布:淘宝上的宠物狗能买吗 编辑:程序博客网 时间:2024/06/06 09:08
开头还是不说废话了直接进入主题吧!
一:关于断点下载所涉及到的知识点
1.对SQLite的增删改查(主要用来保存当前任务的一些信息)
2.HttpURLConnection的请求配置
HttpURLConnection connection = null;//设置下载请求属性connection.setRequestProperty();
3.RandomAccessFile 对文件进行写入
RandomAccessFile rwd = null;//从文件的某一位置写入rwd.seek();
4.基本的I/O流操作,以及逻辑处理
二:第一步我们先来创建一张表用来保存我们的下载信息
public class DbHelper extends SQLiteOpenHelper { public static String TABLE = "file";//表名 public DbHelper(Context context) { super(context, "download.db", null, 1); } @Override public void onCreate(SQLiteDatabase db) { //文件名,下载地址,下载文件的总长度,当前下载完成长度 db.execSQL("create table file(fileName varchar,url varchar,length integer,finished integer)"); }}
三:第二步同时既然是对数据库的操作,那我们在DbHelper.class
中来写好几个公用方法
/** * 插入一条下载信息 */ public void insertData(SQLiteDatabase db, FileInfo info) { ContentValues values = new ContentValues(); values.put("fileName", info.getFileName()); values.put("url", info.getUrl()); values.put("length", info.getLength()); values.put("finished", info.getFinished()); db.insert(TABLE, null, values); } /** * 是否已经插入这条数据 */ public boolean isExist(SQLiteDatabase db, FileInfo info) { Cursor cursor = db.query(TABLE, null, "url = ?", new String[]{info.getUrl()}, null, null, null, null); boolean exist = cursor.moveToNext(); cursor.close(); return exist; } /** * 查询已经存在的一条信息 */ public FileInfo queryData(SQLiteDatabase db, String url) { Cursor cursor = db.query(TABLE, null, "url = ?", new String[]{url}, null, null, null, null); FileInfo info = new FileInfo(); if (cursor != null) { while (cursor.moveToNext()) { String fileName = cursor.getString(cursor.getColumnIndex("fileName")); int length = cursor.getInt(cursor.getColumnIndex("length")); int finished = cursor.getInt(cursor.getColumnIndex("finished")); info.setStop(false); info.setFileName(fileName); info.setUrl(url); info.setLength(length); info.setFinished(finished); } cursor.close(); } return info; } /** * 恢复一条下载信息 */ public void resetData(SQLiteDatabase db, String url) { ContentValues values = new ContentValues(); values.put("finished", 0); values.put("length", 0); db.update(TABLE, values, "url = ?", new String[]{url}); }
3.从上面方法中可以看出来还有一个FileInfo对象,没错这是自己创建的一个下载任务实体类一起来看看吧
//保存下载任务信息public class FileInfo { private String fileName;//文件名 private String url;//下载地址 private int length;//文件大小 private int finished;//下载以已完成进度 private boolean isStop = false;//是否暂停下载 private boolean isDownLoading = false;//是否正在下载 //...... //剩下的都是对应的get and set 方法就不贴出来了,自动生成就好了
四:第三步我们创建一个类DownLoaderManger
来管理我们的下载任务包括、添加下载任务、开始下载、暂停下载、重新下载
public class DownLoaderManger { public static String FILE_PATH = Environment.getExternalStorageDirectory() + "/azhong";//文件下载保存路径 private DbHelper helper;//数据库帮助类 private SQLiteDatabase db; private OnProgressListener listener;//进度回调监听 private Map<String, FileInfo> map = new HashMap<>();//保存正在下载的任务信息 private static DownLoaderManger manger; private DownLoaderManger(DbHelper helper, OnProgressListener listener) { this.helper = helper; this.listener = listener; db = helper.getReadableDatabase(); } /** * 单例模式 * * @param helper 数据库帮助类 * @param listener 下载进度回调接口 * @return */ public static synchronized DownLoaderManger getInstance(DbHelper helper, OnProgressListener listener) { if (manger == null) { synchronized (DownLoaderManger.class) { if (manger == null) { manger = new DownLoaderManger(helper, listener); } } } return manger; } /** * 开始下载任务 */ public void start(String url) { db = helper.getReadableDatabase(); FileInfo info = helper.queryData(db, url); map.put(url, info); //开始任务下载 new DownLoadTask(map.get(url), helper, listener).start(); } /** * 停止下载任务 */ public void stop(String url) { map.get(url).setStop(true); } /** * 重新下载任务 */ public void restart(String url) { stop(url); try { File file = new File(FILE_PATH, map.get(url).getFileName()); if (file.exists()) { file.delete(); } Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } db = helper.getWritableDatabase(); helper.resetData(db, url); start(url); } /** * 获取当前任务状态 */ public boolean getCurrentState(String url) { return map.get(url).isDownLoading(); } /** * 添加下载任务 * * @param info 下载文件信息 */ public void addTask(FileInfo info) { //判断数据库是否已经存在这条下载信息 if (!helper.isExist(db, info)) { db = helper.getWritableDatabase(); helper.insertData(db, info); map.put(info.getUrl(), info); } else { //从数据库获取最新的下载信息 db = helper.getReadableDatabase(); FileInfo o = helper.queryData(db, info.getUrl()); if (!map.containsKey(info.getUrl())) { map.put(info.getUrl(), o); } } }}
五:上面代码中OnProgressListener
接口,当然还有一个最最重要的DownLoadTask
了这里面就是实现了如何断点下载的,下面来一起看下里面的实现逻辑吧。。。
//下载进度接口public interface OnProgressListener { void updateProgress(int max, int progress);}
六:重点–下载线程
/** * 下载文件线程 * 从服务器获取需要下载的文件大小 */public class DownLoadTask extends Thread { private FileInfo info; private SQLiteDatabase db; private DbHelper helper;//数据库帮助类 private int finished = 0;//当前已下载完成的进度 private OnProgressListener listener;//进度回调监听 public DownLoadTask(FileInfo info, DbHelper helper, OnProgressListener listener) { this.info = info; this.helper = helper; this.db = helper.getReadableDatabase(); this.listener = listener; info.setDownLoading(true); } @Override public void run() { getLength(); HttpURLConnection connection = null; RandomAccessFile rwd = null; try { URL url = new URL(info.getUrl()); connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setConnectTimeout(3000); //从上次下载完成的地方下载 int start = info.getFinished(); //设置下载位置(从服务器上取要下载文件的某一段) connection.setRequestProperty("Range", "bytes=" + start + "-" + info.getLength());//设置下载范围 //设置文件写入位置 File file = new File(DownLoaderManger.FILE_PATH, info.getFileName()); rwd = new RandomAccessFile(file, "rwd"); //从文件的某一位置开始写入 rwd.seek(start); finished += info.getFinished(); if (connection.getResponseCode() == 206) {//文件部分下载,返回码为206 InputStream is = connection.getInputStream(); byte[] buffer = new byte[1024 * 4]; int len; while ((len = is.read(buffer)) != -1) { //写入文件 rwd.write(buffer, 0, len); finished += len; info.setFinished(finished); //更新界面显示 Message msg = new Message(); msg.what = 0x123; msg.arg1 = info.getLength(); msg.arg2 = info.getFinished(); handler.sendMessage(msg); //停止下载 if (info.isStop()) { info.setDownLoading(false); //保存此次下载的进度 helper.updateData(db, info); db.close(); return; } } //下载完成 info.setDownLoading(false); helper.updateData(db, info); db.close(); } } catch (Exception e) { e.printStackTrace(); } finally { if (connection != null) { connection.disconnect(); } try { if (rwd != null) { rwd.close(); } } catch (IOException e) { e.printStackTrace(); } } } /** * 首先开启一个线程去获取要下载文件的大小(长度) */ private void getLength() { HttpURLConnection connection = null; try { //连接网络 URL url = new URL(info.getUrl()); connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setConnectTimeout(3000); int length = -1; if (connection.getResponseCode() == 200) {//网络连接成功 //获得文件长度 length = connection.getContentLength(); } if (length <= 0) { //连接失败 return; } //创建文件保存路径 File dir = new File(DownLoaderManger.FILE_PATH); if (!dir.exists()) { dir.mkdirs(); } info.setLength(length); } catch (Exception e) { e.printStackTrace(); } finally { //释放资源 try { if (connection != null) { connection.disconnect(); } } catch (Exception e) { e.printStackTrace(); } } } /** * 更新进度 */ private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case 0x123: if (listener != null) { listener.updateProgress(msg.arg1, msg.arg2); } break; } } };}
七:下载流程—>首先获取要下载文件的总长度—>然后指定从上次结束的位置开始下载文件。客官阅读需仔细哦,精华都在注释里面哦!个人认为重点部分如下两个:
//设置下载位置(从服务器上取要下载文件的某一段)connection.setRequestProperty("Range", "bytes=" + start + "-" + info.getLength());//设置下载范围RandomAccessFile rwd = new RandomAccessFile(file, "rwd");//从文件的某一位置开始写入rwd.seek(start);
八:上面做了一系列准备工作之后,就可以正式开始下载了让我们一起来瞧瞧
1.给主布局界面放两个按钮,来开始/暂停/重新下载
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.azhong.downloader.MainActivity"> <com.azhong.downloader.view.NumberProgressBar android:id="@+id/pb" android:layout_width="match_parent" android:layout_height="wrap_content" /> <Button android:id="@+id/start" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="开始下载" /> <Button android:id="@+id/restart" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:text="重新下载" /></LinearLayout>
2.关于NumberProgressBar的使用可以移驾至这里
3.记得在清单文件中加入 网络访问和内存读写权限哦!
4.既然我们封装了那么久,那肯定用起来就会很简单了
public class MainActivity extends AppCompatActivity implements OnProgressListener { private NumberProgressBar pb;//进度条 private DownLoaderManger downLoader = null; private FileInfo info; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); pb = (NumberProgressBar) findViewById(R.id.pb); final Button start = (Button) findViewById(R.id.start);//开始下载 final Button restart = (Button) findViewById(R.id.restart);//重新下载 final DbHelper helper = new DbHelper(this); downLoader = DownLoaderManger.getInstance(helper, this); info = new FileInfo("Kuaiya482.apk", "http://downloadz.dewmobile.net/Official/Kuaiya482.apk"); downLoader.addTask(info); start.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (downLoader.getCurrentState(info.getUrl())) { downLoader.stop(info.getUrl()); start.setText("开始下载"); } else { downLoader.start(info.getUrl()); start.setText("暂停下载"); } } }); restart.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { downLoader.restart(info.getUrl()); start.setText("暂停下载"); } }); } @Override public void updateProgress(final int max, final int progress) { pb.setMax(max); pb.setProgress(progress); }}
九:断点下载的知识点在文章开头已经给出,剩下的都是加入了自己的逻辑处理和代码封装。感兴趣的伙伴们一定要自己动手尝试着去写,不然你以为你看了一遍会了其实到后来就完全不会了……Demo下载传送门
8 0
- Android网络编程之——文件断点下载(暂停/继续/重新下载)
- 基于android的网络音乐播放器-下载任务的暂停/继续/删除以及断点下载(七)
- 网络编程--多线程断点下载文件
- Android学习笔记_15_网络通信之文件断点下载
- 网络:NSURLSession 下载暂停与继续
- 多媒体编程(多线程下载、断点续传、可以暂停、继续)
- java网络编程之android多线程断点下载并提供下载进度(三)
- 多线程断点下载(开始下载,暂停下载,百分比进度条)
- 多线程断点下载(开始下载,暂停下载,百分比进度条)
- Android学习之文件断点下载
- iOS开发网络篇—文件下载(暂停和恢复)
- iOS开发网络篇—文件下载(四·暂停和恢复)
- iOS开发网络篇—文件下载(四·暂停和恢复)
- iOS开发网络篇—文件下载(暂停和恢复)
- Android:网络:多线程断点下载
- iOS-网络编程 文件上传和断点离线下载
- iOS开发网络篇之文件下载、大文件下载、断点下载
- iOS开发网络篇之文件下载、大文件下载、断点下载
- N26--第十四次博客作业
- Java远程调用(二)实现一个简单的服务框架
- ZOJ 3487Ordinal Numbers
- C 练习实例11 斐波那契数列
- 启动hdfs文件系统后,Browse the filesystem链接打不开
- Android网络编程之——文件断点下载(暂停/继续/重新下载)
- 数据库简易帮助包
- java创建对象的几种方法
- Linux多线程编程(二)-----同步与互斥
- wireshark浅析tcp三次握手
- 图像处理算法(一)---图像常用格式
- Libsvm和Liblinear各自特点和使用经验
- 序列化与反序列化
- ZOJ 3488Conic Section