Android——app的版本更新(强制更新/非强制更新)

比眉伴天荒 2022-06-02 01:23 664阅读 0赞

1.App版本检测:

要实现App的更新下载,我们上面介绍了,前提是服务器要保存一个App的版本号(通常的方式是保存versionCode,当然你要对比versionName也没关系)。当用户去手动检测版本,或者进入首页自动检测时,第一步是需要请求服务器的版本号,拿到版本号之后与当前App版本号(当前版本号可通过PackageInfo获取)进行对比。服务器返回的版本号大于当前App版本号,证明App已经有更新,那么进入第2步。

2.Apk下载

Apk文件是保存在服务器的。我们可以通过Http流将其下载到本地手机,然后更新安装。Android中下载的方式很多种:HttpUrlConnection,Retrofit,okHttp,以及android原生的下载工具类DownLoadManager 等等。我们采用的方式是Google推荐的下载工具类DownLoadManager。关于DownLoadManager的使用其实很简单,简单概括如下:

(1)通过getSystemService获取DownLoadManager。

(2)初始化DownLoadManager的Request,构建下载请求。

(3)调用DownLoadManager的enqueue异步发起请求,该方法返回值为标识当前下载任务的id,即downloadId。

(4)当下载完成后,系统会发出条件为android.intent.action.DOWNLOAD_COMPLETE的广播,我们可以自定义广播接受器,然后在onReceive中处理下载完成的逻辑即可。

我们使用OkHttp实现版本更新下载:

添加依赖:

  1. compile 'com.google.code.gson:gson:2.8.2'
  2. compile 'com.squareup.okhttp3:okhttp:3.9.1'

封装OKHttpUtils

  1. public class OKHttpUtils {
  2. private static volatile OKHttpUtils instance;
  3. private final OkHttpClient client;
  4. private Handler handler = new Handler();
  5. private OKHttpUtils(){
  6. client = new OkHttpClient();
  7. }
  8. public static OKHttpUtils getInstance(){
  9. if (null == instance){
  10. synchronized (OKHttpUtils.class){
  11. if (instance==null){
  12. instance = new OKHttpUtils();
  13. }
  14. }
  15. }
  16. return instance;
  17. }
  18. public void get(String url, Map<String, String> map, final NetCallBack callBack, final Class clazz) {
  19. // http://www.wuxirui.com/api/checkversion.php?version=2.0.2&from=android // 1.http://www.wuxirui.com/api/checkversion.php // 2.http://www.wuxirui.com/api/checkversion.php?version=2.0.2 & // 3.http://www.wuxirui.com/api/checkversion.php? if (TextUtils.isEmpty(url)) {
  20. return;
  21. }
  22. StringBuffer sb = new StringBuffer(url);
  23. // 是否含有? (2,3) if (url.contains("?")) {
  24. // 问号是否是最后一个字符,是3 否2 if (url.endsWith("?")) {
  25. for (Map.Entry entry : map.entrySet()) {
  26. sb.append(entry.getKey())
  27. .append("=")
  28. .append(entry.getValue())
  29. .append("&");
  30. }
  31. } else { // 问号不是最后一个字符2 for (Map.Entry entry : map.entrySet()) {
  32. sb.append("&")
  33. .append(entry.getKey())
  34. .append("=")
  35. .append(entry.getValue());
  36. }
  37. sb.append("&");
  38. }
  39. } else { // 不包含? if (!map.isEmpty()) {
  40. sb.append("?");
  41. for (Map.Entry entry : map.entrySet()) {
  42. sb.append(entry.getKey())
  43. .append("=")
  44. .append(entry.getValue())
  45. .append("&");
  46. }
  47. }
  48. }
  49. sb.deleteCharAt(sb.lastIndexOf("&"));
  50. Log.i("zzz", "get url is: " + sb);
  51. Request request = new Request.Builder()
  52. .url(sb.toString())
  53. .get()
  54. .build();
  55. Call call = client.newCall(request);
  56. call.enqueue(new Callback() {
  57. @Override public void onFailure(Call call, final IOException e) {
  58. Log.e("zzz", "onFailure: " + e.getMessage());
  59. handler.post(new Runnable() {
  60. @Override public void run() {
  61. callBack.onFailed(e);
  62. }
  63. });
  64. }
  65. @Override public void onResponse(Call call, Response response) throws IOException {
  66. String result = response.body().string();
  67. Log.i("zzz", "onResponse: " + result);
  68. if (!TextUtils.isEmpty(result)) {
  69. Gson gson = GsonUtils.getInstance();
  70. final Object o = gson.fromJson(result, clazz);
  71. handler.post(new Runnable() {
  72. @Override public void run() {
  73. callBack.onSuccess(o);
  74. }
  75. });
  76. }
  77. }
  78. });
  79. }
  80. public void download(String url, final File file, final DownLoadListener listener) {
  81. // /sdcard/download/aaa.apk // 父目录是否存在 File parent = file.getParentFile();
  82. if (!parent.exists()) {
  83. parent.mkdir();
  84. }
  85. // 文件是否存在 if (!file.exists()) {
  86. try {
  87. file.createNewFile();
  88. } catch (IOException e) {
  89. e.printStackTrace();
  90. }
  91. }
  92. Request request = new Request.Builder()
  93. .url(url)
  94. .build();
  95. Call call = client.newCall(request);
  96. call.enqueue(new Callback() {
  97. @Override public void onFailure(Call call, final IOException e) {
  98. handler.post(new Runnable() {
  99. @Override public void run() {
  100. listener.failed(e);
  101. }
  102. });
  103. }
  104. @Override public void onResponse(Call call, Response response) throws IOException {
  105. ResponseBody body = response.body();
  106. // 获取文件总长度 long totalLength = body.contentLength();
  107. try {
  108. InputStream is = body.byteStream();
  109. FileOutputStream fos = new FileOutputStream(file);
  110. byte[] buf = new byte[2048];
  111. int length = 0;
  112. int sum = 0;
  113. while ((length = is.read(buf, 0, buf.length)) != -1) {
  114. sum += length;
  115. long progress = sum * 100 / totalLength;
  116. listener.progress(progress);
  117. fos.write(buf, 0, length);
  118. }
  119. fos.flush();
  120. is.close();
  121. fos.close();
  122. listener.success(file.getAbsolutePath());
  123. } catch (IOException e) {
  124. e.printStackTrace();
  125. }
  126. }
  127. });
  128. }
  129. }

