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
原创粉丝点击