整体的思路:

在下载文件时,将进度写入数据库,同一时候通知该ContentProvider的观察者更新页面,这个通知过程不要太频繁。我设置了10次,否则页面会灰常卡。

假设异常中断(网络中断或程序被kill)也没有关系。由于数据库中有记录,仅仅要从该文件的断点位置继续下载就能够了。关键在于一切以数据库为准

就能够了。

同一时候要注意的是:

1、自始至终处理的都是同一个PO对象,没有直接用类的成员变量,由于下次启动线程。成员变量会变化,导致诡异的下载文件不正确应。

2、启动线程后,将该线程保存进static类变量 downloaders 里,方便在Service类外进行停止线程。

3、由于有一个或多个下载进度的页面。所以在下载更新数据库的同一时候。进行了通知观察者的操作。当然也能够有其它的方法,比方,

1)在Activity里加入一个线程时刻扫描数据库等等,只是感觉没有这个好。

2)也实用更新进度的时候,不断发广播的方式,在Activity中注冊广播接收者更新页

大家有更好的办法请回复。

红色字体部分为关键。其它都是浮云。

/**

 *

 * 因为该Service用于多线程下载文件,须要确保原子性。一直处理一个DownLoadFilePO对象

 *

 * <p>detailed comment

 * @see

 * @since 1.0

 */

public class DownLoadService extends Service {

    private static final String TAG = "DownLoadFileService";

    private String downloadUrl;

    private boolean showToast;

    public static Map<String, DownLoadThread> downloaders = new HashMap<String, DownLoadThread>();

@Override

    public IBinder onBind(Intent arg0) {

        return null;

    }

@Override

    public void onCreate() {

        //仅仅运行一次

        super.onCreate();

    }

@Override

    @Deprecated

    public void onStart(Intent intent, int startId) {

        super.onStart(intent, startId);

        if (intent == null) {

            return;

        }

        downloadUrl = intent.getStringExtra("downloadUrl");

        showToast = intent.getBooleanExtra("showToast", false);

DownLoadThread downLoadThread = new DownLoadThread(downloadUrl);

        Thread thread = new Thread(downLoadThread);

        thread.start();

}

/**

     *

     * 下载线程

     *

     * <p>detailed comment

     * @see

     * @since 1.0

     */

    public class DownLoadThread implements Runnable {

        String url;

        boolean downloading = true;

DownLoadThread(String downloadUrl) {

            this.url = downloadUrl;

        }

/**

         * 终止线程

         */

        public void stop() {

            this.downloading = false;

        }

@Override

        public void run() {

            //查看是否已经在下载

            DownLoadFilePO po = queryByDownLoadUrl(url);

            if (po != null) {

if (showToast) {

                    showToast(getResources().getString(R.string.this_file_is_downloaded));

                } else {

                    downloaders.put(downloadUrl, this);

                    showToast(getResources().getString(R.string.this_file_begin_download));

                    download(po);

                }

            } else {

                po = init(url);

                initProgress(po);

                downloaders.put(downloadUrl, this);

                download(po);

            }

        }

/**

         * 下载

         */

