版本更新+断点续传

什么是断点续传?

断点续传其实正如字面意思,就是在下载的断开点继续开始传输,不用再从头开始。所以理解断点续传的核心后,发现其实和很简单,关键就在于对传输中断点的把握。

原理:

断点续传的关键是断点,所以在制定传输协议的时候要设计好,如上图,我自定义了一个交互协议,每次下载请求都会带上下载的起始点,这样就可以支持从断点下载了,其实HTTP里的断点续传也是这个原理,在HTTP的头里有个可选的字段RANGE,表示下载的范围,下面是我用JAVA语言实现的下载断点续传示例。

首先:设置一个点击事件,检测最新的版本号,返回版本号后,在客服端比较更新即可。

分包:(利用MVP模式,访问接口)为了不遗漏,我会把所有页面代码粘进来

接下来,就从版本检测开始一步一步走:

MainActivity:(抽了基类,基类在下面)

package com.example.version;

import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import com.example.version.bean.VersionBean;
import com.example.version.inter.IView;
import com.example.version.presenter.UpdataPresenter;
import com.example.version.presenter.VersionPresenter;
import com.example.version.utils.BaseActivity;
import com.example.version.utils.FileMd5Utils;
import com.example.version.utils.VersionUtils;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import okhttp3.Response;

public class MainActivity extends BaseActivity implements IView<VersionBean> {
    private static final String TAG = "MainActivity";
    private VersionPresenter versionPresenter;
    private Button versionUpdata;
    private VersionBean versionBean;
    private UpdataPresenter updataPresenter;
    private boolean isMust;
    private File file;
    private ProgressDialog dialog;
    private SharedPreferences sp;
    private long fileLength;
    private boolean isSaved = false;
    private String range;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //判断外置存储是否挂载
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            //若挂载 则创建存放位置(SDK中)
            File externalStorageDirectory = Environment.getExternalStorageDirectory();
            //文件的创建地址
            String path = externalStorageDirectory.getAbsolutePath() + File.separator + "new.apk";
            file = new File(path);
            //判断文件是否存在 不存在则创建
            if (!file.exists()) {
                try {
                    file.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        //创建一个getSharedPreferences对象,等会存文件大小的值
        sp = getSharedPreferences("version", MODE_PRIVATE);
        //得到当前文件的长度
        fileLength = sp.getLong("lenght", 0);
        //创建P层,得到最新版本的信息
        versionPresenter = new VersionPresenter(this);
        versionPresenter.netVersion();

        //创建下载apk的P层
        updataPresenter = new UpdataPresenter(new IView<Response>() {
            @Override
            public void success(Response response) {
                //定义一个变量接受当前文件的大小
                long contentLength = 0;
                //判断fileLength的值,如果是第一次就存值,第二次就直接赋值
                if (fileLength == 0) {
                    contentLength = response.body().contentLength();
                    sp.edit().putLong("lenght", contentLength).commit();
                    isSaved = false;
                    Log.e(TAG, "successqqqqq: "+contentLength );
                } else {
                    Log.e(TAG, "successqqqqq: "+contentLength );
                    isSaved = true;
                    contentLength = fileLength;
                }
                //得到文件的输出流
                InputStream inputStream = response.body().byteStream();
                //调用存储的方法
                inputToFile(contentLength, inputStream);
            }

            @Override
            public void error(Throwable t) {
                Log.e(TAG, "error: " + t.getMessage());
            }
        });

        //创建一个ProgressDialog,下载进度条。
        dialog = new ProgressDialog(MainActivity.this);
        dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        dialog.setMessage("下载进度");

    }

    private void inputToFile(long contentLength, InputStream inputStream) {
        // 创建一个byte数组
        byte[] bytes = new byte[2048];
        // 定义一个长度
        int len = 0;
        // 得到文件的长度
        long sum = file.length();
        try {
            // 创建一个RandomAccessFile,随机储存的类
            RandomAccessFile raf = new RandomAccessFile(file, "rw");
            // 移动到当前已经下载文件的大小位置
            raf.seek(sum);
            // 循环遍历得到输出流的值
            while ((len = inputStream.read(bytes)) != -1) {
                // 把流的大小写入到RandomAccessFile
                raf.write(bytes, 0, len);
                sum += len;
                // 移动到当前已经下载文件的大小位置
                raf.seek(sum);
                // 把下载进度转变成progress值
                int i = (int) (sum * 100 / contentLength);
                dialog.setProgress(i);
                // 当进度下载到100时
                if (i > 99) {
                    // 关流
                    inputStream.close();
                    // 进度弹框消失
                    dialog.dismiss();
                    // 比较md5,并下载值
                    isMd5(file);
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 把文件转换成md5值,并于访问网络得到下载信息中的md5值,进行比较
     * @param file
     */
    private void isMd5(File file) {
        String fileMD5 = FileMd5Utils.getFileMD5(file);
        Log.e(TAG, "fileMD5: " + fileMD5);
        String md5 = versionBean.getData().getMd5();
        Log.e(TAG, "md5: " + md5 );
        if (fileMD5.equalsIgnoreCase(md5)) {
            // 调用系统安装的方法
            installApk(file);
        } else {
            Toast.makeText(this, "下载版本有误", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * 系统安装的方法
     * @param file
     */
    private void installApk(File file) {
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_VIEW);
        intent.addCategory(Intent.CATEGORY_DEFAULT);
        intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivityForResult(intent, 12345);
        System.exit(0);
    }

    @Override
    protected void init() {
        versionUpdata = findViewById(R.id.version_updata);
        versionUpdata.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                range = "";
                if (isSaved) {
                    range = "bytes=" + file.length() + "-" + fileLength;
                } else {
                    range = "bytes=" + file.length() + "-";

                }


                // 获得当前版本
                int versionCode = VersionUtils.getVersionCode(MainActivity.this);
                // 判断当前版本号与得到的版本号,判断是否需要更新
                if (versionCode < versionBean.getData().getLastVersion()) {
                    // 弹框提示用户
                    AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                    builder.setTitle("更新").setMessage("检测到新的版本");
                    //判断当前是否需要强制更新
                    if (versionCode < versionBean.getData().getLastVersion()) {
                        isMust = false;
                        //用户点击立即更新的时候,去下载apk文件
                        builder.setPositiveButton("立即更新", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dia, int which) {
                                dialog.show();
                                //下载apk文件
                                updataPresenter.netUpdataVersion(versionBean.getData().getUrl(), range);
                            }
                            //点击外面退出应用
                        }).setOnCancelListener(new DialogInterface.OnCancelListener() {
                            @Override
                            public void onCancel(DialogInterface dialog) {
                                finish();
                            }
                        });
                    } else {
                        isMust = true;
                        //不是强制更新版本,显示更新或者取消即可
                        builder.setPositiveButton("更新", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dia, int which) {
                                dialog.show();
                                //下载apk文件
                                updataPresenter.netUpdataVersion(versionBean.getData().getUrl(), range);
                            }
                        }).setPositiveButton("取消", null);
                    }

                    AlertDialog adialog = builder.create();
                    //禁用返回键
                    adialog.setCanceledOnTouchOutside(isMust);
                    adialog.show();

                }
            }
        });
    }


    @Override
    public int getView() {
        return R.layout.activity_main;
    }

    @Override
    public void success(VersionBean versionBean) {
        this.versionBean = versionBean;
    }

    @Override
    public void error(Throwable t) {
        Log.e(TAG, "error: " + t.getMessage());


    }

}

Activity基类:

package com.example.version.utils;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
public abstract class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getView());
        init();

    }

    protected abstract void init();

    public abstract int getView();
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值