Android 下载器 可断点下载

先上图
支持断点下载,但是程序退出之后再打开不会继续下载,这只是一个Demo练习,有很多Bug还没有来得及修改和优化。
界面非常简单 看图什么都知道了 就不多说了
界面很简单 只看界面都大概知道什么意思了

大概的流程就和下图一样。就分这几个部分。
在这里插入图片描述

initEvents 方法完成了界面的三个Button的功能
①开始下载:首先判断该路径下是否有改文件,如果没有就使用Intent启动一个开始下载的服务类DownloadService。使用intent.putExtra(“fileInfo”, fileInfo) 将 fileinfo传递给服务 (下载的时候不能取消下载,毕竟是个Demo还没有完善)。
②暂停下载:和开始下载的功能一样,把服务换成暂停的服务
③删除文件:将该路径下的文件夹删除。
④广播接收器:注册一个广播接收器,用来接收线程中返回的进度信息。

private void initEvents() {
        tv_name.setText(getFilename(path));
        btn_start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //通过Intent传递参数给service
                if (!getFileExist(DownloadService.DOWNLOAD_PATH + getFilename(path))) {
                    Intent intent = new Intent(getActivity(), DownloadService.class);
                    intent.setAction(DownloadService.ACTION_START);
                    intent.putExtra("fileInfo", fileInfo);
                    getActivity().startService(intent);
                    btn_cenal.setEnabled(false);
                } else {
                    Toast.makeText(getContext(), "文件已存在,不能重复下载", Toast.LENGTH_SHORT).show();
                    BuShow();
                }
            }
        });
        btn_stop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getActivity(), DownloadService.class);
                intent.setAction(DownloadService.ACTION_STOP);
                intent.putExtra("fileInfo", fileInfo);
                getActivity().startService(intent);
            }
        });
        btn_cenal.setOnClickListener(new View.OnClickListener() {
            @SuppressLint("SetTextI18n")
            @Override
            public void onClick(View v) {
                File file = new File(DownloadService.DOWNLOAD_PATH);
                removeFile(file);
                progressBar.setProgress(0);
                tv_show.setText("00%");
            }
        });
        //注册广播接收器
        IntentFilter filter = new IntentFilter();
        filter.addAction(DownloadService.ACTION_UPDATE);
        getActivity().registerReceiver(mReceiver, filter);

    }

以下几个是完成小功能的几个方法 不再多说

//判断文件是否存在
    public boolean getFileExist(String filepath) {
        File file = new File(filepath);
        if (file.exists()) {
            return true;
        }
        return false;
    }

    //删除文件
    public void removeFile(File file) {
        //如果是文件直接删除
        if (file.isFile()) {
            file.delete();

        }
        //如果是目录递归判断,如果是空目录直接删除
        else if (file.isDirectory()) {
            File[] childFile = file.listFiles();
            if (childFile == null || childFile.length <= 0) {
                file.delete();
            } else {
                for (File f : childFile) {
                    removeFile(f);
                }
                file.delete();
            }
        }
    }

    private String getFilename(String path) {
        return path.substring(path.lastIndexOf("/") + 1);
    }

↓↓↓ 更新UI的广播接收器

/**
     * 更新UI的广播接收器
     */
    BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @SuppressLint("SetTextI18n")
        @Override
        public void onReceive(Context context, Intent intent) {
            if (DownloadService.ACTION_UPDATE.equals(intent.getAction())) {
                int finished = intent.getIntExtra("finished", 0);
                progressBar.setProgress(finished);
                tv_show.setText(finished + "%");
                if (progressBar.getProgress() == 100) {
                    btn_cenal.setEnabled(true);
                    BuShow();
                }
            }
        }
    };

这个类的全部代码

public class Fragment_DownLoad extends Fragment {

