多线程下载视频到SD卡+断点续传+播放视频

权限:

    <uses-permission android:name="android.permission.INTERNET"/>  
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>  
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>  
布局:

    <?xml version="1.0" encoding="utf-8"?>  
    <LinearLayout  
        xmlns:android="http://schemas.android.com/apk/res/android"  
        xmlns:app="http://schemas.android.com/apk/res-auto"  
        android:orientation="vertical"  
        xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"  
        android:layout_height="match_parent" tools:context="com.example.xiazai.MainActivity">  
        <Button  
            android:text="下载"  
            android:onClick="xiazai"  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content" />  
        <Button  
            android:text="暂停"  
            android:onClick="zanting"  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content" />  
        <Button  
            android:text="继续"  
            android:onClick="jixu"  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content" />  
        <Button  
            android:text="播放"  
            android:onClick="quxiao"  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content" />  
        <TextView  
            android:id="@+id/tv_progress"  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:text="Hello World!" />  
        <ProgressBar  
            android:id="@+id/pro"  
            android:layout_width="match_parent"  
            android:layout_height="wrap_content"  
            style="?android:attr/progressBarStyleHorizontal"  
            />  
        <VideoView  
            android:id="@+id/vv"  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content" />  
      
    </LinearLayout>  
创建类:

public class DownLoadFile {  
    private static final String SP_NAME = "download_file";  
    private static final String CURR_LENGTH = "curr_length";  
    private static final int DEFAULT_THREAD_COUNT = 4;//默认下载线程数  
    //以下为线程状态  
    private static final String DOWNLOAD_INIT = "1";  
    private static final String DOWNLOAD_ING = "2";  
    private static final String DOWNLOAD_PAUSE = "3";  
  
    private Context mContext;  
  
    private String loadUrl;//网络获取的url  
    private String filePath;//下载到本地的path  
    private int threadCount = DEFAULT_THREAD_COUNT;//下载线程数  
  
    private int fileLength;//文件总大小  
    //使用volatile防止多线程不安全  
    private volatile int currLength;//当前总共下载的大小  
    private volatile int runningThreadCount;//正在运行的线程数  
    private Thread[] mThreads;  
    private String stateDownload = DOWNLOAD_INIT;//当前线程状态  
  
    private DownLoadListener mDownLoadListener;  
  
    public void setOnDownLoadListener(DownLoadListener mDownLoadListener) {  
        this.mDownLoadListener = mDownLoadListener;  
    }  
  
    interface DownLoadListener {  
        //返回当前下载进度的百分比  
        void getProgress(int progress);  
  
        void onComplete();  
  
        void onFailure();  
    }  
  
    public DownLoadFile(Context mContext, String loadUrl, String filePath) {  
        this(mContext, loadUrl, filePath, DEFAULT_THREAD_COUNT, null);  
    }  
  
    public DownLoadFile(Context mContext, String loadUrl, String filePath, DownLoadListener mDownLoadListener) {  
        this(mContext, loadUrl, filePath, DEFAULT_THREAD_COUNT, mDownLoadListener);  
    }  
  
    public DownLoadFile(Context mContext, String loadUrl, String filePath, int threadCount) {  
        this(mContext, loadUrl, filePath, threadCount, null);  
    }  
  
    public DownLoadFile(Context mContext, String loadUrl, String filePath, int threadCount, DownLoadListener mDownLoadListener) {  
        this.mContext = mContext;  
        this.loadUrl = loadUrl;  
        this.filePath = filePath;  
        this.threadCount = threadCount;  
        runningThreadCount = 0;  
        this.mDownLoadListener = mDownLoadListener;  
    }  
  
