zoukankan      html  css  js  c++  java
  • 安卓系统下的多线程断点下载实现

    近期研究多线程下载,写了个demo。整理下来,或许会对别人有帮助。
    多线程下载的话一般开启两到三个线程吧。假设线程太多的话时间会浪费在线程的切换上,倒是浪费了大把的时间。线程多了也不是一件好事。
    原理的话看我的还有一篇博文,其实是将代码移植到了安卓系统上。java实现的多线程下载demo

    public class MainActivity extends Activity {
        protected static final int DOWNLOAD_ERROR = 1;
        private static final int THREAD_ERROR = 2;
        public static final int DWONLOAD_FINISH = 3;
        private EditText et_path;
        private EditText et_count;
        /**
         * 存放进度条的布局
         */
        private LinearLayout ll_container;
    
        /**
         * 进度条的集合
         */
        private List<ProgressBar> pbs;
    
        /**
         * android下的消息处理器。在主线程创建。才干够更新ui
         */
        private Handler handler = new Handler(){
            public void handleMessage(Message msg) {
                switch (msg.what) {
                case DOWNLOAD_ERROR:
                    Toast.makeText(getApplicationContext(), "下载失败", 0).show();
                    break;
                case THREAD_ERROR:
                    Toast.makeText(getApplicationContext(), "下载失败,请重试", 0).show();
                    break;
                case DWONLOAD_FINISH:
                    Toast.makeText(getApplicationContext(), "完成下载", 0).show();
                    break;
                }
            };
        };
    
        /**
         * 线程的数量
         */
        private int threadCount = 3;
    
        /**
         * 每一个下载区块的大小
         */
        private long blocksize;
    
        /**
         * 正在执行的线程的数量
         */
        private  int runningThreadCount;
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            et_path = (EditText) findViewById(R.id.et_addr);
            et_count = (EditText) findViewById(R.id.et_num);
            ll_container = (LinearLayout) findViewById(R.id.ll_pb);
        }
    
    
        public void download(View view){
            //下载文件的路径
            final String path = et_path.getText().toString().trim();
            if(TextUtils.isEmpty(path)){
                Toast.makeText(this, "对不起下载路径不能为空", 0).show();
                return;
            }
            String count = et_count.getText().toString().trim();
            if(TextUtils.isEmpty(path)){
                Toast.makeText(this, "对不起,线程数量不能为空", 0).show();
                return;
            }
            threadCount = Integer.parseInt(count);
            //清空掉旧的进度条
            ll_container.removeAllViews();
            //在界面里面加入count个进度条
            pbs = new ArrayList<ProgressBar>();
            for(int j=0;j<threadCount;j++){
                ProgressBar pb = (ProgressBar) View.inflate(this, R.layout.pb, null);
                ll_container.addView(pb);
                pbs.add(pb);
            }
            Toast.makeText(this, "開始下载", 0).show();
            new Thread(){
                public void run() {
                    try {
                        URL url = new URL(path);
                        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                        conn.setRequestMethod("GET");
                        conn.setConnectTimeout(5000);
                        int code = conn.getResponseCode();
                        if (code == 200) {
                            long size = conn.getContentLength();// 得到服务端返回的文件的大小
                            System.out.println("server文件的大小:" + size);
                            blocksize = size / threadCount;
                            // 1.首先在本地创建一个大小跟server一模一样的空白文件。
                            File file = new File(Environment.getExternalStorageDirectory(),getFileName(path));
                            RandomAccessFile raf = new RandomAccessFile(file, "rw");
                            raf.setLength(size);
                            // 2.开启若干个子线程分别去下载相应的资源。

    runningThreadCount = threadCount; for (int i = 1; i <= threadCount; i++) { long startIndex = (i - 1) * blocksize; long endIndex = i * blocksize - 1; if (i == threadCount) { // 最后一个线程 endIndex = size - 1; } System.out.println("开启线程:" + i + "下载的位置:" + startIndex + "~" + endIndex); int threadSize = (int) (endIndex - startIndex); pbs.get(i-1).setMax(threadSize); new DownloadThread(path, i, startIndex, endIndex).start(); } } conn.disconnect(); } catch (Exception e) { e.printStackTrace(); Message msg = Message.obtain(); msg.what = DOWNLOAD_ERROR; handler.sendMessage(msg); } }; }.start(); } private class DownloadThread extends Thread { private int threadId; private long startIndex; private long endIndex; private String path; public DownloadThread(String path, int threadId, long startIndex, long endIndex) { this.path = path; this.threadId = threadId; this.startIndex = startIndex; this.endIndex = endIndex; } @Override public void run() { try { // 当前线程下载的总大小 int total = 0; File positionFile = new File(Environment.getExternalStorageDirectory(),getFileName(path)+threadId + ".txt"); URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url .openConnection(); conn.setRequestMethod("GET"); // 接着从上一次的位置继续下载数据 if (positionFile.exists() && positionFile.length() > 0) {// 推断是否有记录 FileInputStream fis = new FileInputStream(positionFile); BufferedReader br = new BufferedReader( new InputStreamReader(fis)); // 获取当前线程上次下载的总大小是多少 String lasttotalstr = br.readLine(); int lastTotal = Integer.valueOf(lasttotalstr); System.out.println("上次线程" + threadId + "下载的总大小:" + lastTotal); startIndex += lastTotal; total += lastTotal;// 加上上次下载的总大小。 fis.close(); //存数据库。 //_id path threadid total } conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex); conn.setConnectTimeout(5000); int code = conn.getResponseCode(); System.out.println("code=" + code); InputStream is = conn.getInputStream(); File file = new File(Environment.getExternalStorageDirectory(),getFileName(path)); RandomAccessFile raf = new RandomAccessFile(file, "rw"); // 指定文件開始写的位置。

    raf.seek(startIndex); System.out.println("第" + threadId + "个线程:写文件的開始位置:" + String.valueOf(startIndex)); int len = 0; byte[] buffer = new byte[1024]; while ((len = is.read(buffer)) != -1) { RandomAccessFile rf = new RandomAccessFile(positionFile, "rwd"); raf.write(buffer, 0, len); total += len; rf.write(String.valueOf(total).getBytes()); rf.close(); pbs.get(threadId-1).setProgress(total); } is.close(); raf.close(); } catch (Exception e) { e.printStackTrace(); Message msg = Message.obtain(); msg.what = THREAD_ERROR; handler.sendMessage(msg); } finally { // 仅仅有全部的线程都完成下载后 才干够删除记录文件。 synchronized (MainActivity.class) { System.out.println("线程" + threadId + "完成下载了"); runningThreadCount--; if (runningThreadCount < 1) { System.out.println("全部的线程都工作完成了。

    删除暂时记录的文件"); for (int i = 1; i <= threadCount; i++) { File f = new File(Environment.getExternalStorageDirectory(),getFileName(path)+ i + ".txt"); System.out.println(f.delete()); } Message msg = Message.obtain(); msg.what = DWONLOAD_FINISH; handler.sendMessage(msg); } } } } } private String getFileName(String path){ int start = path.lastIndexOf("/")+1; return path.substring(start); } }

    这里写图片描写叙述
    安卓系统下须要加上訪问网络的权限和訪问本地内存卡的权限。例如以下图:
    这里写图片描写叙述
    源代码已经上传到了CSDN: http://download.csdn.net/detail/rootusers/8508137

  • 相关阅读:
    单例模式
    HashSet、LinkedHashSet、SortedSet、TreeSet
    ArrayList、LinkedList、CopyOnWriteArrayList
    HashMap、Hashtable、LinkedHashMap
    andrew ng machine learning week8 非监督学习
    andrew ng machine learning week7 支持向量机
    andrew ng machine learning week6 机器学习算法理论
    andrew ng machine learning week5 神经网络
    andrew ng machine learning week4 神经网络
    vue组件监听属性变化watch方法报[Vue warn]: Method "watch" has type "object" in the component definition. Did you reference the function correctly?
  • 原文地址:https://www.cnblogs.com/yutingliuyl/p/7147139.html
Copyright © 2011-2022 走看看