android开发步步为营之108:下载断点续传

来源:互联网 发布:cube escape 知乎 编辑:程序博客网 时间:2024/04/28 05:31

        android开发过程中,下载是必备的功能,下载安装包,或者下载图片,假设用户下载过程中人为中断网络,或者网络不稳定中断下载任务,好的用户体验是从断开的地方继续下载,而不是又从头开始下载,因为比方说用户是拿4g来下载的,你一个游戏安装包100多M,用户下载了90M,突然手机没电了,充好电,又从头下载,那岂不是浪费用户的流量。所以断点续传是非常必要的一个功能。其实断点续传也可以使用多线程来实现的,本篇先不写的这么麻烦了,就单线程去下载一个任务了,如果中断了,下次再点击下载的时候,从断点继续下载。好,开始我们的实验。本实验是下载一个安装包。比如我们下载360手机卫士。给出Demo代码。

        1、activity_resume_download.xml 下载页面

<span style="font-size:18px;"><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="断点续传测试" />    <Button        android:id="@+id/btn_download"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="开始下载" />    <Button        android:id="@+id/btn_cancel"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="取消下载" />    <ProgressBar        android:id="@+id/progressBar"        style="?android:attr/progressBarStyleHorizontal"        android:layout_width="match_parent"        android:layout_height="20dp"        android:layout_gravity="center_horizontal" /></LinearLayout></span>
          

            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 0
原创粉丝点击