    /** 
     * 开始下载 
     */  
    protected void downLoad() {  
        //在线程中运行,防止anr  
        new Thread(new Runnable() {  
            @Override  
            public void run() {  
                try {  
                    //初始化数据  
                    if (mThreads == null)  
                        mThreads = new Thread[threadCount];  
  
                    //建立连接请求  
                    URL url = new URL(loadUrl);  
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
                    conn.setConnectTimeout(5000);  
                    conn.setRequestMethod("GET");  
                    int code = conn.getResponseCode();//获取返回码  
                    if (code == 200) {//请求成功,根据文件大小开始分多线程下载  
                        fileLength = conn.getContentLength();  
                        //根据文件大小,先创建一个空文件  
                        //“r“——以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。  
                        //“rw“——打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。  
                        //“rws“—— 打开以便读取和写入,对于 “rw”,还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。  
                        //“rwd“——打开以便读取和写入,对于 “rw”,还要求对文件内容的每个更新都同步写入到底层存储设备。  
                        RandomAccessFile raf = new RandomAccessFile(filePath, "rwd");  
                        raf.setLength(fileLength);  
                        raf.close();  
                        //计算各个线程下载的数据段  
                        int blockLength = fileLength / threadCount;  
  
                        SharedPreferences sp = mContext.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);  
                        //获取上次取消下载的进度,若没有则返回0  
                        currLength = sp.getInt(CURR_LENGTH, 0);  
                        for (int i = 0; i < threadCount; i++) {  
                            //开始位置,获取上次取消下载的进度,默认返回i*blockLength,即第i个线程开始下载的位置  
                            int startPosition = sp.getInt(SP_NAME + (i + 1), i * blockLength);  
                            //结束位置,-1是为了防止上一个线程和下一个线程重复下载衔接处数据  
                            int endPosition = (i + 1) * blockLength - 1;  
                            //将最后一个线程结束位置扩大,防止文件下载不完全,大了不影响,小了文件失效  
                            if ((i + 1) == threadCount)  
                                endPosition = endPosition * 2;  
  
                            mThreads[i] = new DownThread(i + 1, startPosition, endPosition);  
                            mThreads[i].start();  
                        }  
                    }else {  
                        handler.sendEmptyMessage(FAILURE);  
                    }  
                } catch (Exception e) {  
                    e.printStackTrace();  
                    handler.sendEmptyMessage(FAILURE);  
                }  
            }  
        }).start();  
    }  
  
    /** 
     * 取消下载 
     */  
    protected void cancel() {  
        if (mThreads != null) {  
            //若线程处于等待状态,则while循环处于阻塞状态,无法跳出循环,必须先唤醒线程,才能执行取消任务  
            if (stateDownload.equals(DOWNLOAD_PAUSE))  
                onStart();  
            for (Thread dt : mThreads) {  
                ((DownThread) dt).cancel();  
            }  
        }  
    }  
  
    /** 
     * 暂停下载 
     */  
    protected void onPause() {  
        if (mThreads != null)  
            stateDownload = DOWNLOAD_PAUSE;  
    }  
  
    /** 
     * 继续下载 
     */  
    protected void onStart() {  
        if (mThreads != null)  
            synchronized (DOWNLOAD_PAUSE) {  
                stateDownload = DOWNLOAD_ING;  
                DOWNLOAD_PAUSE.notifyAll();  
            }  
    }  
  
    protected void onDestroy() {  
        if (mThreads != null)  
            mThreads = null;  
    }  
  
    private class DownThread extends Thread {  
  
        private boolean isGoOn = true;//是否继续下载  
        private int threadId;  
        private int startPosition;//开始下载点  
        private int endPosition;//结束下载点  
        private int currPosition;//当前线程的下载进度  
  
        private DownThread(int threadId, int startPosition, int endPosition) {  
            this.threadId = threadId;  
            this.startPosition = startPosition;  
            currPosition = startPosition;  
            this.endPosition = endPosition;  
            runningThreadCount++;  
        }  
  
        @Override  
        public void run() {  
            SharedPreferences sp = mContext.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);  
            try {  
                URL url = new URL(loadUrl);  
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
                conn.setRequestMethod("GET");  
                conn.setRequestProperty("Range", "bytes=" + startPosition + "-" + endPosition);  
                conn.setConnectTimeout(5000);  
                //若请求头加上Range这个参数,则返回状态码为206,而不是200  
                if (conn.getResponseCode() == 206) {  
                    InputStream is = conn.getInputStream();  
                    RandomAccessFile raf = new RandomAccessFile(filePath, "rwd");  
                    raf.seek(startPosition);//跳到指定位置开始写数据  
                    int len;  
                    byte[] buffer = new byte[1024];  
                    while ((len = is.read(buffer)) != -1) {  
                        //是否继续下载  
                        if (!isGoOn)  
                            break;  
                        //回调当前进度  
                        if (mDownLoadListener != null) {  
                            currLength += len;  
                            int progress = (int) ((float) currLength / (float) fileLength * 100);  
                            handler.sendEmptyMessage(progress);  
                        }  
  
                        raf.write(buffer, 0, len);  
                        //写完后将当前指针后移,为取消下载时保存当前进度做准备  
                        currPosition += len;  
                        synchronized (DOWNLOAD_PAUSE) {  
                            if (stateDownload.equals(DOWNLOAD_PAUSE)) {  
                                DOWNLOAD_PAUSE.wait();  
                            }  
                        }  
                    }  
                    is.close();  
                    raf.close();  
                    //线程计数器-1  
                    runningThreadCount--;  
                    //若取消下载,则直接返回  
                    if (!isGoOn) {  
                        //此处采用SharedPreferences保存每个线程的当前进度,和三个线程的总下载进度  
                        if (currPosition < endPosition) {  
                            sp.edit().putInt(SP_NAME + threadId, currPosition).apply();  
                            sp.edit().putInt(CURR_LENGTH, currLength).apply();  
                        }  
                        return;  
                    }  
                    if (runningThreadCount == 0) {  
                        sp.edit().clear().apply();  
                        handler.sendEmptyMessage(SUCCESS);  
                        handler.sendEmptyMessage(100);  
                        mThreads = null;  
                    }  
                } else {  
                    sp.edit().clear().apply();  
                    handler.sendEmptyMessage(FAILURE);  
                }  
            } catch (Exception e) {  
                sp.edit().clear().apply();  
                e.printStackTrace();  
                handler.sendEmptyMessage(FAILURE);  
            }  
        }  
  
        public void cancel() {  
            isGoOn = false;  
        }  
    }  
  
    private final int SUCCESS = 0x00000101;  
    private final int FAILURE = 0x00000102;  
  
    private Handler handler = new Handler() {  
        @Override  
        public void handleMessage(Message msg) {  
  
            if (mDownLoadListener != null) {  
                if (msg.what == SUCCESS) {  
                    mDownLoadListener.onComplete();  
                } else if (msg.what == FAILURE) {  
  
                    mDownLoadListener.onFailure();  
                } else {  
                    mDownLoadListener.getProgress(msg.what);  
                }  
            }  
        }  
    };  
}  

