多线程下载Android

                                            多线程下载

1.新建:MultiDownloader\app\src\main\res\xml\network_security_config.xml

配置支持HTTP请求

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>

    <base-config cleartextTrafficPermitted="true" />

</network-security-config>

 

2. AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.glsite.multidownloader">

    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        android:networkSecurityConfig="@xml/network_security_config">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

 

3.新建MultiDownloader\app\src\main\res\layout\pb.xml

进度条

<?xml version="1.0" encoding="utf-8"?>
<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
    style="@style/Widget.AppCompat.ProgressBar.Horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

</ProgressBar>

 

4.activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <EditText
        android:id="@+id/et_threadcount"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="36dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:ems="10"
        android:hint="请输入下载线程的数量"
        android:inputType="textPersonName"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/et_path"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:ems="10"
        android:inputType="textPersonName"
        android:text="http://192.168.1.130:8080/Day10/QQ.exe"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/et_threadcount" />

    <Button
        android:id="@+id/bt_self"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        android:text="自己的下载方式"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/et_path" />

    <Button
        android:id="@+id/bt_other"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:text="第三方的下载方式"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/et_path" />

    <LinearLayout
        android:id="@+id/ll_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="100dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:orientation="vertical"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.503"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/et_path"></LinearLayout>
</android.support.constraint.ConstraintLayout>

 

5.MultiDownloader\lib\src\main\java\com\glsite\lib\MultiDownloader.java

测试:

package com.glsite.lib;

import com.sun.jndi.toolkit.url.UrlUtil;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Path;

public class MultiDownloader {
    /**
     * 总共的线程数
     */
    public static final int TOTAL_THREAD_COUNT = 3;

    /**
     * 要下载的文件的链接
     */
    public static String path = "http://localhost:8080/Day10/QQ.exe";

    /**
     * 运行状态的线程数
     */
    private static int runningThreadCount = 0;

