版本更新+断点续传

依赖

implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
implementation 'io.reactivex.rxjava2:rxjava:2.1.12'

implementation 'com.squareup.retrofit2:retrofit:2.4.0'
compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
compile 'com.squareup.retrofit2:converter-gson:2.4.0'

compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'

布局

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_height="match_parent"
    tools:context="com.example.com.demo513.MainActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="检测更新"
        android:id="@+id/tv"
        android:gravity="center_horizontal"
        android:layout_marginTop="30dp" />

</LinearLayout>

api

package com.example.com.demo513.api;

import com.example.com.demo513.bean.ResultBean;

import io.reactivex.Flowable;
import retrofit2.http.GET;

/**
 * Created by linmeihui on 2018/5/13.
 */

public interface ServiceApi {
    @GET("api/checkversion.php")
    Flowable<ResultBean> getBean() ;
}


model

package com.example.com.demo513.model;

import com.example.com.demo513.bean.ResultBean;
import com.example.com.demo513.presenter.DataPresenter;
import com.example.com.demo513.presenter.MyDataPresenter;
import com.example.com.demo513.utils.OkHttpUtils;

import io.reactivex.Flowable;

/**
 * Created by linmeihui on 2018/5/13.
 */

public class MyDataModel {

    public void toUrl(String url, DataPresenter dataPresenter) {

        Flowable<ResultBean> bean = OkHttpUtils.getInstance(url).getApi().getBean();
        dataPresenter.onSuccess(bean);

    }
}

presenter

扫描二维码关注公众号,回复: 1706398 查看本文章
package com.example.com.demo513.presenter;

import io.reactivex.Flowable;

/**
 * Created by linmeihui on 2018/5/13.
 */

public interface DataPresenter<T> {
    //成功方法
    void onSuccess(Flowable<T> t);

}
package com.example.com.demo513.presenter;

import com.example.com.demo513.bean.ResultBean;
import com.example.com.demo513.model.MyDataModel;
import com.example.com.demo513.view.DataView;

import io.reactivex.Flowable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import io.reactivex.subscribers.DefaultSubscriber;

/**
 * Created by linmeihui on 2018/5/13.
 */

public class MyDataPresenter implements DataPresenter<ResultBean> {

    private DataView dataView;
    private final MyDataModel myDataModel;

    public MyDataPresenter (DataView dataView){
        this.dataView=dataView;
        myDataModel = new MyDataModel();
    }

    @Override
    public void onSuccess(Flowable<ResultBean> t) {
        t.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new DefaultSubscriber<ResultBean>() {
                    @Override
                    public void onNext(ResultBean resultBean) {
                        dataView.success(resultBean);
                    }

                    @Override
                    public void onError(Throwable t) {
                        dataView.faile(t.getMessage());
                    }

                    @Override
                    public void onComplete() {

                    }
                });

    }

    public void toGetUrl(String url){
        myDataModel.toUrl(url,this);
    }

}

utils

package com.example.com.demo513.utils;

import java.io.File;
import java.io.FileInputStream;
import java.math.BigInteger;
import java.security.MessageDigest;

/**
 * Created by linmeihui on 2018/5/12.
 */

public class FileMd5Utils {

    public static String getFileMD5(File file) {
        if (!file.isFile()) {
            return null;
        }
        MessageDigest digest = null;
        FileInputStream in = null;
        byte buffer[] = new byte[1024];
        int len;
        try {
            digest = MessageDigest.getInstance("MD5");
            in = new FileInputStream(file);
            while ((len = in.read(buffer, 0, 1024)) != -1) {
                digest.update(buffer, 0, len);
            }
            in.close();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        BigInteger bigInt = new BigInteger(1, digest.digest());
        return bigInt.toString(16);
    }
}
package com.example.com.demo513.utils;

import com.example.com.demo513.api.ServiceApi;
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;

import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

/**
 * Created by linmeihui on 2018/5/13.
 */

public class OkHttpUtils {


    private static OkHttpUtils instance;
    private final Retrofit retrofit;