MainActivity:

public class MainActivity extends AppCompatActivity {  
    private ProgressBar pro;  
    private TextView tv;  
    private DownLoadFile downLoadFile ;  
    private VideoView vv;  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        initView();  
    }  
    private void initView() {  
  
        pro = (ProgressBar) findViewById(R.id.pro);  
        tv = (TextView) findViewById(R.id.tv_progress);  
        vv= (VideoView) findViewById(R.id.vv);  
        File f = new File(Environment.getExternalStorageDirectory()+"/wenjian/");  
        if(!f.exists()){  
            f.mkdir();  
        }  
        //存储地址  
        String path =  Environment.getExternalStorageDirectory()+"/wenjian/mm.mp4";  
        //设置最大度  
        pro.setMax(100);  
        //实例化  
        downLoadFile = new DownLoadFile(this,  
                "http://mirror.aarnet.edu.au/pub/TED-talks/911Mothers_2010W-480p.mp4"  
                , path,10, new DownLoadFile.DownLoadListener() {  
            @Override  
            public void getProgress(int progress) {  
                tv.setText("当前进度:" + progress + "%");  
                pro.setProgress(progress);  
            }  
  
            @Override  
            public void onComplete() {  
                Toast.makeText(MainActivity.this, "下载完成", Toast.LENGTH_SHORT).show();  
            }  
  
            @Override  
            public void onFailure() {  
                Toast.makeText(MainActivity.this, "下载失败", Toast.LENGTH_SHORT).show();  
            }  
        }  
        );  
    }  
    public void xiazai(View view){  
        downLoadFile.downLoad();  
    }  
    public void zanting(View view){  
        downLoadFile.onPause();  
    }  
    public void jixu(View view){  
        downLoadFile.onStart();  
    }  
    public void quxiao(View view){  
        String path =  Environment.getExternalStorageDirectory()+"/wenjian/mm.mp4";  
      vv.setVideoPath(path);  
        vv.start();  
    }  
  
    @Override  
    protected void onDestroy() {  
        super.onDestroy();  
        downLoadFile.onDestroy();  
    }  
}  



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值