定义接口:

NetCallBack

  1. public interface NetCallBack {
  2. void onSuccess(Object o);
  3. void onFailed(Exception e);
  4. }

DownLoadListener

  1. public interface DownLoadListener {
  2. void success(String path);
  3. void failed(Exception e);
  4. void progress(long progress);
  5. }

gson单例模式:

GsonUtils

  1. public class GsonUtils {
  2. private static Gson instance;
  3. private GsonUtils(){
  4. }
  5. public static Gson getInstance(){
  6. if (instance==null){
  7. instance = new Gson();
  8. }
  9. return instance;
  10. }
  11. }

VersionUtils

  1. public class VersionUtils {
  2. public static String getVersionName(Context context) {
  3. String versionName = "";
  4. PackageManager packageManager = context.getPackageManager();
  5. String packageName = context.getPackageName();
  6. try {
  7. PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0);
  8. versionName = packageInfo.versionName;
  9. } catch (PackageManager.NameNotFoundException e) {
  10. e.printStackTrace();
  11. }
  12. return versionName;
  13. }
  14. }

MainActivity

  1. public class MainActivity extends AppCompatActivity {
  2. private static final String TAG = "MainActivity";
  3. private String updateUrl = "http://www.wuxirui.com/api/checkversion.php";
  4. //http://www.wuxirui.com/download/jinritoutiao.apk private String path = "/download/";
  5. private Button btn;
  6. private ProgressBar progressBar;
  7. private TextView tv_pro;
  8. @SuppressLint("HandlerLeak")
  9. private Handler handler = new Handler() {
  10. @Override public void handleMessage(Message msg) {
  11. super.handleMessage(msg);
  12. int i = msg.what;
  13. if (i==100){
  14. tv_pro.setVisibility(View.GONE);
  15. progressBar.setVisibility(View.GONE);
  16. }else {
  17. tv_pro.setText(i + "%");
  18. progressBar.setProgress(i);
  19. }
  20. }
  21. };
  22. @Override protected void onCreate(Bundle savedInstanceState) {
  23. super.onCreate(savedInstanceState);
  24. setContentView(R.layout.activity_main);
  25. btn = findViewById(R.id.btn);
  26. progressBar = findViewById(R.id.progressBar);
  27. tv_pro = findViewById(R.id.tv_pro);
  28. String versionName = VersionUtils.getVersionName(this);
  29. Log.i("zzz", "versionName: " + versionName);
  30. Map<String, String> map = new HashMap<>();
  31. map.put("version", versionName);
  32. // if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
  33. File externalStorageDirectory = Environment.getExternalStorageDirectory();
  34. // /sdcard/download/ String absolutePath = externalStorageDirectory.getAbsolutePath();
  35. path = absolutePath + path;
  36. }
  37. OKHttpUtils.getInstance().get(updateUrl, map, new NetCallBack() {
  38. @Override public void onSuccess(Object o) {
  39. if (o instanceof MessageBean) {
  40. MessageBean info = (MessageBean) o;
  41. if (info != null) {
  42. if (info.isSuccess()) {
  43. MessageBean.ResultBean result1 = info.getResult();
  44. final MessageBean.ResultBean result = info.getResult();
  45. // 有更新 if (result.isHasnewversion()) {
  46. boolean isMust = result.isMustupdate();
  47. AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
  48. builder.setTitle("更新提示")
  49. .setMessage(isMust ? "需要立即更新" : "是否需要更新")
  50. .setPositiveButton("更新", new DialogInterface.OnClickListener() {
  51. @Override public void onClick(DialogInterface dialogInterface, int i) {
  52. tv_pro.setVisibility(View.VISIBLE);
  53. progressBar.setVisibility(View.VISIBLE);
  54. String url = result.getUrl();
  55. // http://www.wuxirui.com/download/jinritoutiao.apk if (url.contains(".")) {
  56. String typeName = url.substring(url.lastIndexOf(".") + 1);
  57. if (url.contains("/")) {
  58. String filename = url.substring(url.lastIndexOf("/") + 1, url.lastIndexOf("."));
  59. path = path + filename + "." + typeName;
  60. }
  61. }
  62. // 下载 download(result.getUrl(), new File(path), new DownLoadListener() {
  63. @Override public void success(String path) {
  64. Log.i(TAG, "success: " + path);
  65. // 启动自动安装 installApk(new File(path));
  66. }
  67. @Override public void failed(Exception e) {
  68. Log.e(TAG, "failed: " + e.getMessage());
  69. }
  70. @Override public void progress(long progress) {
  71. Log.i(TAG, "progress: " + progress);
  72. handler.sendEmptyMessageDelayed((int) progress, 1000);
  73. }
  74. });
  75. }
  76. });
  77. if (!isMust) {
  78. builder.setNegativeButton("稍候", new DialogInterface.OnClickListener() {
  79. @Override public void onClick(DialogInterface dialogInterface, int i) {
  80. }
  81. });
  82. }
  83. builder.create().show();
  84. }
  85. }
  86. }
  87. }
  88. }
  89. @Override public void onFailed(Exception e) {
  90. }
  91. }, MessageBean.class);
  92. }
  93. private void download(String url, File file, DownLoadListener listener) {
  94. OKHttpUtils.getInstance().download(url, file, listener);
  95. }
  96. /** * 安装apk * * @param file */ private void installApk(File file) {
  97. Intent intent = new Intent();
  98. intent.setAction(Intent.ACTION_VIEW);
  99. intent.addCategory("android.intent.category.DEFAULT");
  100. intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  101. intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
  102. startActivity(intent);
  103. android.os.Process.killProcess(android.os.Process.myPid());
  104. }
  105. }

