第一步,通过接口获取更新信息(版本号、更新内容、apk下载地址、是否强制更新)
第二步,通过接口拿到的版本号和本地的版本号进行比较,如果拿到的版本号比本地的版本号大,那就进行版本升级
第三步,版本升级分为三种情况:
1、非wifi情况下,弹出版本更新提示框,用户点击“立即升级”按钮开始下载apk,下载完成后提示安装。
2、wifi情况下,直接后台下载apk,下载完后弹出版本更新提示框,用户点击“立即安装”按钮开始安装apk。
===========================================
第二步,通过接口拿到的版本号和本地的版本号进行比较,如果拿到的版本号比本地的版本号大,那就进行版本升级
第三步,版本升级分为三种情况:
1、非wifi情况下,弹出版本更新提示框,用户点击“立即升级”按钮开始下载apk,下载完成后提示安装。
2、wifi情况下,直接后台下载apk,下载完后弹出版本更新提示框,用户点击“立即安装”按钮开始安装apk。
3、强制更新为true的时候,无论是否wifi情况下,都是应该弹出更新提示框,用户点击“立即升级”按钮开始下载升级,提示框不能取消,点击“关闭”按钮直接退出app。
基本思路就是以上,接下来进入主题:
版本号: 红框里面是版本号 ,为了进行测验代码 ,版本号可更改
上代码:
MainActivity 类:
import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.widget.ProgressBar; import android.widget.SeekBar; import java.io.File; import java.util.HashMap; import bwie.com.day_update_1227.bean.Update; import bwie.com.day_update_1227.http.VersionUtils; import bwie.com.day_update_1227.presenter.UpDatePresenter; import bwie.com.day_update_1227.view.DownLoadListener; import bwie.com.day_update_1227.view.IView; public class MainActivity extends AppCompatActivity implements IView{ private static final String TAG = "MainActivity"; private String url="http://www.wuxirui.com/"; private Update.ResultBean result; private String path = "/download/"; private UpDatePresenter upDatePresenter; private SeekBar seekBar; private ProgressBar progressBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); progressBar = (ProgressBar)findViewById(R.id.progressBar3); //使用工具类获取到自己的版本号 String versionName = VersionUtils.getVersionName(this); //判断是否挂载 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { File externalStorageDirectory = Environment.getExternalStorageDirectory(); // /sdcard/download/ String absolutePath = externalStorageDirectory.getAbsolutePath(); path = absolutePath + path; } HashMap<String,String> map = new HashMap<>(); map.put("version",versionName); upDatePresenter = new UpDatePresenter(); upDatePresenter.attachView(this); upDatePresenter.getUpdate(url,map); } @Override public void success(Object o) { if (o instanceof Update){ Update news= (Update) o; if (news!=null){ //请求成功 isSuccess 成功:true 。失败:false if(news.isSuccess()){ result = news.getResult(); //有更新 isHas_new_version 更新 if (result.isHas_new_version()){ //isMust_update 强制更新 boolean isMust = result.isMust_update(); //显示对话框 AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setTitle("更新提示") //使用三元运算判断强制更新是否为true或者fales .setMessage(isMust ? "需要立即跟新":"是否需要跟新") .setPositiveButton("更新", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { String url = result.getUrl(); //contains :包含 if (url.contains(".")){ String typeName = url.substring(url.lastIndexOf(".") + 1); if (url.contains("/")){ String filename = url.substring(url.lastIndexOf("/") + 1, url.lastIndexOf(".")); path = path + filename + "." + typeName; } } //下载 download(result.getUrl(), new File(path), new DownLoadListener() { @Override public void success(String path) { Log.i(TAG, "success: " + path); // 启动自动安装 installApk(new File(path)); } @Override public void failes(Exception e) { Log.e(TAG, "failed: " + e.getMessage()); } @Override public void progress(long progress) { Log.i(TAG, "progress: " + progress); //设置进度条 progressBar.setProgress((int) progress); } }); } }); //如果强制更新为false就走以下 if (!isMust){ builder.setNegativeButton("稍候", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { } }); } builder.create().show(); } } } } } @Override public void failes(Exception e) { } private void download(String url, File file, DownLoadListener listener){ upDatePresenter.download(url,file,listener); } /** * 安装apk * @param file */ private void installApk(File file) { Intent intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); intent.addCategory("android.intent.category.DEFAULT"); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive"); startActivity(intent); android.os.Process.killProcess(android.os.Process.myPid()); } }
view 视图接口:
public interface IView {
void success(Object o);
void failes(Exception e);
}
public interface DownLoadListener {
void success(String path);
void failes(Exception e);
void progress(long progress);
}
===========================================
Presenter层:
import android.os.Handler; import android.util.Log; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Map; import bwie.com.day_update_1227.bean.Update; import bwie.com.day_update_1227.model.IModel; import bwie.com.day_update_1227.model.UpDateModel; import bwie.com.day_update_1227.view.DownLoadListener; import bwie.com.day_update_1227.view.IView; import io.reactivex.Flowable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; import io.reactivex.subscribers.DisposableSubscriber; import okhttp3.Call; import okhttp3.Callback; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import okhttp3.ResponseBody; /** * Created by 迷人的脚毛!! on 2017/12/27. */ public class UpDatePresenter implements IPresenter { private Handler handler=new Handler(); private static final String TAG = "UpDatePresenter"; private IView iv; private DisposableSubscriber<Update> disposableSubscriber; public void attachView(IView iv) { this.iv = iv; } public void delete(){ if (iv!=null){ iv=null; } if (disposableSubscriber!=null){ if (disposableSubscriber.isDisposed()){ disposableSubscriber.dispose(); } } } @Override public void getUpdate(String url, Map<String,String> map) { IModel model=new UpDateModel(this); model.getUpdate(url,map); } public void getUpdates(Flowable<Update> flowable){ disposableSubscriber = flowable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(new DisposableSubscriber<Update>() { @Override public void onNext(Update update) { if (update!=null){ iv.success(update); Log.i(TAG, "p层: "+update.getMessage()); } } @Override public void onError(Throwable t) { } @Override public void onComplete() { } }); } public void download(String url, final File file, final DownLoadListener listener){ // 父目录是否存在 File parent = file.getParentFile(); if (!parent.exists()) { parent.mkdir(); } // 文件是否存在 if (!file.exists()) { try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url(url) .build(); Call call = client.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(Call call, final IOException e) { handler.post(new Runnable() { @Override public void run() { listener.failes(e); } }); } @Override public void onResponse(Call call, Response response) throws IOException { ResponseBody body = response.body(); // 获取文件总长度 long totalLength = body.contentLength(); try { InputStream is = body.byteStream(); FileOutputStream fos = new FileOutputStream(file); byte[] buf = new byte[2048]; int length = 0; int sum = 0; while ((length = is.read(buf, 0, buf.length)) != -1) { sum += length; long progress = sum * 100 / totalLength; listener.progress(progress); fos.write(buf, 0, length); } fos.flush(); is.close(); fos.close(); listener.success(file.getAbsolutePath()); } catch (IOException e) { e.printStackTrace(); } } }); } }
接口:
public interface IPresenter { void getUpdate(String url, Map<String,String> map); }
================================================================================
Model层:public class UpDateModel implements IModel { private UpDatePresenter presenter; public UpDateModel(UpDatePresenter presenter) { this.presenter = presenter; } @Override public void getUpdate(String url,Map<String,String> map) { Flowable<Update> flowable = RetrofirUtil.getInsterent(url).getApiService().getUpdate(map); presenter.getUpdates(flowable); } }
====================================================================public interface IModel { void getUpdate(String url, Map<String,String> map); }
接下来就是工具类:ApiService:这样就可以简单的实现一个版本更新具体逻辑可以根据实际要求进行嵌套,在这个版本更新中,我用到了okhttp解析,public interface ApiService { //http://www.wuxirui.com/api/checkversion.php @GET("api/checkversion.php") Flowable<Update> getUpdate(@QueryMap Map<String,String> map); }
RetrofirUtil类:import retrofit2.Retrofit; import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; import retrofit2.converter.gson.GsonConverterFactory; public class RetrofirUtil { private static volatile RetrofirUtil insterent; private final ApiService apiService; private RetrofirUtil(String url) { Retrofit retrofit = new Retrofit.Builder() .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .baseUrl(url) .build(); apiService = retrofit.create(ApiService.class); } public static RetrofirUtil getInsterent(String url){ if (null==insterent){ synchronized (RetrofirUtil.class){ if (insterent==null){ insterent=new RetrofirUtil(url); } } } return insterent; } public ApiService getApiService(){ return apiService; } }
VersionUtils 类:public class VersionUtils { public static String getVersionName(Context context) { String versionName = ""; //拿到包管理器 PackageManager packageManager = context.getPackageManager(); //拿到包名 String packageName = context.getPackageName(); try { PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0); //版本号 versionName = packageInfo.versionName; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } return versionName; } }
=========================================================封装类:public class Update { /** * success : true * message : 有可用的更新 * result : {"has_new_version":true,"must_update":true,"url":"http://www.wuxirui.com/download/jinritoutiao.apk"} */ private boolean success; private String message; private ResultBean result; public boolean isSuccess() { return success; } public void setSuccess(boolean success) { this.success = success; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public ResultBean getResult() { return result; } public void setResult(ResultBean result) { this.result = result; } public static class ResultBean { /** * has_new_version : true * must_update : true * url : http://www.wuxirui.com/download/jinritoutiao.apk */ private boolean has_new_version; private boolean must_update; private String url; public boolean isHas_new_version() { return has_new_version; } public void setHas_new_version(boolean has_new_version) { this.has_new_version = has_new_version; } public boolean isMust_update() { return must_update; } public void setMust_update(boolean must_update) { this.must_update = must_update; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } } }
还有Retrofit+Rxjava解析,因为开始想直接用 Retrofit+Rxjava 进行解析的可后来在下载新版本apk时不得不使用okhttp来进行解析,具体可以在写的时候体会!!!下一篇文章我会写断点续传,版本更新和断点续传有着密不可分的关系,为了更清晰,所以分开写。用到的依赖:
compile "io.reactivex.rxjava2:rxjava:2.1.1" compile 'io.reactivex.rxjava2:rxandroid:2.0.1' compile 'com.squareup.retrofit2:retrofit:2.3.0' compile 'com.squareup.retrofit2:converter-gson:2.3.0' compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0' compile 'com.squareup.okhttp3:okhttp:3.9.1'