Android网络编程之——文件断点下载

一:关于断点下载所涉及到的知识点

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); } }

猜你喜欢

转载自www.cnblogs.com/chenxibobo/p/9657226.html
今日推荐