    private View view;
    private String path;
    private Button btn_start;
    private Button btn_stop;
    private TextView tv_name;
    private ProgressBar progressBar;
    private FileInfo fileInfo;
    private TextView tv_show;
    private Button btn_cenal;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        view = inflater.inflate(R.layout.activity_download, container, false);
        initview();
        initEvents();
        return view;
    }

    private void initview() {
        path = "http://cdn.llsapp" +
                ".com/android/LLS-v4.0-595-20160908-143200.apk";
        btn_start = view.findViewById(R.id.btn_start);
        btn_stop = view.findViewById(R.id.btn_stop);
        btn_cenal = view.findViewById(R.id.btn_cenal);
        progressBar = view.findViewById(R.id.progress);
        tv_show = view.findViewById(R.id.tv_show);
        tv_name = view.findViewById(R.id.tv_name);
        //tv_add = view.findViewById(R.id.tv_add);
        //创建文件信息对象
        fileInfo = new FileInfo(0, path, getFilename(path), 0, 0);
        progressBar.setMax(100);

        if (getFileExist(DownloadService.DOWNLOAD_PATH + getFilename(path))) {
            btn_cenal.setEnabled(true);
        } else {
            btn_cenal.setEnabled(false);
        }

    }

    private void initEvents() {
        tv_name.setText(getFilename(path));
        btn_start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //通过Intent传递参数给service
                if (!getFileExist(DownloadService.DOWNLOAD_PATH + getFilename(path))) {
                    Intent intent = new Intent(getActivity(), DownloadService.class);
                    intent.setAction(DownloadService.ACTION_START);
                    intent.putExtra("fileInfo", fileInfo);
                    getActivity().startService(intent);
                    btn_cenal.setEnabled(false);
                } else {
                    Toast.makeText(getContext(), "文件已存在,不能重复下载", Toast.LENGTH_SHORT).show();
                    BuShow();
                }
            }
        });
        btn_stop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getActivity(), DownloadService.class);
                intent.setAction(DownloadService.ACTION_STOP);
                intent.putExtra("fileInfo", fileInfo);
                getActivity().startService(intent);
            }
        });
        btn_cenal.setOnClickListener(new View.OnClickListener() {
            @SuppressLint("SetTextI18n")
            @Override
            public void onClick(View v) {
                File file = new File(DownloadService.DOWNLOAD_PATH);
                removeFile(file);
                progressBar.setProgress(0);
                tv_show.setText("00%");
            }
        });
        //注册广播接收器
        IntentFilter filter = new IntentFilter();
        filter.addAction(DownloadService.ACTION_UPDATE);
        getActivity().registerReceiver(mReceiver, filter);

    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        getActivity().unregisterReceiver(mReceiver);
    }

    /**
     * 更新UI的广播接收器
     */
    BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @SuppressLint("SetTextI18n")
        @Override
        public void onReceive(Context context, Intent intent) {
            if (DownloadService.ACTION_UPDATE.equals(intent.getAction())) {
                int finished = intent.getIntExtra("finished", 0);
                progressBar.setProgress(finished);
                tv_show.setText(finished + "%");
                if (progressBar.getProgress() == 100) {
                    btn_cenal.setEnabled(true);
                    BuShow();
                }
            }
        }
    };

    private void BuShow() {
        AlertDialog.Builder builder = new AlertDialog.Builder(view.getContext());
        builder.setTitle("提示:");
        builder.setMessage("下载成功是否打开");
        builder.setCancelable(true);
        builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {

            }
        });
        builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                File file = new File(DownloadService.DOWNLOAD_PATH + getFilename(path));
                try {
                    OpenFileUtils.openFile(getContext(), file);
                } catch (Exception e) {
                    Toast.makeText(view.getContext(), "错误:" + e.toString(), Toast
                            .LENGTH_SHORT)
                            .show();
                    e.printStackTrace();
                }
            }
        });
        builder.show();
    }

    //判断文件是否存在
    public boolean getFileExist(String filepath) {
        File file = new File(filepath);
        if (file.exists()) {
            return true;
        }
        return false;
    }

    //删除文件
    public void removeFile(File file) {
        //如果是文件直接删除
        if (file.isFile()) {
            file.delete();

        }
        //如果是目录递归判断,如果是空目录直接删除
        else if (file.isDirectory()) {
            File[] childFile = file.listFiles();
            if (childFile == null || childFile.length <= 0) {
                file.delete();
            } else {
                for (File f : childFile) {
                    removeFile(f);
                }
                file.delete();
            }

        }
        Toast.makeText(getContext(), "删除成功", Toast.LENGTH_SHORT).show();
    }

    private String getFilename(String path) {
        return path.substring(path.lastIndexOf("/") + 1);
    }
}