布局文件:

activity_main.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:tools="http://schemas.android.com/tools"
  4. xmlns:app="http://schemas.android.com/apk/res-auto"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. tools:context="com.bwie.com.myapplication.MainActivity">
  8. <Button android:id="@+id/btn"
  9. android:layout_width="match_parent"
  10. android:layout_height="wrap_content"
  11. android:text="检测版本更新"
  12. />
  13. <ProgressBar android:id="@+id/progressBar"
  14. style="?android:attr/progressBarStyleHorizontal"
  15. android:layout_width="match_parent"
  16. android:layout_height="wrap_content"
  17. android:layout_below="@+id/btn"
  18. android:layout_marginTop="45dp"
  19. android:max="100"
  20. android:visibility="gone"
  21. android:progress="0"
  22. />
  23. <TextView android:id="@+id/tv_pro"
  24. android:layout_width="wrap_content"
  25. android:layout_height="wrap_content"
  26. android:text="0%"
  27. android:layout_below="@+id/progressBar"
  28. android:layout_centerHorizontal="true"
  29. android:visibility="gone"
  30. />
  31. </RelativeLayout>

发表评论

表情:
评论列表 (有 0 条评论,664人围观)

还没有评论,来说两句吧...

相关阅读

    相关 APP强制更新(uni-app)

    项目要求: 用户打开APP后,检测是否是最新版本,不是最新版本提示用户更新。 -------------------- 解决思路: 步骤一:需要一个接口,用来和

    相关 vue 强制更新

    发生情况: (1)没有留意到数组或对象的变更检测注意事项 (2)可能依赖了一个未被 Vue 的响应式系统追踪的状态 (3)已经做到了上述的