依赖:
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' compile 'com.android.support:recyclerview-v7:26.1.0' //注意版本 compile 'com.squareup.retrofit2:retrofit:2.0.1' // Okhttp库 compile 'com.squareup.okhttp3:okhttp:3.1.2' compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5' // Retrofit库 implementation 'com.squareup.retrofit2:retrofit:2.4.0' compile 'com.squareup.retrofit2:converter-gson:2.4.0' compile 'com.squareup.retrofit2:adapter-rxjava2:2.4.0' // rxjava+rxandroid+retrofit2+okhttp implementation 'io.reactivex.rxjava2:rxandroid:2.0.2' implementation 'io.reactivex.rxjava2:rxjava:2.1.12' compile 'com.github.bumptech.glide:glide:3.7.0' compile 'com.google.code.gson:gson:2.2.4' compile 'com.squareup.okhttp3:logging-interceptor:3.4.1' compile 'com.jakewharton:butterknife:8.8.1' annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' compile 'org.greenrobot:eventbus:3.0.0' compile 'com.github.bumptech.glide:glide:3.7.0' compile 'com.facebook.fresco:fresco:0.12.0'
//清单文件加个请求网络
bean包:
DateBean:
public class DateBean { /** * success : true * msg : 检查到新的版本 * data : {"last_version":5,"last_must_update":3,"url":"http://www.xieast.com/api/new_version.apk","md5":"8a7e66aa24db642af2afbff7b6b1f359"} */ private boolean success; private String msg; private DataBean data; public boolean isSuccess() { return success; } public void setSuccess(boolean success) { this.success = success; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public DataBean getData() { return data; } public void setData(DataBean data) { this.data = data; } public static class DataBean { /** * last_version : 5 * last_must_update : 3 * url : http://www.xieast.com/api/new_version.apk * md5 : 8a7e66aa24db642af2afbff7b6b1f359 */ private int last_version; private int last_must_update; private String url; private String md5; public int getLast_version() { return last_version; } public void setLast_version(int last_version) { this.last_version = last_version; } public int getLast_must_update() { return last_must_update; } public void setLast_must_update(int last_must_update) { this.last_must_update = last_must_update; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getMd5() { return md5; } public void setMd5(String md5) { this.md5 = md5; } } }
inter包:
ApiService类:
public interface ApiService { @GET("api/checkversion.php") Observable<DateBean> getData(); }
Service类:
public class ServiceUrl { public static final String BASE_URL="http://www.xieast.com/";
}
IPresenter类:
public interface IPresenter<T> { void OnReceiver(T t); }IView类:
public interface IView<T> { void OnSuccess(T t); }
model层:
DateModel类:
public class DateModel { IPresenter iPresenter; public DateModel(IPresenter iPresenter){ this.iPresenter=iPresenter; } public void getData(){ HttpUtils.getInstance().getService() .getData() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<DateBean>() { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(DateBean dateBean) { iPresenter.OnReceiver(dateBean); } @Override public void onError(Throwable e) { } @Override public void onComplete() { } }); } }
presenter包:
DatePresenter:
public class DatePresenter implements IPresenter<DateBean> { IView iView; private DatePresenter dataPresenter; private DateModel dataModel; public DatePresenter(IView iView){ this.iView=iView; } public void getData(){ dataModel = new DateModel(this); dataModel.getData(); } @Override public void OnReceiver(DateBean dateBean) { iView.OnSuccess(dateBean); } }
utils包:
HttpUtils类:
public class HttpUtils { private static volatile HttpUtils instance; private final Retrofit retrofit; private HttpUtils(){ //日志拦截器 HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); interceptor.setLevel(HttpLoggingInterceptor.Level.BASIC); OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(interceptor) .build(); retrofit = new Retrofit.Builder() .baseUrl(ServiceUrl.BASE_URL) .client(client) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); } public static HttpUtils getInstance(){ if(instance==null){ synchronized (HttpUtils.class){ if(null==instance){ instance=new HttpUtils(); } } } return instance; } public ApiService getService(){ return retrofit.create(ApiService.class); } }
View层:
FileMd5Utils类:
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); } }
MainActivity类:
public class MainActivity extends AppCompatActivity implements IView<DateBean> { @BindView(R.id.btone) Button btone; private DatePresenter datePresenter; private ProgressDialog dialog; private DateBean.DataBean data; private boolean isMust = false; private File file; private SharedPreferences sp; private String url = "http://www.xieast.com/"; private Long fileLength; private boolean isCoun = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); //判断外置存储是否挂载 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(); } } } //获取一个 sp = getSharedPreferences("version", MODE_PRIVATE); fileLength = sp.getLong("length", 0); if (fileLength != 0) { isCoun = true; } else { isCoun = false; } datePresenter = new DatePresenter(this); datePresenter.getData(); //创建进度条弹出框 dialog = new ProgressDialog(MainActivity.this); //设置进度条框式为横向 dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); //设置手指触摸弹出框外,触发点击事件 dialog.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialogInterface) { dialog.dismiss(); finish(); } }); initLenter(); } //设置监听方法 private void initLenter() { btone.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //获取更大的新版本 int last_version = data.getLast_version(); try { PackageManager packageManager = getPackageManager(); PackageInfo packageInfo = packageManager.getPackageInfo(getPackageName(), 0); int i= packageInfo.versionCode; if (i<last_version){ AlertDialog.Builder builder=new AlertDialog.Builder(MainActivity.this); builder.setTitle("更新").setMessage("检查到最新版本"); //判断是否要强制更新 if(i<data.getLast_must_update()){ isMust=true; Toast.makeText(MainActivity.this,"版本太需要强制更新",Toast.LENGTH_SHORT).show(); //设置按钮并添加点击事件监听 builder.setPositiveButton("立刻更新", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { //调用下载新版本Apk setUpdateVersion(data.getUrl(), data.getMd5()); } })//当手指触摸到弹框之外进行监听 .setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialogInterface) { //关闭弹框 dialog.dismiss(); //关闭当前程序 finish(); } }); }else{ builder.setNegativeButton("取消",null).setPositiveButton("更新", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { Toast.makeText(MainActivity.this,"更新版本",Toast.LENGTH_SHORT).show(); } }); } AlertDialog alertDialog = builder.create(); alertDialog.setCanceledOnTouchOutside(!isMust); alertDialog.show(); }else{ Toast.makeText(MainActivity.this,"已近是最新版本",Toast.LENGTH_SHORT).show(); } } catch (Exception e) { e.printStackTrace(); } } }); } //现在APK方法 private void setUpdateVersion(String url, final String md5){ dialog.show(); OkHttpClient client = new OkHttpClient(); final Request request = new Request.Builder() .url(url) .get() .build(); okhttp3.Call call = client.newCall(request); call.enqueue(new okhttp3.Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { long l = response.body().contentLength(); InputStream inputStream = response.body().byteStream(); toInputString(l,inputStream,md5); } }); } private void toInputString(long l, InputStream inputStream,String md5) { try { //随机储存的类 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循环进行读取数据 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); //为进度条赋值 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 OnSuccess(DateBean dateBean) { data = dateBean.getData(); } }