    public static void main(String[] args) {
        try {
            URL url = new URL(path);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            int code = conn.getResponseCode();
            if (code == 200) {
                int length = conn.getContentLength();
                System.out.println("file length:" + length);
                RandomAccessFile raf = new RandomAccessFile(getDownloadFileName(path), "rw");
                // 创建一个空的文件并且设置它的文件长度等于服务器上的文件长度
                raf.setLength(length);
                raf.close();

                int blockSize = length / TOTAL_THREAD_COUNT;
                System.out.println("every block size:" + blockSize);

                runningThreadCount = TOTAL_THREAD_COUNT;

                for (int threadId = 0; threadId < TOTAL_THREAD_COUNT; threadId++) {
                    int startPosition = threadId * blockSize;
                    int endPosition = (threadId + 1) * blockSize -1;
                    if (threadId == (TOTAL_THREAD_COUNT - 1 )) {
                        endPosition = length - 1;
                    }
                    new DownloadThread(threadId, startPosition, endPosition).start();
                }

            } else {
                System.out.println("download error, code = " + code);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 从网络路径获取文件名
     *
     * @param path
     *          网络路径
     * @return  文件名
     */
    private static String getDownloadFileName(String path) {
        return path.substring(path.lastIndexOf("/") + 1);
    }

    /**
     * 下载文件的线程
     */
    private static class DownloadThread extends Thread{
        /**
         * 线程id
         */
        private int threadId;

        /**
         * 当前现成下载的起始位置
         */
        private int startPosition;

        /**
         * 当前线程下载的终止位置
         */
        private int endPosition;

        public DownloadThread(int threadId, int startPosition, int endPosition) {
            this.threadId = threadId;
            this.startPosition = startPosition;
            this.endPosition = endPosition;
        }

        @Override
        public void run() {

            System.out.println("thread:" + threadId + "begin working");

            try {
                File finfo = new File(TOTAL_THREAD_COUNT + getDownloadFileName(path) + threadId + ".txt");
                if (finfo.exists() && finfo.length() > 0) {
                    FileInputStream fis = new FileInputStream(finfo);
                    BufferedReader br = new BufferedReader(new InputStreamReader(fis));
                    String lastPosition = br.readLine();
                    startPosition = Integer.parseInt(lastPosition);
                    fis.close();
                }

                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                System.out.println("begin and end:" + threadId + " range of download: " + startPosition + "~~" + endPosition);
                conn.setRequestProperty("Range", "bytes=" + startPosition + "-" + endPosition);
                int code = conn.getResponseCode();
                if (code == 206) {
                    InputStream is = conn.getInputStream();
                    RandomAccessFile raf = new RandomAccessFile(getDownloadFileName(path), "rw");

                    raf.seek(startPosition);

                    int len = 0;
                    byte[] buffer = new byte[1024];
                    int total = 0; // downloaded data of current thread in this times;
                    while ((len = is.read(buffer)) != -1) {
                        raf.write(buffer, 0, len);

                        total += len;
                        RandomAccessFile inforaf = new RandomAccessFile(TOTAL_THREAD_COUNT + getDownloadFileName(path) + threadId + ".txt", "rwd");
                        inforaf.write(String.valueOf(startPosition + total).getBytes());
                        inforaf.close();
                    }

                    is.close();
                    raf.close();
                    System.out.println("thread:" + threadId + " download complete...");
                } else {
                    System.out.println("request download failed.");
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                synchronized (MultiDownloader.class) {
                    runningThreadCount--;
                    if (runningThreadCount <= 0) {
                        System.out.println("multi thread download complete.");
                        for (int i = 0; i < TOTAL_THREAD_COUNT; i++) {
                            File finfo = new File(TOTAL_THREAD_COUNT + getDownloadFileName(path) + i + ".txt");
//                            System.out.println(finfo.delete());
                        }
                    }
                }
            }
        }
    }




}

6.MainActivity.java

package com.glsite.multidownloader;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private EditText mEtPath;
    private EditText mEtThreadCount;
    private LinearLayout mLLContainer;
    private Button mBtSelf;
    private Button mBtOther;

    /**
     * 总共的线程数
     */
    public int totalThreadCount = 3;

    /**
     * 要下载的文件的链接
     */
    public String path = "http://192.168.1.130:8080/Day10/QQ.exe";

    /**
     * 运行状态的线程数
     */
    private static int runningThreadCount = 0;

    /**
     * ProgressBar的集合
     */
    private ArrayList<ProgressBar> mPbs;

    /**
     * 当前app的缓存目录
     */
    private String CACHE_DIR;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 1.找到控件
        // 2.在button点击事件当中下载文件
        // 3.下载的时候要在界面显示下载的进度
        mEtPath = findViewById(R.id.et_path);
        mEtThreadCount = findViewById(R.id.et_threadcount);
        mLLContainer = findViewById(R.id.ll_container);
        mBtSelf = findViewById(R.id.bt_self);
        mBtOther = findViewById(R.id.bt_other);

        mBtSelf.setOnClickListener(this);
        mBtOther.setOnClickListener(this);

        // 初始化缓存目录路径
        CACHE_DIR = this.getCacheDir().getAbsolutePath() + "/";
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bt_self:
                downloadBySelf();
                break;
            case R.id.bt_other:

                break;
        }
    }

    /**
     * 通过自己自定义的多线程代码去下载网络文件
     */
    private void downloadBySelf() {
        path =  mEtPath.getText().toString().trim();
        totalThreadCount = Integer.valueOf(mEtThreadCount.getText().toString().trim());
        mLLContainer.removeAllViews();
        mPbs = new ArrayList<>();
        for (int i = 0; i < totalThreadCount; i++) {
            // 有几个线程就添加几个progressbar
            ProgressBar pb = (ProgressBar) View.inflate(this, R.layout.pb, null);
            mLLContainer.addView(pb);
            mPbs.add(pb);
        }

        new Thread() {
            @Override
            public void run() {
                try {
                    URL url = new URL(path);
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    conn.setRequestMethod("GET");
                    int code = conn.getResponseCode();
                    if (code == 200) {
                        int length = conn.getContentLength();
                        System.out.println("file length:" + length);
                        RandomAccessFile raf = new RandomAccessFile(CACHE_DIR + getDownloadFileName(path), "rw");
                        // 创建一个空的文件并且设置它的文件长度等于服务器上的文件长度
                        raf.setLength(length);
                        raf.close();

                        int blockSize = length / totalThreadCount;
                        System.out.println("every block size:" + blockSize);

                        runningThreadCount = totalThreadCount;

                        for (int threadId = 0; threadId < totalThreadCount; threadId++) {
                            int startPosition = threadId * blockSize;
                            int endPosition = (threadId + 1) * blockSize -1;
                            if (threadId == (totalThreadCount - 1 )) {
                                endPosition = length - 1;
                            }
                            new DownloadThread(threadId, startPosition, endPosition).start();
                        }

                    } else {
                        System.out.println("download error, code = " + code);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }.start();

    }

    /**
     * 从网络路径获取文件名
     *
     * @param path
     *          网络路径
     * @return  文件名
     */
    private static String getDownloadFileName(String path) {
        return path.substring(path.lastIndexOf("/") + 1);
    }

    /**
     * 下载文件的线程
     */
    private class DownloadThread extends Thread{
        /**
         * 线程id
         */
        private int threadId;

        /**
         * 当前现成下载的起始位置
         */
        private int startPosition;

        /**
         * 当前线程下载的终止位置
         */
        private int endPosition;

        /**
         * 当前线程需要去下载的总共的字节
         */
        private int threadTotal;

        /**
         * 该线程上一次下载了的字节数
         */
        private int lastDownloadTotalSize;

        public DownloadThread(int threadId, int startPosition, int endPosition) {
            this.threadId = threadId;
            this.startPosition = startPosition;
            this.endPosition = endPosition;
            this.threadTotal = endPosition - startPosition;
            mPbs.get(threadId).setMax(threadTotal);
        }

        @Override
        public void run() {

            System.out.println("thread:" + threadId + "begin working");

            try {

                File finfo = new File(CACHE_DIR + totalThreadCount + getDownloadFileName(path) + threadId + ".txt");
                if (finfo.exists() && finfo.length() > 0) {
                    FileInputStream fis = new FileInputStream(finfo);
                    BufferedReader br = new BufferedReader(new InputStreamReader(fis));
                    String lastPosition = br.readLine();
                    // 这里计算出来的就是表示的上次该线程下载了多少个字节总数
                    lastDownloadTotalSize = Integer.parseInt(lastPosition) - startPosition;
                    startPosition = Integer.parseInt(lastPosition);
                    fis.close();
                }

                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                System.out.println("begin and end:" + threadId + " range of download: " + startPosition + "~~" + endPosition);
                conn.setRequestProperty("Range", "bytes=" + startPosition + "-" + endPosition);
                int code = conn.getResponseCode();
                if (code == 206) {
                    InputStream is = conn.getInputStream();
                    RandomAccessFile raf = new RandomAccessFile(CACHE_DIR + getDownloadFileName(path), "rw");

                    raf.seek(startPosition);

                    int len = 0;
                    byte[] buffer = new byte[1024];
                    int total = 0; // downloaded data of current thread in this times;
                    while ((len = is.read(buffer)) != -1) {
                        raf.write(buffer, 0, len);

                        total += len;
                        RandomAccessFile inforaf = new RandomAccessFile(CACHE_DIR + totalThreadCount + getDownloadFileName(path) + threadId + ".txt", "rwd");
                        inforaf.write(String.valueOf(startPosition + total).getBytes());
                        inforaf.close();
                        mPbs.get(threadId).setProgress(total + lastDownloadTotalSize);
                    }

                    is.close();
                    raf.close();
                    System.out.println("thread:" + threadId + " download complete...");
                } else {
                    System.out.println("request download failed.");
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                synchronized (MainActivity.class) {
                    runningThreadCount--;
                    if (runningThreadCount <= 0) {
                        System.out.println("multi thread download complete.");
                        for (int i = 0; i < totalThreadCount; i++) {
                            File finfo = new File(CACHE_DIR + totalThreadCount + getDownloadFileName(path) + i + ".txt");
                            // System.out.println(finfo.delete());
                        }
                    }
                }
            }
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_无往而不胜_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值