Downloadservice类继承了Service
在 onStartCommand方法中根据用户的命令 判断是执行下载还是
暂停下载。
开始下载:启动一个子线程
暂停下载:mTask.isPause = true; 这句话会导致线程执行 return 不再进行下载。

 @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (ACTION_START.equals(intent.getAction())) {
            FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");
            Log.d("测试", "start:" + fileInfo.toString());
            //启动初始化线程
            new InitThread(fileInfo).start();
        } else if (ACTION_STOP.equals(intent.getAction())) {
            FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");
            Log.d("测试", "stop:" + fileInfo.toString());
            if (mTask != null) {
                mTask.isPause = true;
            }

        }
        return super.onStartCommand(intent, flags, startId);
    }

InitThread类继承了Thread
这个类的功能就是在获取网络文件,在本地创建一个和网络文件大小一样的文件,最后启动handler

class InitThread extends Thread {
        private FileInfo mFileInfo = null;
        private HttpURLConnection conn;
        private RandomAccessFile raf;

        public InitThread(FileInfo mFileInfo) {
            this.mFileInfo = mFileInfo;
        }

        @Override
        public void run() {
            try {
                //连接网络文件
                URL url = new URL(mFileInfo.getUrl());
                conn = (HttpURLConnection) url.openConnection();
                conn.setConnectTimeout(3000);
                //下载用GET  其他的用POST
                conn.setRequestMethod("GET");
                int lenght = -1;
                if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {//OK
                    //获取文件长度
                    lenght = conn.getContentLength();
                }
                //判断获取到的文件长度是否有问题
                if (lenght <= 0) {
                    return;
                }
                File dir = new File(DOWNLOAD_PATH);
                if (!dir.exists()) {
                    dir.mkdir();
                }
                //在本地创建文件
                File file = new File(dir, mFileInfo.getFileName());
                //权限可读写删
                raf = new RandomAccessFile(file, "rwd");

                //本地文件长度等于网络文件的长度
                raf.setLength(lenght);
                mFileInfo.setLength(lenght);
                handler.obtainMessage(MSG_INIT, mFileInfo).sendToTarget();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                conn.disconnect();
                try {
                    raf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

handler 启动下载任务

Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case MSG_INIT:
                    FileInfo fileInfo = (FileInfo) msg.obj;
                    Log.d("测试", "Init:" + fileInfo);
                    //启动下载任务
                    mTask = new DownloadTask(DownloadService.this, fileInfo);
                    mTask.download();
                    break;
            }
        }
    };

DownloadService 完整的代码

public class DownloadService extends Service {