        private void download(DownLoadFilePO po) {

            HttpURLConnection connection = null;

            RandomAccessFile randomAccessFile = null;

            InputStream is = null;

            try {

                URL url = new URL(downloadUrl);

                connection = (HttpURLConnection) url.openConnection();

                connection.setConnectTimeout(5000);

                connection.setRequestMethod("GET");

                // 设置远程文件的下载范围。格式为Range:bytes x-y;

                connection.setRequestProperty("Range",

                        "bytes=" + po.getCurrent_size() + "-" + po.getTotal_size());

                //设置本地文件的下载開始位置

                File localFile = new File(po.getFile());

                randomAccessFile = new RandomAccessFile(localFile, "rwd");

                randomAccessFile.seek(po.getCurrent_size());

                is = connection.getInputStream();

                byte[] buffer = new byte[1024000];

                int length = -1;

                int count = 0;

                while ((length = is.read(buffer)) != -1) {

                    randomAccessFile.write(buffer, 0, length);

                    // 更新数据库中的下载信息

                    po.setCurrent_size(po.getCurrent_size() + length);

                    updateProgress(po);

                    // 用消息将下载信息传给进度条,对进度条进行更新

                    count++;

                    if (count % 10 == 0) {

                        getContentResolver().notifyChange(ConstantUtil.uri, null);//假设改变数据。则通知全部人

                        Log.v(TAG, po.getName() + "=======下载进度======" + po.getCurrent_size());

                    }

                    if (!downloading) {

                        Log.v(TAG, po.getName() + "=======暂停====" + po.getCurrent_size());

                        downloaders.remove(po.getDownloadUrl());

                        return;

                    }

                }

                //下载完毕

                if (po.getCurrent_size() == po.getTotal_size()) {

                    finishProgress(po);

                }

                downloaders.remove(po.getDownloadUrl());

            } catch (Exception e) {

                getContentResolver().notifyChange(ConstantUtil.uri, null);//假设改变数据,则通知全部人

                this.stop();

                e.printStackTrace();

            } finally {

                try {

                    is.close();

                    randomAccessFile.close();

                    connection.disconnect();

                } catch (Exception e) {

                    e.printStackTrace();

                }

            }

        }

    }

/**

     * 初始化

     */

    private DownLoadFilePO init(String downloadUrl) {

        DownLoadFilePO po = new DownLoadFilePO();

try {

            if (!FileUtil.checkFileDir()) {

                return null;

            }

URL url = new URL(downloadUrl);

            HttpURLConnection connection = (HttpURLConnection) url.openConnection();

            connection.setConnectTimeout(5000);

            connection.setRequestMethod("GET");

            int fileSize = connection.getContentLength();

            //总大小

            String fileName = FileUtil.getFileNameByUrl(downloadUrl);

            try {

                fileName = URLDecoder.decode(fileName, "UTF-8");

            } catch (UnsupportedEncodingException e) {

                Log.e(TAG, e.getMessage());

            }

            File localFile = new File(ConstantUtil.downloadFileDir + fileName);

            //推断同名文件已经存在。如存在同名的文件。需改动文件名 sample.mp4 ==>sample(1).mp4

            int i = 1;

            while (localFile.exists()) {

                int index = fileName.lastIndexOf(".");

                fileName = fileName.substring(0, index) + "(" + i + ")"

                        + fileName.substring(index, fileName.length());

                localFile = new File(ConstantUtil.downloadFileDir + fileName);

                i++;

            }

            localFile.createNewFile();

            i = 0;

            //            }

            // 本地訪问文件

            RandomAccessFile accessFile = new RandomAccessFile(localFile, "rwd");

            accessFile.setLength(fileSize);

            accessFile.close();

            connection.disconnect();

po.setName(fileName);

            po.setDownloadUrl(downloadUrl);

            po.setCurrent_size(0L);

            po.setTotal_size(fileSize);

            po.setFile(localFile.getAbsolutePath());

        } catch (Exception e) {

            e.printStackTrace();

        }

        return po;

    }

/**

     * 向数据库中插入初始化数据

     */

    private void initProgress(DownLoadFilePO po) {

        ContentValues values = new ContentValues();

        values.put(DataBaseUtil.DOWNLOAD_FILE_NAME, po.getName());

        values.put(DataBaseUtil.CREATE_FILE_DATE, new Date().toString());

        values.put(DataBaseUtil.DOWNLOAD_FILE_TOTAL_SIZE, po.getTotal_size());

        values.put(DataBaseUtil.DOWNLOAD_FILE_CURRENT_SIZE, 0);

        values.put(DataBaseUtil.DOWNLOAD_FINISHED, false);

        values.put(DataBaseUtil.DOWNLOAD_URL, po.getDownloadUrl());

        values.put(DataBaseUtil.DONWLOAD_FILE_PATH, po.getFile());

this.getContentResolver().insert(ConstantUtil.uri, values); //向ContentProvider插入数据 

    }

/**

     *

     * 更新进度

     *

     *<p>detail comment

     *@see

     *@since

     *

     */

