• 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);
        }
    }
  • 相关阅读:
    mysql配置参数
    nginx配置https,重定向后https变成了http
    网速测试工具
    批量清理mysql进程
    新版本django中的path不能使用正则表达式
    巨好看的xshell配色
    zabbix 基于sendmail发送邮件相关问题
    curl分析请求的各个部分耗时情况
    wqs二分的边界
    oauth2
  • 原文地址:https://www.cnblogs.com/chenxibobo/p/9657226.html
Copyright © 2020-2023  润新知