zoukankan      html  css  js  c++  java
  • 网络资源下载时断点续传的实现

    断点续传用到的知识点:
    1.使用RandomAccessFile设定文件大小并于指定位置开始读数据[randomAccessFile.seek(position)]。    
    2.请求资源链接时指定所请求数据的返回范围。
        httpURLConnection.setRequestProperty("Range", "bytes=" + start + "-" + (contentLength - 1));

    效果图如下[CSDN]:

    下载gif

    (相当抱歉,这个动画的时间太长了)

    以下代码中的NetworkTool为通过个人编程经验封装好的网络工具类,强力推荐,当然也欢迎拍砖。
    使用NetworkTool访问一个网络链接并获取数据的小示例为:

    HttpURLConnection httpConn = NetworkTool.openUrl(context, url);
    int respondCode = NetworkTool.connect(httpConn);
    if (respondCode == HttpURLConnection.HTTP_OK) {
    byte[] data = NetworkTool.fetchData_doClose(httpConn);
    String content = new String(data);
    data = null;
    // parse content
    } else {
    // handles something
    }
    NetworkTool.disconnect(httpConn);

    代码中的DontPressWithParentButton可用于ListView中,当点击该Button时不会触发ListView中的OnItemClickListener。实现方法为重写Button的setPressed(boolean)方法

    @Override
    public void setPressed(boolean pressed) {
    if (pressed && ((View) getParent()).isPressed()) {
    return;
    }
    super.setPressed(pressed);
    }

    代码如下:
    lab.sodino.downloadbreak.ActDownload.java

    package lab.sodino.downloadbreak;
    import java.io.File;
    import java.text.DecimalFormat;
    import lab.sodino.downloadbreak.bean.BeanDownload;
    import lab.sodino.downloadbreak.util.LogOut;
    import lab.sodino.downloadbreak.util.NetworkTool;
    import android.app.Activity;
    import android.content.Intent;
    import android.net.Uri;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;
    import android.view.View;
    import android.widget.Button;
    import android.widget.ProgressBar;
    import android.widget.TextView;
    public class ActDownload extends Activity {
    /** 下载存放地:"/sdcard/sodino/"。 */
    public static final String RES_LOAD_FOLDER = File.separator + "sdcard" + File.separator
    + "sodino" + File.separator;
    /** 刷新进度。 */
    public static final int REFRESH = 1;
    public static final int CODE = 10;
    private BeanDownload bean;
    private TextView txtName;
    private TextView txtProgress;
    private TextView txtSize;
    private ProgressBar progressBar;
    private Button btnAction;
    private Handler handler;
    private BtnListener btnListener;
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.l_download);
    initBeanDownload();
    initViews$Handler();
    }
    private void initBeanDownload() {
    bean = new BeanDownload();
    bean.name = "微信.apk";
    // 请找个可以无需跳转直接下载的地址
    bean.url = "http://XXOO.com/weixin20android16.apk";
    bean.state = BeanDownload.STATE_INTERRUPTED;
    bean.size = bean.loadedSize = 0l;
    bean.enable = true;
    }
    private void initViews$Handler() {
    txtName = (TextView) findViewById(R.id.txtName);
    txtName.setText(bean.name);
    txtProgress = (TextView) findViewById(R.id.txtProgress);
    txtProgress.setText(getProgressTxt(bean));
    txtSize = (TextView) findViewById(R.id.txtSize);
    txtSize.setText(formatSizeTxt(bean.size));
    progressBar = (ProgressBar) findViewById(R.id.progressBar);
    progressBar.setProgress(getProgressInt(bean, progressBar.getMax()));
    btnListener = new BtnListener();
    btnAction = (Button) findViewById(R.id.btnAction);
    btnAction.setOnClickListener(btnListener);
    btnAction.setText(getTxt(bean));
    btnAction.setEnabled(isEnable(bean));
    // handler
    handler = new Handler() {
    public void handleMessage(Message msg) {
    txtProgress.setText(getProgressTxt(bean));
    txtSize.setText(formatSizeTxt(bean.size));
    progressBar.setProgress(getProgressInt(bean, progressBar.getMax()));
    btnAction.setText(getTxt(bean));
    btnAction.setEnabled(isEnable(bean));
    }
    };
    }
    private void pauseDownload() {
    bean.enable = false;
    handler.sendEmptyMessage(REFRESH);
    }
    private void doDownload() {
    handler.sendEmptyMessage(REFRESH);
    new DownloadThread().start();
    }
    private void reloadDownload() {
    bean.size = bean.loadedSize = 0;
    bean.enable = true;
    doDownload();
    }
    private void installDownload() {
    Intent intent = new Intent(Intent.ACTION_VIEW);
    String filePath = RES_LOAD_FOLDER + bean.name;
    intent.setDataAndType(Uri.parse("file://" + filePath),
    "application/vnd.android.package-archive");
    // 如果仅是简单的startActivity(intent),会造成onCreate()再执行一次。
    ActDownload.this.startActivityForResult(intent, CODE);
    }
    class BtnListener implements Button.OnClickListener {
    public void onClick(View v) {
    LogOut.out(this, "state:" + bean.state);
    switch (bean.state) {
    case BeanDownload.STATE_LOADING:
    // 点击了"暂停"
    pauseDownload();
    break;
    case BeanDownload.STATE_INTERRUPTED:
    // 点击了"继续"
    doDownload();
    break;
    case BeanDownload.STATE_DOWNLOAD_FAIL:
    // 点击了"重载"
    reloadDownload();
    break;
    case BeanDownload.STATE_COMPLETED:
    // 点击了"安装"
    installDownload();
    break;
    }
    }
    }
    class DownloadThread extends Thread {
    public void run() {
    bean.state = BeanDownload.STATE_LOADING;
    bean.enable = true;
    NetworkTool.download2File(ActDownload.this, bean, handler);
    LogOut.out(this, "size:" + bean.size + " loaded:" + bean.loadedSize + " enable:"
    + bean.enable);
    // 测试“重载”请释放下面代码的注释然后等待下载正常结束
    // bean.loadedSize = 0;
    if (bean.size > 0 && bean.loadedSize == bean.size) {
    String localPath = RES_LOAD_FOLDER + bean.name;
    File tmpFile = new File(localPath + ".tmp");
    tmpFile.renameTo(new File(localPath));
    bean.enable = false;
    bean.state = BeanDownload.STATE_COMPLETED;
    } else {
    if (bean.enable == false) {
    bean.state = BeanDownload.STATE_INTERRUPTED;
    } else {
    bean.state = BeanDownload.STATE_DOWNLOAD_FAIL;
    }
    }
    LogOut.out(this, "state=" + bean.state);
    handler.sendEmptyMessage(REFRESH);
    }
    }
    public static String getProgressTxt(BeanDownload bean) {
    String resStr = "0%";
    if (bean.size != 0) {
    double result = bean.loadedSize * 1.0 / bean.size;
    DecimalFormat decFormat = new DecimalFormat("#.#%");
    resStr = decFormat.format(result);
    }
    return resStr;
    }
    private String formatSizeTxt(long size) {
    String sizeTxt = "未知";
    if (size > 0) {
    size = size >> 10;
    sizeTxt = String.valueOf(size) + "k";
    }
    return sizeTxt;
    }
    public static int getProgressInt(BeanDownload bean, int max) {
    int result = (bean.size > 0) ? (int) (bean.loadedSize * max * 1.0 / bean.size) : 0;
    return result;
    }
    private String getTxt(BeanDownload bean) {
    String txt = "安装";
    switch (bean.state) {
    case BeanDownload.STATE_COMPLETED:
    txt = "安装";
    break;
    case BeanDownload.STATE_LOADING:
    txt = "暂停";
    break;
    case BeanDownload.STATE_INTERRUPTED:
    txt = "继续";
    break;
    case BeanDownload.STATE_DOWNLOAD_FAIL:
    txt = "重载";
    break;
    }
    return txt;
    }
    private boolean isEnable(BeanDownload bean) {
    boolean enable = true;
    if (bean.enable == false && bean.state == BeanDownload.STATE_LOADING) {
    enable = false;
    }
    return enable;
    }
    }

    lab.sodino.downloadbreak.bean.BeanDownload.java

    package lab.sodino.downloadbreak.bean;
    /**
    * @author Sodino E-mail:sodinoopen@hotmail.com
    * @version Time:2011-6-8 下午11:33:10
    */
    public class BeanDownload {
    /** 正在下载数据。Button应显示“暂停”。 */
    public static final int STATE_LOADING = 0;
    /** 数据全部下载完成。Button应显示“安装”。 */
    public static final int STATE_COMPLETED = 1;
    /** 数据下载过程中被暂停。Button应显示“继续”。 */
    public static final int STATE_INTERRUPTED = 2;
    /** 下载安装包失败。Button应显示“失败”。 */
    public static final int STATE_DOWNLOAD_FAIL = 3;
    public String name;
    public long size;
    public long loadedSize;
    public String url;
    public int state;
    public boolean enable;
    }

    lab.sodino.downloadbreak.ui.DontPressWithParentButton.java

    package lab.sodino.downloadbreak.ui;
    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.View;
    import android.widget.Button;
    /**
    * @author Sodino E-mail:sodinoopen@hotmail.com
    * @version Time:2011-6-5 下午08:37:27
    */
    public class DontPressWithParentButton extends Button {
    public DontPressWithParentButton(Context context, AttributeSet attrs) {
    super(context, attrs);
    }
    @Override
    public void setPressed(boolean pressed) {
    if (pressed && ((View) getParent()).isPressed()) {
    return;
    }
    super.setPressed(pressed);
    }
    }

    lab.sodino.downloadbreak.util.NetworkTool.java

    package lab.sodino.downloadbreak.util;
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.RandomAccessFile;
    import java.net.HttpURLConnection;
    import java.net.InetSocketAddress;
    import java.net.MalformedURLException;
    import java.net.URL;
    import lab.sodino.downloadbreak.ActDownload;
    import lab.sodino.downloadbreak.bean.BeanDownload;
    import android.content.Context;
    import android.net.ConnectivityManager;
    import android.net.NetworkInfo;
    import android.os.Handler;
    /**
    * 管理联网操作,包括管理url参数、下载APK包、获取任务字符串。<br/>
    *
    * @author Sodino E-mail:sodinoopen@hotmail.com
    * @version Time:2011-4-6 下午03:42:50
    */
    public class NetworkTool {
    /**
    * 开启一个HTTP链接。
    */
    public static HttpURLConnection openUrl(Context context, String urlStr) {
    LogOut.out("Network", "urlStr[" + urlStr + "]");
    URL urlURL = null;
    HttpURLConnection httpConn = null;
    try {
    urlURL = new URL(urlStr);
    // 需要android.permission.ACCESS_NETWORK_STATE
    // 在没有网络的情况下,返回值为null。
    NetworkInfo networkInfo = ((ConnectivityManager) context
    .getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
    // 如果是使用的运营商网络
    if (networkInfo != null) {
    if (networkInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
    // 获取默认代理主机ip
    String host = android.net.Proxy.getDefaultHost();
    // 获取端口
    int port = android.net.Proxy.getDefaultPort();
    if (host != null && port != -1) {
    // 封装代理連接主机IP与端口号。
    InetSocketAddress inetAddress = new InetSocketAddress(host, port);
    // 根据URL链接获取代理类型,本链接适用于TYPE.HTTP
    java.net.Proxy.Type proxyType = java.net.Proxy.Type.valueOf(urlURL
    .getProtocol().toUpperCase());
    java.net.Proxy javaProxy = new java.net.Proxy(proxyType, inetAddress);
    httpConn = (HttpURLConnection) urlURL.openConnection(javaProxy);
    } else {
    httpConn = (HttpURLConnection) urlURL.openConnection();
    }
    } else {
    httpConn = (HttpURLConnection) urlURL.openConnection();
    }
    httpConn.setDoInput(true);
    } else {
    // LogOut.out(this, "No Avaiable Network");
    }
    } catch (NullPointerException npe) {
    npe.printStackTrace();
    } catch (MalformedURLException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    }
    return httpConn;
    }
    /** 启动链接并将RespondCode值返回。 */
    public static int connect(HttpURLConnection httpConn) {
    int code = -1;
    if (httpConn != null) {
    try {
    httpConn.connect();
    code = httpConn.getResponseCode();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    LogOut.out("NetworkTool", "respond_code=" + code);
    return code;
    }
    /**
    * 将指定的HTTP链接内容存储到指定的的文件中。<br/>
    * 返回值仅当参考。<br/>
    *
    * @param httpConn
    * @param filePath
    * 指定存储的文件路径。
    */
    public static boolean download2File(HttpURLConnection httpConn, String filePath) {
    boolean result = true;
    File file = new File(filePath);
    FileOutputStream fos = null;
    byte[] data = new byte[1024];
    int readLength = -1;
    InputStream is = null;
    try {
    fos = new FileOutputStream(file);
    is = httpConn.getInputStream();
    while ((readLength = is.read(data)) != -1) {
    fos.write(data, 0, readLength);
    fos.flush();
    }
    fos.flush();
    } catch (IOException ie) {
    result = false;
    ie.printStackTrace();
    } finally {
    try {
    if (is != null) {
    is.close();
    }
    if (fos != null) {
    fos.close();
    }
    } catch (IOException ie) {
    ie.printStackTrace();
    }
    }
    return result;
    }
    /**
    * 将bean资源下载。<br/>
    * 支持断点续传。
    *
    */
    public static void download2File(Context context, BeanDownload bean, Handler handler) {
    String filePath = ActDownload.RES_LOAD_FOLDER + bean.name + ".tmp";
    HttpURLConnection httpConn = null;
    File file = new File(filePath);
    RandomAccessFile randomFile = null;
    FileOutputStream fos = null;
    int dataBlockLength = 2048;
    byte[] data = new byte[dataBlockLength];
    int readLength = -1;
    InputStream is = null;
    try {
    if (bean.size <= 0) {
    bean.loadedSize = 0;
    if (file.getParentFile().exists() == false) {
    file.getParentFile().mkdirs();
    }
    if (file.exists() == false) {
    file.createNewFile();
    }
    // 采用普通的下载方式
    fos = new FileOutputStream(file);
    httpConn = openUrl(context, bean.url);
    int respondCode = connect(httpConn);
    LogOut.out("NetworkTool", "respondCode=" + respondCode);
    if (respondCode == HttpURLConnection.HTTP_OK) {
    bean.size = httpConn.getContentLength();
    is = httpConn.getInputStream();
    while ((readLength = is.read(data)) != -1 && bean.enable) {
    fos.write(data, 0, readLength);
    bean.loadedSize += readLength;
    handler.sendEmptyMessage(ActDownload.REFRESH);
    }
    }
    } else {
    // 采用断点续传方式
    randomFile = new RandomAccessFile(file, "rw");
    randomFile.setLength(bean.size);
    httpConn = openUrl(context, bean.url);
    httpConn.setRequestProperty("Range", "bytes=" + bean.loadedSize + "-"
    + (bean.size - 1));
    int respondCode = connect(httpConn);
    if (respondCode == HttpURLConnection.HTTP_PARTIAL) {
    is = httpConn.getInputStream();
    while ((readLength = is.read(data)) != -1 && bean.enable) {
    randomFile.seek(bean.loadedSize);
    randomFile.write(data, 0, readLength);
    bean.loadedSize += readLength;
    handler.sendEmptyMessage(ActDownload.REFRESH);
    }
    }
    }
    } catch (Exception e) {
    e.printStackTrace();
    } finally {
    try {
    if (is != null) {
    is.close();
    }
    if (httpConn != null) {
    disconnect(httpConn);
    }
    if (fos != null) {
    fos.close();
    }
    if (randomFile != null) {
    randomFile.close();
    }
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }
    /** 读取HttpURLConnection的数据并关闭相关流。 */
    public static byte[] fetchData_doClose(HttpURLConnection httpConn) {
    byte[] data = null;
    ByteArrayOutputStream baos = null;
    InputStream is = null;
    int read = -1;
    try {
    baos = new ByteArrayOutputStream();
    is = httpConn.getInputStream();
    while ((read = is.read()) != -1) {
    baos.write(read);
    }
    data = baos.toByteArray();
    } catch (IOException ie) {
    ie.printStackTrace();
    } finally {
    try {
    if (is != null) {
    is.close();
    }
    if (baos != null) {
    baos.close();
    }
    if (httpConn != null) {
    httpConn.disconnect();
    }
    } catch (IOException ie) {
    ie.printStackTrace();
    }
    }
    return data;
    }
    public static void disconnect(HttpURLConnection httpConn) {
    if (httpConn != null) {
    httpConn.disconnect();
    }
    }
    }

    /res/drawable/l_download.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="20dip"
    android:layout_marginBottom="0dip">
    <LinearLayout android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:layout_weight="1"
    android:layout_marginLeft="10dip"
    android:layout_marginRight="10dip">
    <LinearLayout android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="10dip"
    android:layout_marginBottom="10dip"
    android:orientation="horizontal">
    <TextView android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:id="@+id/txtName"
    android:singleLine="true"
    android:ellipsize="middle"
    android:textColor="#ffffffff"
    android:textSize="15sp"
    ></TextView>
    <TextView android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/txtProgress"
    android:textColor="#ffffffff"
    android:textSize="10sp"
    android:layout_marginRight="10dip"
    ></TextView>
    <TextView android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/txtSize"
    android:textColor="#ffffffff"
    android:textSize="10sp"
    ></TextView>
    </LinearLayout>
    <ProgressBar android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:id="@+id/progressBar"
    style="?android:attr/progressBarStyleHorizontal" mce_style="?android:attr/progressBarStyleHorizontal"
    ></ProgressBar>
    </LinearLayout>
    <lab.sodino.downloadbreak.ui.DontPressWithParentButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:minWidth="60dip"
    android:minHeight="30dip"
    android:id="@+id/btnAction"
    android:textColor="#ff000000"
    android:textStyle="bold"
    android:layout_marginRight="5dip"
    android:layout_marginLeft="5dip"
    android:layout_marginTop="10dip"
    android:layout_marginBottom="10dip"
    android:focusable="false"
    android:focusableInTouchMode="false"
    ></lab.sodino.downloadbreak.ui.DontPressWithParentButton>
    </LinearLayout>

    所要添加的权限

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

  • 相关阅读:
    推荐一本书 改善你的视力:跟眼镜说再见
    Gentoo中gcc4.1.2到gcc4.3.2的升级
    msbuild学习的一些相关链接
    SqlServer 2005安装问题
    Gentoo linux中安装php5运行环境
    sql 时间函数(全)
    asp.net中的对话框
    win7 资源管理器指向我的电脑
    C/C++ 位操作 总结
    【转】Java字节序转换
  • 原文地址:https://www.cnblogs.com/nan325/p/3030549.html
Copyright © 2011-2022 走看看