    public static final String ACTION_START = "ACTION_START";
    public static final String ACTION_STOP = "ACTION_STOP";
    public static final String ACTION_UPDATE = "ACTION_UPDATE";
    //获取SD卡根目录
    public static final String DOWNLOAD_PATH = Environment.getExternalStorageDirectory()
            .getAbsolutePath() + "/downloads/";
    public static final int MSG_INIT = 0;
    private DownloadTask mTask = null;


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (ACTION_START.equals(intent.getAction())) {
            FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");
            Log.d("测试", "start:" + fileInfo.toString());
            //启动初始化线程
            new InitThread(fileInfo).start();
        } else if (ACTION_STOP.equals(intent.getAction())) {
            FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");
            Log.d("测试", "stop:" + fileInfo.toString());
            if (mTask != null) {
                mTask.isPause = true;
            }

        }
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @SuppressLint("HandlerLeak")
    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case MSG_INIT:
                    FileInfo fileInfo = (FileInfo) msg.obj;
                    Log.d("测试", "Init:" + fileInfo);
                    //启动下载任务
                    mTask = new DownloadTask(DownloadService.this, fileInfo);
                    mTask.download();
                    break;
            }
        }
    };


    /*
        初始化子线程
     */
    class InitThread extends Thread {
        private FileInfo mFileInfo = null;
        private HttpURLConnection conn;
        private RandomAccessFile raf;

        public InitThread(FileInfo mFileInfo) {
            this.mFileInfo = mFileInfo;
        }

        @Override
        public void run() {
            try {
                //连接网络文件
                URL url = new URL(mFileInfo.getUrl());
                conn = (HttpURLConnection) url.openConnection();
                conn.setConnectTimeout(3000);
                //下载用GET  其他的用POST
                conn.setRequestMethod("GET");
                int lenght = -1;
                if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {//OK
                    //获取文件长度
                    lenght = conn.getContentLength();
                }
                //判断获取到的文件长度是否有问题
                if (lenght <= 0) {
                    return;
                }
                File dir = new File(DOWNLOAD_PATH);
                if (!dir.exists()) {
                    dir.mkdir();
                }
                //在本地创建文件
                File file = new File(dir, mFileInfo.getFileName());
                //权限可读写删
                raf = new RandomAccessFile(file, "rwd");

                //本地文件长度等于网络文件的长度
                raf.setLength(lenght);
                mFileInfo.setLength(lenght);
                handler.obtainMessage(MSG_INIT, mFileInfo).sendToTarget();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                conn.disconnect();
                try {
                    raf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

DownloadTask 类 下载任务类

内部类DownloadThread继承Thread
因为牵扯到断点下载,所以需要用到数据库,每次开始下载之前先判断数据库中是否有数据,如果有数据就接着下载,如果没有数据就向数据库插入线程信息。
里边有注释看注释吧

/**
     * 下载线程
     */
    class DownloadThread extends Thread {
        private ThreadInfo mThreadinfo = null;
        private HttpURLConnection conn;
        private RandomAccessFile raf;
        private InputStream input;

        public DownloadThread(ThreadInfo mThreadinfo) {
            this.mThreadinfo = mThreadinfo;
        }

        public void run() {
            /*
            向数据库插入线程信息
             */
            if (!mDao.isExists(mThreadinfo.getUrl(), mFileinfo.getId())) {
                mDao.insertThread(mThreadinfo);
            }
            try {
                URL url = new URL(mThreadinfo.getUrl());
                conn = (HttpURLConnection) url.openConnection();
                conn.setConnectTimeout(3000);
                conn.setRequestMethod("GET");
                /*
                设置下载位置
                 */
                //开始位置 = 线程的开始位置+线程的完成位置
                int start = mThreadinfo.getStart() + mThreadinfo.getFinished();
                //下载的范围
                conn.setRequestProperty("Range", "bytes=" + start + "-" + mThreadinfo.getEnd());
                /*
                设置文件写入位置
                 */
                File file = new File(DownloadService.DOWNLOAD_PATH, mFileinfo.getFileName());
                raf = new RandomAccessFile(file, "rwd");
                raf.seek(start);
                mFinished += mThreadinfo.getFinished();
                /*
                开始下载
                 */
                if (conn.getResponseCode() == HttpURLConnection.HTTP_PARTIAL) {
                    //1.读取数据
                    input = conn.getInputStream();
                    byte[] buffer = new byte[1024 * 4];
                    int len = -1;
                    long time = System.currentTimeMillis();
                    while ((len = input.read(buffer)) != -1) {
                        //2.写入文件
                        raf.write(buffer, 0, len);
                        //4.把下载进度发送广播给Activity
                        mFinished += len;
                        //每0.5s刷新一次界面
                        if (System.currentTimeMillis() - time > 500) {
                            time = System.currentTimeMillis();
                            Intent intent = new Intent(DownloadService.ACTION_UPDATE);
                            intent.putExtra("finished", (int) (mFinished * 100 / mFileinfo
                                    .getLength()));
                            context.sendBroadcast(intent);
                        }
                        //3.下载暂停时,把下载进度保存到数据库
                        if (isPause) {
                            mDao.updateThread(mThreadinfo.getUrl(), mThreadinfo.getId(), mFinished);
                            return;
                        }
                    }
                }
                //删除线程信息
                mDao.deleteThread(mThreadinfo.getUrl(), mThreadinfo.getId());
                Log.d("测试", "删除了");
                Intent intent = new Intent(DownloadService.ACTION_UPDATE);
                intent.putExtra("finished", 100);
                context.sendBroadcast(intent);


            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    conn.disconnect();
                    input.close();
                    raf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }

        }
    }

还有一个download方法 创建下载线程

//创建下载线程
    public void download() {
        //读取数据库的线程信息
        List<ThreadInfo> threadInfos = mDao.getThreads(mFileinfo.getUrl());
        ThreadInfo threadInfo = null;
        if (threadInfos.size() == 0) {
            //初始化线程信息对象
            threadInfo = new ThreadInfo(0, mFileinfo.getUrl(), 0, mFileinfo.getLength(), 0);
        } else {
            threadInfo = threadInfos.get(0);
        }
        //创建子线程进行下载
        new DownloadThread(threadInfo).start();
    }

DownloadTask 全部代码


        this.context = context;
        this.mFileinfo = mFileinfo;
        mDao = new ThreadDAOImpl(context);
    }

    //创建下载线程
    public void download() {
        //读取数据库的线程信息
        List<ThreadInfo> threadInfos = mDao.getThreads(mFileinfo.getUrl());
        ThreadInfo threadInfo = null;
        if (threadInfos.size() == 0) {
            //初始化线程信息对象
            threadInfo = new ThreadInfo(0, mFileinfo.getUrl(), 0, mFileinfo.getLength(), 0);
        } else {
            threadInfo = threadInfos.get(0);
        }
        //创建子线程进行下载
        new DownloadThread(threadInfo).start();
    }


    /**
     * 下载线程
     */
    class DownloadThread extends Thread {
        private ThreadInfo mThreadinfo = null;
        private HttpURLConnection conn;
        private RandomAccessFile raf;
        private InputStream input;

        public DownloadThread(ThreadInfo mThreadinfo) {
            this.mThreadinfo = mThreadinfo;
        }

        public void run() {
            /*
            向数据库插入线程信息
             */
            if (!mDao.isExists(mThreadinfo.getUrl(), mFileinfo.getId())) {
                mDao.insertThread(mThreadinfo);
            }
            try {
                URL url = new URL(mThreadinfo.getUrl());
                conn = (HttpURLConnection) url.openConnection();
                conn.setConnectTimeout(3000);
                conn.setRequestMethod("GET");
                /*
                设置下载位置
                 */
                //开始位置 = 线程的开始位置+线程的完成位置
                int start = mThreadinfo.getStart() + mThreadinfo.getFinished();
                //下载的范围
                conn.setRequestProperty("Range", "bytes=" + start + "-" + mThreadinfo.getEnd());
                /*
                设置文件写入位置
                 */
                File file = new File(DownloadService.DOWNLOAD_PATH, mFileinfo.getFileName());
                raf = new RandomAccessFile(file, "rwd");
                raf.seek(start);
                mFinished += mThreadinfo.getFinished();
                /*
                开始下载
                 */
                if (conn.getResponseCode() == HttpURLConnection.HTTP_PARTIAL) {
                    //1.读取数据
                    input = conn.getInputStream();
                    byte[] buffer = new byte[1024 * 4];
                    int len = -1;
                    long time = System.currentTimeMillis();
                    while ((len = input.read(buffer)) != -1) {
                        //2.写入文件
                        raf.write(buffer, 0, len);
                        //4.把下载进度发送广播给Activity
                        mFinished += len;
                        //每0.5s刷新一次界面
                        if (System.currentTimeMillis() - time > 500) {
                            time = System.currentTimeMillis();
                            Intent intent = new Intent(DownloadService.ACTION_UPDATE);
                            intent.putExtra("finished", (int) (mFinished * 100 / mFileinfo
                                    .getLength()));
                            context.sendBroadcast(intent);
                        }
                        //3.下载暂停时,把下载进度保存到数据库
                        if (isPause) {
                            mDao.updateThread(mThreadinfo.getUrl(), mThreadinfo.getId(), mFinished);
                            return;
                        }
                    }
                }
                //删除线程信息
                mDao.deleteThread(mThreadinfo.getUrl(), mThreadinfo.getId());
                Log.d("测试", "删除了");
                Intent intent = new Intent(DownloadService.ACTION_UPDATE);
                intent.putExtra("finished", 100);
                context.sendBroadcast(intent);


            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    conn.disconnect();
                    input.close();
                    raf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }

        }
    }
}

差不多了,主要的就这么多东西,我也是第一次写博客,也不知道怎么写,我觉得自己把写的代码以这种形式再回忆一遍记忆的会更清楚。有些知识点我也不是很理解。慢慢积累。会成为大佬的。
有的代码就是自己知道是干嘛的就是说不出来。以自己理解的方法理解了。 以后尽量写的更加详细。 对了。我这个是跟着慕课网上学习的 网址:https://www.imooc.com/course/list 大家可以去看看,我就是跟着视频写的。改动不大。

发布了3 篇原创文章 · 获赞 3 · 访问量 366

猜你喜欢

转载自blog.csdn.net/weixin_43355729/article/details/90339883