    private OkHttpUtils(String url){

        //拦截器
        HttpLoggingInterceptor httpLoggingInterceptor=new HttpLoggingInterceptor();
        httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);

        OkHttpClient okHttpClient=new OkHttpClient.Builder()
                .addInterceptor(httpLoggingInterceptor)
                .build();


        retrofit = new Retrofit.Builder()
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .client(okHttpClient)
                .baseUrl(url)
                .build();


    }


    public static OkHttpUtils getInstance(String url){
        if (instance==null){
            synchronized (OkHttpUtils.class){
                if (null==instance){
                    instance=new OkHttpUtils(url);
                }
            }
        }
        return instance;
    }


    public ServiceApi getApi(){
        return retrofit.create(ServiceApi.class);
    }

}
package com.example.com.demo513.view;

import com.example.com.demo513.bean.ResultBean;

/**
 * Created by linmeihui on 2018/5/13.
 */

public interface DataView<T>{
    void success(ResultBean resultBean);
    void faile(String message);

}

activity

package com.example.com.demo513;

import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import com.example.com.demo513.bean.ResultBean;
import com.example.com.demo513.presenter.MyDataPresenter;
import com.example.com.demo513.utils.FileMd5Utils;
import com.example.com.demo513.view.DataView;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;

import javax.security.auth.login.LoginException;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class MainActivity extends AppCompatActivity implements DataView<ResultBean>{

    private TextView tv;
    private MyDataPresenter myDataPresenter;
    private File file;
    private String url="http://www.xieast.com/";;
    private ProgressDialog dialog;
    private SharedPreferences sp;
    private long fileLength;
    private boolean isflag=false;
    private ResultBean.DataBean data;
    private boolean isMust=true;

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


        init();
        initListener();


    }



    private void init() {

        //判断外置存储是否挂载
        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();
                }
            }

        }


        tv = findViewById(R.id.tv);

        myDataPresenter = new MyDataPresenter(this);

        myDataPresenter.toGetUrl(url);

        //创建进度条
        dialog = new ProgressDialog(this);
        dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        //dialog.setCanceledOnTouchOutside(false);


        sp = getSharedPreferences("version",MODE_PRIVATE);

        fileLength = sp.getLong("length", 0);

        if (fileLength!=0){
            isflag=true;
        }else {
            isflag=false;
        }


    }

    //监听
    private void initListener() {

        tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                int last_version = data.getLast_version();

                //获取本地name
                PackageManager packageManager = getPackageManager();

                try {
                    PackageInfo packageInfo = packageManager.getPackageInfo(getPackageName(), 0);
                    int versionCode = packageInfo.versionCode;
                        //判断是否小宇最新版本
                        if (versionCode<last_version){

                        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);

                        builder.setTitle("更新提示");
                        builder.setMessage("检测到最新版本");
                        //判断是否小于强制更新版本
                        if (versionCode<data.getLast_must_update()){

                            isMust=true;
                            Toast.makeText(MainActivity.this,"版本太低,需要强制更新",Toast.LENGTH_SHORT).show();

                            builder.setPositiveButton("立即更新", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {

                                    //调用下载APK的方法
                                    setUpdateVersion(data.getUrl(),data.getMd5());

                                }
                            }).setOnCancelListener(new DialogInterface.OnCancelListener() {
                                @Override
                                public void onCancel(DialogInterface dialog) {
                                    dialog.dismiss();
                                    finish();
                                }
                            });
                        }else {

                            isMust=false;
                            builder.setNegativeButton("取消",null);
                            builder.setPositiveButton("立即更新", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {

                                    setUpdateVersion(data.getUrl(),data.getMd5());

                                }
                            });
                        }

                        AlertDialog alertDialog=builder.create();

                        alertDialog.setCanceledOnTouchOutside(!isMust);

                        alertDialog.show();

                    }else {
                            Toast.makeText(MainActivity.this, "已经是最新版本", Toast.LENGTH_SHORT).show();

                        }




                } catch (PackageManager.NameNotFoundException e) {
                    e.printStackTrace();
                }

            }
        });

    }


    //下载apk方法
    private void setUpdateVersion(String url, final String md5) {

        String range="";

        //如果getSharedPreferences 存入果值
        //range的头部拼接  file.length() 获取文件的长度
        //当存入又数据的话 range  bytes的拼接就等于 文件的长度 并且文件的总长度从getSharedPreferences中取出
        if (isflag){
            range="bytes="+file.length()+"-"+fileLength;
        }else {
            range="bytes="+file.length()+"-";
        }

        dialog.show();

        //实例化OKhttp对象
        OkHttpClient okHttpClient=new OkHttpClient();
        //实例化Request对象 下载文件有一个问题就是 断点, 为了避免就需要加入一个Header头部字段

        Request request=new Request.Builder()
                .addHeader("range",range)
                .url(url)
                .get()
                .build();

        Call call = okHttpClient.newCall(request);

        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {

                //定义一个文件总长度变量
                long contentLength = 0;

                //判断 如果getSharedPreferencest
                // 里存入的数据等于0的时候 获取网络请求过来数据总长度 并存入 SharedPreferencest
                if (fileLength==0){

                    contentLength = response.body().contentLength();
                    //存入sharedPreferencest
                    sp.edit().putLong("length",contentLength).commit();

                }else {

                    //如果存入的数据不等于0 则吧SharedPreferencest中的数据 赋值给 contentLength
                    contentLength=fileLength;
                }

                InputStream inputStream = response.body().byteStream();

                toInputString(contentLength, inputStream, md5);

            }
        });

    }


    private void toInputString(long l, InputStream inputStream, String md5){

        try {
            Log.d("--", "文件总长度: "+l);
            //随机储存的类
            RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
            //定义一个byte数据 来决定一次读取多少字节
            byte[] bytes=new byte[2048];

            int len=0;

            //获取文件的长度
            long sum = file.length();

            //将获取过文件长度的变量存入 随机储存类中,
            //sd卡中文件长度是0是,随机储存类储存的位置也是在0
            randomAccessFile.seek(sum);

            while ((len=inputStream.read(bytes,0,bytes.length))!=-1){

                sum += len;
                //进行写入数据
                randomAccessFile.write(bytes,0,len);
                //移动到当前已经下载的文件大小的位置
                randomAccessFile.seek(sum);
                //算出下载进度
                int i = (int) (sum * 100 / l);
                Log.d("--", "toInputString: "+i);
                //为进度条赋值
                dialog.setProgress(i);

                //当进度条数大于99时进行关闭
                if (i > 99) {

                    //读取完了 需要进行关闭流
                    inputStream.close();

                    //并关闭进度条的弹框
                    dialog.dismiss();

                    //调用检测 本地文件是否还和网络文件一致
                    checkAPK(file, md5);

                    //跳出循环
                    break;
                }

            }




        } catch ( Exception e) {
            e.printStackTrace();
        }


    }

    private void checkAPK(File file, String md5) {

        //调用工具类 算出文件的md5        String fileMD5 = FileMd5Utils.getFileMD5(file);

        Log.d("--", "网络md5:---" + md5 + "--本地文件的MD5:--" + fileMD5);
        //equalsIgnoreCase 实现可以忽略大小写, 由于md5是又大小写
        //判断 文件的md5值 和网络下载的md5值 是否一致
        if (fileMD5.equalsIgnoreCase(md5)) {
            insertAPK(file);
        } else {
            Toast.makeText(this, "文件出现故障 md5值不一致", Toast.LENGTH_SHORT).show();
        }


    }

    private void insertAPK(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");
        //最好添加上这个Flag
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivityForResult(intent, 1000);
        System.exit(0);


    }

    @Override
    public void success(ResultBean resultBean) {
        Log.i("----", "success: "+resultBean.getData().getLast_version());

        data = resultBean.getData();
    }

    @Override
    public void faile(String message) {

    }
}

猜你喜欢

转载自blog.csdn.net/ch5211314/article/details/80302322
今日推荐