    private void updateProgress(DownLoadFilePO po) {

        ContentValues values = new ContentValues();

        values.put(DataBaseUtil.DOWNLOAD_FILE_CURRENT_SIZE, po.getCurrent_size());

        this.getContentResolver().update(ConstantUtil.uri, values,

                DataBaseUtil.DOWNLOAD_URL + "=?

", new String[] { po.getDownloadUrl() });

    }

/**

     *

     * 完毕下载

     *

     *<p>detail comment

     *@param currentSize

     *@see

     *@since

     *

     */

    private void finishProgress(DownLoadFilePO po) {

        ContentValues values = new ContentValues();

        values.put(DataBaseUtil.DOWNLOAD_FILE_CURRENT_SIZE, po.getTotal_size());

        values.put(DataBaseUtil.DOWNLOAD_FINISHED, true);

this.getContentResolver().update(ConstantUtil.uri, values,

                DataBaseUtil.DOWNLOAD_URL + "=?

", new String[] { po.getDownloadUrl() });

        getContentResolver().notifyChange(ConstantUtil.uri, null);

    }

/**

     *

     * 查看该url是否在下载

     *

     *<p>detail comment

     *@param url

     *@return

     *@see

     *@since

     *

     */

    private DownLoadFilePO queryByDownLoadUrl(String url) {

        Cursor cursor = this.getContentResolver().query(

                ConstantUtil.uri,

                new String[] { DataBaseUtil.DOWNLOAD_FILE_NAME,

                        DataBaseUtil.DOWNLOAD_FILE_CURRENT_SIZE,

                        DataBaseUtil.DOWNLOAD_FILE_TOTAL_SIZE, DataBaseUtil.DONWLOAD_FILE_PATH,

                        DataBaseUtil.DOWNLOAD_FINISHED }, DataBaseUtil.DOWNLOAD_URL + "=?",

                new String[] { url }, null);

        if (cursor.moveToNext()) {

            DownLoadFilePO po = new DownLoadFilePO();

            for (int i = 0; i < cursor.getCount(); i++) {

                cursor.moveToPosition(i);

                po.setName(cursor.getString(0));

                po.setCurrent_size(cursor.getLong(1));

                po.setTotal_size(cursor.getLong(2));

                po.setFile(cursor.getString(3));

                po.setDownloadUrl(url);

            }

            return po;

        }

return null;

    }

private void showToast(final String toast) {

        Handler handler = new Handler(Looper.getMainLooper());

        handler.post(new Runnable() {

            public void run() {

                Toast.makeText(DownLoadService.this, toast, Toast.LENGTH_SHORT).show();

            }

        });

    }

}



用Service实现断点下载的更多相关文章

  1. 安卓(android)之实现断点下载功能

    一.建立实体类 1.文件实体类 package com.example.zjw.myapplication.dao; import java.io.Serializable; /** * 预下载文件实 ...

  2. Retrofit 2.0 超能实践(四),完成大文件断点下载

    作者:码小白 文/CSDN 博客 本文出自:http://blog.csdn.net/sk719887916/article/details/51988507 码小白 通过前几篇系统的介绍和综合运用, ...

  3. andoid 多线程断点下载

    本示例介绍在Android平台下通过HTTP协议实现断点续传下载. 我们编写的是Andorid的HTTP协议多线程断点下载应用程序.直接使用单线程下载HTTP文件对我们来说是一件非常简单的事.那么,多 ...

  4. Android学习笔记_15_网络通信之文件断点下载

    一.断点下载原理: 使用多线程下载文件可以更快完成文件的下载,多线程下载文件之所以快,是因为其抢占的服务器资源多.如:假设服务器同时最多服务100个用户,在服务器中一条线程对应一个用户,100条线程在 ...

  5. 我的Android进阶之旅------>Android基于HTTP协议的多线程断点下载器的实现

    一.首先写这篇文章之前,要了解实现该Android多线程断点下载器的几个知识点 1.多线程下载的原理,如下图所示 注意:由于Android移动设备和PC机的处理器还是不能相比,所以开辟的子线程建议不要 ...

  6. Java实现多线程断点下载(下载过程中可以暂停)

    线程可以理解为下载的通道,一个线程就是一个文件的下载通道,多线程也就是同时开启好几个下载通道.当服务器提供下载服务时,使用下载者是共享带宽的,在优先级相同的情况下,总服务器会对总下载线程进行平均分配. ...

  7. iOS 大文件断点下载

    iOS 在下载大文件的时候,可能会因为网络或者人为等原因,使得下载中断,那么如何能够进行断点下载呢? // resumeData的文件路径 #define XMGResumeDataFile [[NS ...

  8. 【Java EE 学习 22 下】【单线程下载】【单线程断点下载】【多线程下载】

    一.文件下载简述 1.使用浏览器从网页上下载文件,Servlet需要增加一些响应头信息 (1)response.setContentType("application/force-downl ...

  9. IOS 断点下载

    // // ViewController.m // UI4_断点下载 // // Created by qianfeng on 15/8/19. // Copyright (c) 2015年 ZBC. ...

随机推荐

  1. 标签流 VS 脚本流

    搞过点前端,玩过几个框架之后,基本都会发现框架在设计上的一些套路和流派,今天给大家扒一扒其中的两个书写流派“标签流”和“脚本流” 我们以一个button按钮为例: 这样裸写HTML标签的方式基本没法儿 ...

  2. SqlServer代理作业

    最近一直在学习SqlServer 作业方面的知识,总结一下. 一:作业存在的库. msdb  use msdb Msdb数据库是代理服务数据库,为其报警.任务调度和记录操作员的操作提供存储空间. 二: ...

  3. IIS开多个HTTPS站点

    默认情况一个服务器的IIS只能绑定一个HTTPS也就是443端口 要实现多个站点对应HTTPS只能更改IIS配置 地址:C:\Windows\system32\inetsrv\config\appli ...

  4. .NetCore Cap 结合 RabbitMQ 实现消息订阅

    开源分布式消息框架 Cap 可以在GitHub上拉也可以通过nuget添加 上一篇博文写了 Windows RabbitMQ的安装使用 Cap支持事务,通过捕获数据库上下文连接对象实现 消息事务,消息 ...

  5. API的防重

    说说API的防重放机制 2017-03-20 18:19 by 轩脉刃, 685 阅读, 7 评论, 收藏, 编辑 说说API的防重放机制 我们在设计接口的时候,最怕一个接口被用户截取用于重放攻击.重 ...

  6. 基于jquery的水平滚轴组件,多参数可设置。

    闲来无事,继续封装.此次封装的为水平滚轴组件,可选择滚动的距离大小.闲话不多说,直接上图. 参数说明: vis:4                中间区域可显示的 li 个数 scroll:4     ...

  7. 基于CommonsCollections4的Gadget分析

    基于CommonsCollections4的Gadget分析 Author:Welkin 0x1 背景及概要 随着Java应用的推广和普及,Java安全问题越来越被人们重视,纵观近些年来的Java安全 ...

  8. strlen()和mb_strlen()

    换行需要用双引号,单引号只会输出字符. strlen()返回字符串所占的字节数,对于utf8编码的中文,一个汉字占三个字节. mb_strlen()返回字符个数,如果不写第二个参数,就会使用内部编码, ...

  9. css实现自适应正方形

    这里介绍7种方法,仅供参考. 1.vm单位 <div class="square-shape">这是一个可以自适应的正方形,此法适用于移动端web页面.</div ...

  10. 图的遍历 之 深搜dfs

    DFS 遍历 深度优先搜索是一个递归过程,有回退过程. 对一个无向连通图,在访问图中某一起始顶点u 后,由u 出发,访问它的某一邻接顶点v1:再从v1 出发,访问与v1 邻接但还没有访问过的顶点v2: ...