Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android開發步步為營之108:下載斷點續傳

android開發步步為營之108:下載斷點續傳

編輯:關於Android編程

android開發過程中,下載是必備的功能,下載安裝包,或者下載圖片,假設用戶下載過程中人為中斷網絡,或者網絡不穩定中斷下載任務,好的用戶體驗是從斷開的地方繼續下載,而不是又從頭開始下載,因為比方說用戶是拿4g來下載的,你一個游戲安裝包100多M,用戶下載了90M,突然手機沒電了,充好電,又從頭下載,那豈不是浪費用戶的流量。所以斷點續傳是非常必要的一個功能。其實斷點續傳也可以使用多線程來實現的,本篇先不寫的這麼麻煩了,就單線程去下載一個任務了,如果中斷了,下次再點擊下載的時候,從斷點繼續下載。好,開始我們的實驗。本實驗是下載一個安裝包。比如我們下載360手機衛士。給出Demo代碼。

1、activity_resume_download.xml 下載頁面



    

 

2、ResumeDownloadActivity.java

package com.figo.study.activity;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.Toast;

import com.figo.study.R;
import com.figo.study.mgr.DownloadMgr;
import com.figo.study.utils.FileUtils;

import java.io.File;

public class ResumeDownloadActivity extends Activity implements View.OnClickListener {
    String tag = "ResumeDownloadActivity";
    ProgressBar mProgressBar;
    String downloadUrl = "http://msoftdl.360.cn/mobilesafe/shouji360/360safe/500192/360MobileSafe.apk";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_resume_download);

        findViewById(R.id.btn_download).setOnClickListener(this);
        findViewById(R.id.btn_cancel).setOnClickListener(this);

        mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
    }

    private final Handler msgHandler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 0:
                    Toast.makeText(getApplicationContext(), msg.getData().get("msg").toString(), Toast.LENGTH_SHORT).show();
                    break;
                default:
                    break;
            }
        }
    };

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_download:
                String directory = FileUtils.getStorageDirectory();
                String fileName = directory + File.separator + getFileName(downloadUrl);
                DownloadMgr.getInstance().addTask(downloadUrl, fileName, new DownloadMgr.Callback() {
                    @Override
                    public void onProgress(long progress, long total) {
                        super.onProgress(progress, total);
                        mProgressBar.setProgress((int) (100 * progress / total));
                    }

                    @Override
                    public void onStart() {
                        super.onStart();
                        sendMsg("start");

                    }

                    @Override
                    public void onSuccess() {
                        super.onSuccess();
                        Log.i(tag, "success");
                        sendMsg("success");

                    }

                    @Override
                    public void onFailed(boolean cancelled, String msg) {
                        super.onFailed(cancelled, msg);
                        Log.e(tag, msg);
                        //Looper.getMainLooper().prepare();//這麼干雖然可以在子線程,彈出toast,但是子線程執行到這裡,後面的代碼將不再執行
//                        Toast.makeText(ResumeDownloadActivity.this, "download start", Toast.LENGTH_SHORT).show();
                        //Looper.getMainLooper().loop();
                        //進程間通信還是用Handler比較靠譜
                        sendMsg(msg);
                    }
                });

                break;
            case R.id.btn_cancel:
                DownloadMgr.getInstance().cancelTask(downloadUrl);
                break;
        }
    }

    private String getFileName(String downloadUrl) {
        return downloadUrl.substring(downloadUrl.lastIndexOf("/"));
    }

    private void sendMsg(String msg) {
        Message msgNew = new Message();
        msgNew.what = 0;
        Bundle bundle = new Bundle();
        bundle.putString("msg", msg);
        msgNew.setData(bundle);
        msgHandler.sendMessage(msgNew);
    }
}

3、下載工具類DownloadMgr.java

package com.figo.study.mgr;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;

import com.figo.study.activity.MyApplication;
import com.figo.study.utils.CommonUtil;
import com.figo.study.utils.IOUtil;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.HashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * Created by figo on 16/7/25.
 */
public class DownloadMgr {
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    Executor mExecutor = Executors.newFixedThreadPool(MAXIMUM_POOL_SIZE);
    static DownloadMgr mDownloadMgr;
    static Object obj = new Object();
    HashMap<String, DownloadTask> mTasks = new HashMap<String, DownloadTask>();

    public static void init() {
        getInstance();
    }

    public static DownloadMgr getInstance() {
        synchronized (obj) {
            if (mDownloadMgr == null) {
                mDownloadMgr = new DownloadMgr();
            }
        }
        return mDownloadMgr;
    }

    public void addTask(String downloadUrl, String filePath, Callback callback) {
        if (!mTasks.containsKey(downloadUrl)) {
            mTasks.put(downloadUrl, new DownloadTask(downloadUrl, filePath, callback));
        }
        mTasks.get(downloadUrl).startDownload();
    }

    public void removeTask(String downloadUrl, String filePath, Callback callback) {
        if (mTasks.containsKey(downloadUrl)) {
            mTasks.get(downloadUrl).cancel();
        }
        mTasks.remove(downloadUrl);
    }

    public class DownloadTask implements Runnable {
        private String downloadUrl;
        private String filePath;
        Callback callback;

        public DownloadTask(String downloadUrl, String filePath, Callback callback) {
            this.downloadUrl = downloadUrl;
            this.filePath = filePath;
            this.callback = callback;
        }

        public void startDownload() {
            mExecutor.execute(this);
        }


        @Override
        public void run() {
            runResumable(downloadUrl, filePath, callback);
        }

        synchronized boolean cancel() {
            if (thread == null)
                return false;

            thread.interrupt();
            return true;
        }
    }

    Thread thread;

    public void runResumable(String downloadUrl, String filePath, Callback callback) {
        Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
        thread = Thread.currentThread();
        final Context ctx = MyApplication.getInstance();
        String msg = "";
        boolean interrupted = false;

        HttpURLConnection conn = null;
        long resumePosition = 0;
        final File file = new File(filePath);

        try {
            //20160720 add
            final File parent = file.getParentFile();
            if (!parent.exists()) {
                parent.mkdirs();
            }
            if (!file.exists()) {
                file.createNewFile();
            }
            callback.onStart();

//簡單一點就用md5來校驗
//            if (file.exists() && StringUtil.equalsIgnoreCase(md5, Md5.md5(file))) {
//                suc = true;
//                return;
//            }
            //非wifi環境不下載
            if (!isWifiActive(ctx)) {
                msg = "請在wifi環境下下載";
                callback.onFailed(true, msg);
                return;
            }

            resumePosition = file.exists() ? file.length() : 0;
            // Create connection object
            conn = (HttpURLConnection) new URL(downloadUrl).openConnection();
            conn.setConnectTimeout(60000);
            conn.setReadTimeout(60000);

            conn.setDoInput(true);
            conn.setUseCaches(false);

            // Make the request
            conn.setRequestMethod("GET");
            conn.setRequestProperty("User-Agent", "Java/Android");
            conn.setRequestProperty("Connection", "close");
            conn.setRequestProperty("Http-version", "HTTP/1.1");
            conn.setRequestProperty("Cache-Control", "no-transform");
            if (resumePosition > 0) {
                //斷點續傳的關鍵設置Range
                conn.setRequestProperty("Range", "bytes=" + resumePosition + "-");
            }

            conn.connect();

            final int responseCode = conn.getResponseCode();
            if (responseCode == 416) {
                msg = "已經下載!";
                callback.onFailed(true, msg);
                return;
            }
            if (responseCode != 200 && responseCode != 206) {
                msg = "網絡繁忙,請稍後再試!";
                callback.onFailed(true, msg);

                return;
            }

            long fileLength = conn.getContentLength();
            InputStream is = new BufferedInputStream(conn.getInputStream());
            FileOutputStream fos = new FileOutputStream(file, resumePosition > 0);
            try {
                int read = 0;
                long progress = resumePosition;
                byte[] buffer = new byte[4096 * 2];
                while ((read = is.read(buffer)) > 0 && !(interrupted = Thread.interrupted())) {
                    try {
                        fos.write(buffer, 0, read);
                    } catch (Exception e) {
                        msg = "磁盤空間已滿,無法下載";
                        throw e;
                    }

                    // progress
                    progress += read;
                    callback.onProgress(progress, fileLength);

                }
            } finally {
                IOUtil.closeQuietly(fos);
                IOUtil.closeQuietly(is);
            }
            //20160720 resumable download
            if (file.exists()) {
                //也可以通過md5來校驗
//                if (StringUtil.equalsIgnoreCase(md5, Md5.md5(file))) {
//                    suc = true;
//                    return;
//                }
                //檢驗數據是否完整
                if (file.length() == fileLength + resumePosition) {
                    callback.onSuccess();
                    return;
                }
            }

        } catch (Exception e) {
            interrupted = interrupted || Thread.interrupted() || (e instanceof InterruptedIOException && !(e instanceof SocketTimeoutException));
            msg = "網絡異常,下載失敗";

            if (interrupted) {
                msg = "下載被中斷!";
            }
            callback.onFailed(true, msg);

        } finally {
            disconnect(conn);
        }
    }

    static void disconnect(HttpURLConnection conn) {
        try {
            if (conn == null)
                return;

            conn.disconnect();
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }


    public static abstract class Callback {


        public void onStart() {
        }

        public void onProgress(long progress, long total) {
        }

        public void onSuccess() {
        }

        public void onFailed(boolean cancelled, String msg) {
        }
    }

    public boolean isWifiActive(Context ctx) {
        try {
            ConnectivityManager mgr = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo info = mgr.getActiveNetworkInfo();
            return (info != null) ? info.getType() == ConnectivityManager.TYPE_WIFI : false;
        } catch (Exception e) {
            return false;
        }
    }

    public void cancelAllTask() {
        try {
            if (mTasks != null) {
                for (String taskKey : mTasks.keySet()) {
                    mTasks.get(taskKey).cancel();
                }
            }
        } catch (Exception e) {
            CommonUtil.printStackTrace(e);
        }

    }

    public void cancelTask(String key) {
        try {
            if (mTasks != null) {
                mTasks.get(key).cancel();
            }
        } catch (Exception e) {
            CommonUtil.printStackTrace(e);
        }
    }

    public static boolean checkNetAvailable(Context ctx) {
        try {
            ConnectivityManager mgr = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo info = mgr.getActiveNetworkInfo();
            return (info != null) ? true : false;
        } catch (Exception e) {
            return true;
        }
    }
}
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved