🐳 《Android》 安卓开发教程 -版本更新-AppUpdater for Android

一、安卓的版本更新

在移动互联网时代,App 版本更新是应用程序维护和优化的重要环节。通过定期更新,开发者可以提升用户体验、修复漏洞,并保持竞争力

版本大致更新例子如下

二、AppUpdater for Android介绍

点击前往Github官网介绍

AppUpdater特点:

  • ✅ 专注于App更新一键傻瓜式升级
  • ✅ 够轻量,体积小
  • ✅ 支持监听下载和自定义下载流程
  • ✅ 支持下载失败时,可重新下载
  • ✅ 支持文件MD5校验,避免重复下载
  • ✅ 支持通知栏提示内容和流程全部可配置
  • ✅ 支持取消下载
  • ✅ 支持使用HttpsURLConnection或OkHttpClient进行下载
  • ✅ 支持Android 10(Q)
  • ✅ 支持Android 11(R)
  • ✅ 支持Android 12(S)

三、具体使用步骤

1. 在Project的 settings.gradle 里面添加远程仓库

xml 复制代码
allprojects {
    repositories {
        //...
        mavenCentral()
    }
}

2. 在Module的 build.gradle 里面添加引入依赖项

xml 复制代码
    implementation 'com.github.jenly1314.AppUpdater:app-updater:1.2.0'
    implementation 'com.github.jenly1314.AppUpdater:app-dialog:1.2.0'

3.新建VersionUpdateActivity

JAVA 复制代码
package com.imindbot.medicalinformation.ui.test;

import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

import com.imindbot.medicalinformation.BuildConfig;
import com.imindbot.medicalinformation.R;
import com.imindbot.medicalinformation.data.response.VersionInfoResponse;
import com.imindbot.medicalinformation.databinding.ActivityUploadDataBinding;
import com.imindbot.medicalinformation.databinding.ActivityVersionUpdateBinding;
import com.imindbot.medicalinformation.utils.VersionInfo;
import com.imindbot.medicalinformation.utils.VersionUtil;
import com.king.app.dialog.AppDialog;
import com.king.app.dialog.AppDialogConfig;
import com.king.app.updater.AppUpdater;
import com.king.app.updater.UpdateConfig;
import com.king.app.updater.callback.AppUpdateCallback;
import com.king.app.updater.callback.UpdateCallback;
import com.king.app.updater.http.OkHttpManager;

import java.io.File;

public class VersionUpdateActivity extends AppCompatActivity {

    private final Object mLock = new Object();

      private String mUrl = "https://gitlab.com/jenly1314/AppUpdater/-/raw/master/app/release/app-release.apk";

    private ActivityVersionUpdateBinding binding;

    private TextView tvProgress;
    private ProgressBar progressBar;

    private Toast toast;

    private AppUpdater mAppUpdater;

    private int version;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityVersionUpdateBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        try {
            PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
            int versionCode = pInfo.versionCode;  // 获取 versionCode
            String versionName = pInfo.versionName;  // 获取 versionName
            Log.d("AppVersion", "versionCode: " + versionCode + ", versionName: " + versionName);
        binding.version.setText("版本号:" + versionCode+"版本名字:"+versionName);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }



    }


    public Context getContext() {
        return this;
    }

    public void showToast(String text) {
        if (toast == null) {
            synchronized (mLock) {
                if (toast == null) {
                    toast = Toast.makeText(getContext(), text, Toast.LENGTH_SHORT);
                }
            }
        }
        toast.setText(text);
        toast.show();
    }

    /**
     * 简单一键后台升级
     */
    private void clickBtn1() {
//        mAppUpdater = new AppUpdater(getContext(), mUrl);
//        mAppUpdater.start();
        VersionInfoResponse.VersionData versionData  =  new VersionInfoResponse.VersionData();
        versionData.setVersionNumber("2.0.1");
        versionData.setUpdateContent("修复了bug");
        versionData.setDownloadUrl("1.0.1");

//        saveVersionInfo(versionData);
        VersionUtil.saveVersionInfo(getApplicationContext(), "3.0", "更新内容","下载地址");


        // 更新 UI
        updateUI();
    }

    /**
     * 一键下载并监听
     */
    private void clickBtn2() {
        UpdateConfig config = new UpdateConfig();
        config.setUrl(mUrl);
        config.addHeader("token", "xxxxxx");
        mAppUpdater = new AppUpdater(getContext(), config)
                .setHttpManager(OkHttpManager.getInstance())
                .setUpdateCallback(new UpdateCallback() {

                    @Override
                    public void onDownloading(boolean isDownloading) {
                        if (isDownloading) {
                            showToast("已经在下载中,请勿重复下载。");
                        } else {
//                            showToast("开始下载...");
                            View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_progress, null);
                            tvProgress = view.findViewById(R.id.tvProgress);
                            progressBar = view.findViewById(R.id.progressBar);
                            AppDialog.INSTANCE.showDialog(getContext(), view, false);
                        }
                    }

                    @Override
                    public void onStart(String url) {

                    }

                    @Override
                    public void onProgress(long progress, long total, boolean isChanged) {
                        if (isChanged) {
                            updateProgress(progress, total);
                        }
                    }

                    @Override
                    public void onFinish(File file) {
                        AppDialog.INSTANCE.dismissDialog();
                        showToast("下载完成");
                    }

                    @Override
                    public void onError(Exception e) {
                        AppDialog.INSTANCE.dismissDialog();
                        showToast("下载失败");
                    }

                    @Override
                    public void onCancel() {
                        AppDialog.INSTANCE.dismissDialog();
                        showToast("取消下载");
                    }
                });
        mAppUpdater.start();
    }

    private void updateProgress(long progress, long total) {
        if (tvProgress == null || progressBar == null) {
            return;
        }
        if (progress > 0) {
            int currProgress = (int) (progress * 1.0f / total * 100.0f);
            tvProgress.setText(getString(R.string.app_updater_progress_notification_content) + currProgress + "%");
            progressBar.setProgress(currProgress);
        } else {
            tvProgress.setText(getString(R.string.app_updater_start_notification_content));
        }

    }

    /**
     * 系统弹框升级
     */
    private void clickBtn3() {
        new AlertDialog.Builder(this)
                .setTitle("发现新版本")
                .setMessage("1、新增某某功能、\n2、修改某某问题、\n3、优化某某BUG、")
                .setPositiveButton("升级", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        mAppUpdater = new AppUpdater.Builder(getContext())
                                .setUrl(mUrl)
                                .setSupportCancelDownload(true)
                                .build()
                                .setUpdateCallback(new AppUpdateCallback() {
                                    @Override
                                    public void onStart(String url) {
                                        super.onStart(url);
                                        // 模仿系统自带的横幅通知效果
                                        AppDialogConfig config = new AppDialogConfig(getContext(), R.layout.dialog_heads_up);
                                        config.setStyleId(R.style.app_dialog_heads_up)
                                                .setWidthRatio(.95f)
                                                .setGravity(Gravity.TOP);
                                        AppDialog.INSTANCE.showDialog(getContext(), config);
                                        new CountDownTimer(2000, 500) {

                                            @Override
                                            public void onTick(long millisUntilFinished) {

                                            }

                                            @Override
                                            public void onFinish() {
                                                AppDialog.INSTANCE.dismissDialog();
                                            }
                                        }.start();
                                    }

                                    @Override
                                    public void onProgress(long progress, long total, boolean isChanged) {

                                    }

                                    @Override
                                    public void onFinish(File file) {
                                        showToast("下载完成");
                                    }
                                });
                        mAppUpdater.start();
                    }
                }).show();
    }

    /**
     * 简单弹框升级
     */
    private void clickBtn4() {
        AppDialogConfig config = new AppDialogConfig(getContext());
        config.setTitle("简单弹框升级")
                .setConfirm("升级")
                .setContent("1、新增某某功能、\n2、修改某某问题、\n3、优化某某BUG、")
                .setOnClickConfirm(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        mAppUpdater = new AppUpdater(getContext(), mUrl);
                        mAppUpdater.start();
                        AppDialog.INSTANCE.dismissDialog();
                    }
                });
        AppDialog.INSTANCE.showDialog(config);
    }

    /**
     * 简单自定义弹框升级
     */
    private void clickBtn5() {
        AppDialogConfig config = new AppDialogConfig(getContext(), R.layout.dialog);
        config.setConfirm("升级")
                .setHideCancel(true)
                .setTitle("简单自定义弹框升级")
                .setContent("1、新增某某功能、\n2、修改某某问题、\n3、优化某某BUG、")
                .setOnClickConfirm(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        mAppUpdater = new AppUpdater.Builder(getContext())
                                .setUrl(mUrl)
                                .build();
                        mAppUpdater.start();
                        AppDialog.INSTANCE.dismissDialog();
                    }
                });
        AppDialog.INSTANCE.showDialog(config);
    }

    /**
     * 自定义弹框,优先缓存升级
     */
    private void clickBtn6() {
        View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_custom, null);

        TextView tvTitle = view.findViewById(R.id.tvTitle);
        tvTitle.setText("自定义弹框升级");
        TextView tvContent = view.findViewById(R.id.tvContent);
        tvContent.setText("1、新增某某功能、\n2、修改某某问题、\n3、优化某某BUG、");

        View btnCancel = view.findViewById(R.id.btnCancel);
        btnCancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                AppDialog.INSTANCE.dismissDialog();
            }
        });
        View btnConfirm = view.findViewById(R.id.btnConfirm);
        btnConfirm.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mAppUpdater = new AppUpdater.Builder(getContext())
                        .setUrl(mUrl)
//                        .setApkMD5("3df5b1c1d2bbd01b4a7ddb3f2722ccca")// 支持MD5校验,如果缓存APK的MD5与此MD5相同,则直接取本地缓存安装,推荐使用MD5校验的方式
                        .setVersionCode(BuildConfig.VERSION_CODE)// 支持versionCode校验,设置versionCode之后,新版本versionCode相同的apk只下载一次,优先取本地缓存,推荐使用MD5校验的方式
                        .setFilename("AppUpdater.apk")
                        .setVibrate(true)
                        .build();
                mAppUpdater.setHttpManager(OkHttpManager.getInstance()).start();
                AppDialog.INSTANCE.dismissDialog();
            }
        });

        AppDialog.INSTANCE.showDialog(getContext(), view);
    }

    /**
     * 简单DialogFragment升级
     */
    private void clickBtn7() {
        AppDialogConfig config = new AppDialogConfig(getContext());
        config.setTitle("简单DialogFragment升级")
                .setConfirm("升级")
                .setContent("1、新增某某功能、\n2、修改某某问题、\n3、优化某某BUG、")
                .setOnClickConfirm(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        mAppUpdater = new AppUpdater.Builder(getContext())
                                .setUrl(mUrl)
                                .build();
                        mAppUpdater.setHttpManager(OkHttpManager.getInstance()) // 使用OkHttp的实现进行下载
                                .setUpdateCallback(new UpdateCallback() { // 更新回调
                                    @Override
                                    public void onDownloading(boolean isDownloading) {
                                        // 下载中:isDownloading为true时,表示已经在下载,即之前已经启动了下载;为false时,表示当前未开始下载,即将开始下载
                                    }

                                    @Override
                                    public void onStart(String url) {
                                        // 开始下载
                                    }

                                    @Override
                                    public void onProgress(long progress, long total, boolean isChanged) {
                                        // 下载进度更新:建议在isChanged为true时,才去更新界面的进度;因为实际的进度变化频率很高
                                    }

                                    @Override
                                    public void onFinish(File file) {
                                        // 下载完成
                                    }

                                    @Override
                                    public void onError(Exception e) {
                                        // 下载失败
                                    }

                                    @Override
                                    public void onCancel() {
                                        // 取消下载
                                    }
                                }).start();

                        AppDialog.INSTANCE.dismissDialogFragment(getSupportFragmentManager());
                    }
                });
        AppDialog.INSTANCE.showDialogFragment(getSupportFragmentManager(), config);

    }

    private void clickCancel() {
        if (mAppUpdater != null) {
            mAppUpdater.stop();
        }

    }

    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn1:
                clickBtn1();
                break;
            case R.id.btn2:
                clickBtn2();
                break;
            case R.id.btn3:
                clickBtn3();
                break;
            case R.id.btn4:
                clickBtn4();
                break;
            case R.id.btn5:
                clickBtn5();
                break;
            case R.id.btn6:
                clickBtn6();
                break;
            case R.id.btn7:
                clickBtn7();
                break;
            case R.id.btnCancel:
                clickCancel();
                break;
        }
    }

    //存储远程版本信息
    private void saveVersionInfo(VersionInfoResponse.VersionData versionData) {
        SharedPreferences sharedPreferences = getSharedPreferences("app_version", MODE_PRIVATE);
        SharedPreferences.Editor editor = sharedPreferences.edit();

        // 存储远程版本信息
        editor.putString("version_number", versionData.getVersionNumber());
        editor.putString("update_content", versionData.getUpdateContent());
        editor.putString("download_url", versionData.getDownloadUrl());
//        editor.putLong("version_digit", versionData.getVersionDigit());
        editor.apply();
    }

    // 获取本地版本号
    private String getLocalVersion() {
        try {
            PackageManager pm = getPackageManager();
            PackageInfo pi = pm.getPackageInfo(getPackageName(), 0);
            return pi.versionName;  // 获取应用版本号
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
            return "0.0.0";
        }
    }

    private void updateUI() {

//        SharedPreferences sharedPreferences = getSharedPreferences("app_version", MODE_PRIVATE);
//        String newVersion = sharedPreferences.getString("version_number", "未获取到版本号");  // 默认值是 "未获取到版本号"
        VersionInfo versionInfo = VersionUtil.getVersionInfo(getApplicationContext());

        binding.version.setText("版本号:" + versionInfo);
    }





}

4.编写 activity_version_update.xml

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_height="match_parent"
    tools:context=".ui.test.VersionUpdateActivity">

    <Button
        android:id="@+id/btn1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:layout_marginTop="40dp"
        android:text="简单一键后台升级"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        style="@style/OnClick"/>
    <Button
        android:id="@+id/btn2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:layout_marginTop="10dp"
        android:text="一键监听进度升级"
        app:layout_constraintTop_toBottomOf="@+id/btn1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        style="@style/OnClick"/>
    <Button
        android:id="@+id/btn3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:layout_marginTop="10dp"
        android:text="系统弹框升级"
        app:layout_constraintTop_toBottomOf="@+id/btn2"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        style="@style/OnClick"/>
    <Button
        android:id="@+id/btn4"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:layout_marginTop="10dp"
        android:text="简单弹框升级"
        app:layout_constraintTop_toBottomOf="@+id/btn3"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        style="@style/OnClick"/>
    <Button
        android:id="@+id/btn5"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:layout_marginTop="10dp"
        android:text="简单自定义弹框升级"
        app:layout_constraintTop_toBottomOf="@+id/btn4"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        style="@style/OnClick"/>

    <Button
        android:id="@+id/btn6"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:layout_marginTop="10dp"
        android:text="自定义弹框,优先缓存升级"
        app:layout_constraintTop_toBottomOf="@+id/btn5"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        style="@style/OnClick"/>

    <Button
        android:id="@+id/btn7"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:layout_marginTop="10dp"
        android:text="简单DialogFragment升级"
        app:layout_constraintTop_toBottomOf="@+id/btn6"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        style="@style/OnClick"/>

    <Button
        android:id="@+id/btnCancel"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:layout_marginTop="10dp"
        android:text="取消下载"
        app:layout_constraintTop_toBottomOf="@+id/btn7"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        style="@style/OnClick"/>


    <TextView
        android:id="@+id/version"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:textSize="25sp"
        android:text="版本号:"
        tools:ignore="MissingConstraints">

    </TextView>



</androidx.constraintlayout.widget.ConstraintLayout>

效果如下

5.color文件夹下 /src/main/res/color/btn_cancel_color_selector.xml

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true" android:color="@color/text_666"/>
    <item android:state_pressed="false" android:color="@color/text_999"/>
</selector>

6. app/src/main/res/drawable-v24/ic_launcher_foreground.xml

xml 复制代码
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:width="108dp"
    android:height="108dp"
    android:viewportHeight="108"
    android:viewportWidth="108">
    <path
        android:fillType="evenOdd"
        android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
        android:strokeColor="#00000000"
        android:strokeWidth="1">
        <aapt:attr name="android:fillColor">
            <gradient
                android:endX="78.5885"
                android:endY="90.9159"
                android:startX="48.7653"
                android:startY="61.0927"
                android:type="linear">
                <item
                    android:color="#ffffff"
                    android:offset="0.0" />
                <item
                    android:color="#ffffff"
                    android:offset="1.0" />
            </gradient>
        </aapt:attr>
    </path>
    <path
        android:fillColor="#FFFFFF"
        android:fillType="nonZero"
        android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
        android:strokeColor="#00000000"
        android:strokeWidth="1" />
</vector>

7. app/src/main/res/drawable-v24/ic_launcher_foreground.xml

xml 复制代码
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:width="108dp"
    android:height="108dp"
    android:viewportHeight="108"
    android:viewportWidth="108">
    <path
        android:fillType="evenOdd"
        android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
        android:strokeColor="#00000000"
        android:strokeWidth="1">
        <aapt:attr name="android:fillColor">
            <gradient
                android:endX="78.5885"
                android:endY="90.9159"
                android:startX="48.7653"
                android:startY="61.0927"
                android:type="linear">
                <item
                    android:color="#ffffff"
                    android:offset="0.0" />
                <item
                    android:color="#ffffff"
                    android:offset="1.0" />
            </gradient>
        </aapt:attr>
    </path>
    <path
        android:fillColor="#FFFFFF"
        android:fillType="nonZero"
        android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
        android:strokeColor="#00000000"
        android:strokeWidth="1" />
</vector>

8. app/src/main/res/drawable/btn_confirm_selector.xml

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_pressed="false">
        <shape>
            <solid android:color="@color/colorAccent"/>
            <corners android:radius="20dp"/>
        </shape>
    </item>

    <item android:state_pressed="true">
        <shape>
            <solid android:color="@color/colorPrimaryDark"/>
            <corners android:radius="20dp"/>
        </shape>
    </item>

</selector>

9 . app/src/main/res/drawable/dialog_title_bg.xml

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >

    <solid android:color="#F5F5F5" />

    <corners
        android:bottomLeftRadius="0dp"
        android:bottomRightRadius="0dp"
        android:topLeftRadius="10dp"
        android:topRightRadius="10dp" />

</shape>

10. app/src/main/res/layout/dialog.xml

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:layout_margin="30dp"
    android:background="@drawable/app_dialog_bg"
    android:orientation="vertical">
    <TextView
        android:id="@+id/tvDialogTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:padding="10dp"
        android:lines="1"
        android:textSize="16sp"
        android:background="@drawable/dialog_title_bg"
        android:text="@string/app_dialog_title"/>
    <TextView
        android:id="@+id/tvDialogContent"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp"
        android:textSize="@dimen/app_dialog_content_text_size"
        android:textColor="@color/app_dialog_content_color"
        android:layout_marginBottom="16dp"
        android:lineSpacingMultiplier="1.2" />
    <include layout="@layout/app_dialog_line_h"/>
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal">
        <Button
            android:id="@+id/btnDialogCancel"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:paddingTop="10dp"
            android:paddingBottom="10dp"
            android:layout_weight="1"
            android:text="@string/app_dialog_cancel"
            android:textSize="18sp"
            android:textColor="@color/app_dialog_button_color_selector"
            android:background="?android:attr/selectableItemBackground"/>
        <include
            android:id="@+id/line"
            layout="@layout/app_dialog_line_v"/>
        <Button
            android:id="@+id/btnDialogConfirm"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:paddingTop="10dp"
            android:paddingBottom="10dp"
            android:layout_weight="1"
            android:text="@string/app_dialog_confirm"
            android:textSize="18sp"
            android:textColor="@color/app_dialog_button_color_selector"
            android:background="?android:attr/selectableItemBackground"/>
    </LinearLayout>

</LinearLayout>

11. app/src/main/res/layout/dialog_custom.xml

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:layout_margin="30dp"
    android:background="@drawable/app_dialog_bg"
    android:orientation="vertical">
    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:padding="6dp"
        android:lines="1"
        android:textSize="16sp" />
    <TextView
        android:id="@+id/tvContent"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp"
        android:lineSpacingMultiplier="1" />
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginTop="10dp"
        android:layout_marginRight="20dp"
        android:layout_marginBottom="10dp"
        android:gravity="center_horizontal">
        <TextView
            android:id="@+id/btnConfirm"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingTop="10dp"
            android:paddingBottom="10dp"
            android:gravity="center"
            android:layout_weight="1"
            android:text="升级"
            android:textSize="16sp"
            android:textColor="@color/white"
            android:background="@drawable/btn_confirm_selector"/>
        <TextView
            android:id="@+id/btnCancel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="2dp"
            android:paddingTop="10dp"
            android:paddingBottom="10dp"
            android:paddingLeft="16dp"
            android:paddingRight="16dp"
            android:gravity="center"
            android:layout_weight="1"
            android:text="@string/ignore"
            android:textSize="16sp"
            android:textColor="@color/btn_cancel_color_selector" />
    </LinearLayout>

</LinearLayout>

12. app/src/main/res/layout/dialog_heads_up.xml

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="16dp"
    android:gravity="center_vertical"
    android:background="@drawable/app_dialog_bg">
    <ImageView
        android:id="@+id/ivIcon"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:src="@mipmap/ic_launcher"
        android:layout_marginRight="4dp"
        android:layout_marginEnd="4dp"
        android:contentDescription="@null"/>
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_toRightOf="@+id/ivIcon"
        android:layout_toEndOf="@+id/ivIcon"
        android:textSize="14sp"
        android:textColor="@color/app_dialog_title_color"
        android:text="@string/app_updater_start_notification_content"/>

</RelativeLayout>

13. app/src/main/res/layout/dialog_progress.xml

java 复制代码
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="16dp"
    android:gravity="center_vertical"
    android:background="@drawable/app_dialog_bg">
    <ImageView
        android:id="@+id/ivIcon"
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:src="@mipmap/ic_launcher"
        android:layout_marginRight="4dp"
        android:layout_marginEnd="4dp"
        android:contentDescription="@null"/>
    <TextView
        android:id="@+id/tvDialogTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@+id/ivIcon"
        android:layout_toEndOf="@+id/ivIcon"
        android:textSize="14sp"
        android:textColor="@color/app_dialog_title_color"
        android:text="@string/app_updater_progress_notification_title"/>
    <TextView
        android:id="@+id/tvProgress"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/tvDialogTitle"
        android:layout_marginTop="10dp"
        android:text="@string/app_updater_start_notification_content"
        android:textSize="14sp"
        android:textColor="@color/app_dialog_content_color"
        android:lineSpacingMultiplier="1.2" />
    <ProgressBar
        android:id="@+id/progressBar"
        android:layout_width="match_parent"
        android:layout_height="6dp"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp"
        android:max="100"
        android:layout_below="@+id/tvProgress"
        style="@style/Widget.AppCompat.ProgressBar.Horizontal"/>
</RelativeLayout>

14. app/src/main/res/values/colors.xml

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#5588FF</color>
    <color name="colorPrimaryDark">#4B7CFD</color>
    <color name="colorAccent">#5588FF</color>

    <color name="ic_launcher_background">#FFFFFF</color>

    <color name="white">#FFFFFF</color>

    <color name="text_666">#666666</color>
    <color name="text_999">#999999</color>


</resources>

15. app/src/main/res/values/strings.xml

xml 复制代码
<resources>
    <string name="app_name">AppUpdater</string>

    <string name="ignore">忽略</string>

</resources>

16. app/src/main/res/values/styles.xml

xml 复制代码
<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="android:textAllCaps">false</item>
    </style>


    <style name="app_dialog_heads_up" parent="android:style/Theme.Dialog">
        <item name="android:windowFrame">@null</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:backgroundDimEnabled">false</item>
        <item name="android:windowAnimationStyle">@style/app_dialog_top_animation</item>
    </style>

    <style name="OnClick">
        <item name="android:clickable">true</item>
        <item name="android:onClick">onClick</item>
    </style>
</resources>

四、具体实战

Java 复制代码
private VersionUpgradeModel versionUpgradeModel;

 // 初始化 ViewModel
versionUpgradeModel = new ViewModelProvider(this).get(VersionUpgradeModel.class);

   //检查新版本
        binding.checkNewVersion.setOnClickListener(v -> {
            if (!isRequestInProgress) {
                isRequestInProgress = true; // 请求开始,设置标志位
                updateVersion(); // 发送请求获取版本信息
            }
        });

          //tv_version 版本哈
        binding.tvVersion.setText("App版本:"+getLocalVersion());
        


  versionUpgradeModel.getVersionInfoResponse().observe(this, new Observer<VersionInfoResponse>() {
            @Override
            public void onChanged(@Nullable VersionInfoResponse versionInfoResponse) {
                if (versionInfoResponse != null) {
                    if (versionInfoResponse.code == 200) {
                        Log.i(GetTokenUtil.COMMON_TAG, "版本信息:" + versionInfoResponse);
//                        versionUpdate(versionInfoResponse.getData().getVersionNumber(), versionInfoResponse.getData().getUpdateContent(),versionInfoResponse.getData().getDownloadUrl());
                        // 进行版本比较并决定是否更新
                        checkForUpdate(versionInfoResponse.getData());
                    } else {
                        // 如果接口返回失败,可以显示错误提示
//                        Toast.makeText(AddHealthCheckupActivity.this, "获取版本信息失败", Toast.LENGTH_SHORT).show();
                    }
                }
                isRequestInProgress = false; // 请求结束,重置标志位
             }
        });

   //获取本地版本信息并进行比较
     private void checkForUpdate(VersionInfoResponse.VersionData versionData) {
        try {
            PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
            int versionCode = pInfo.versionCode;  // 获取 versionCode
            String versionName = pInfo.versionName;  // 获取 versionName
            Log.d("AppVersion", "versionCode: " + versionCode + ", versionName: " + versionName);
            if (VersionComparator.compare(versionData.versionNumber,versionName )) {
                // 本地版本小于远程版本,提示更新
                versionUpdate(versionData.getVersionNumber(), versionData.getUpdateContent(), versionData.getDownloadUrl());
            } else {
                // 本地版本已是最新,无需更新
                ToastUtils.showSuccess("当前已是最新版本");
            }
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
    }
    

       private void versionUpdate(String versionNumber, String versionContent, String apkUrl) {
        // 确保Activity处于有效状态
        if (!isFinishing() && !isDestroyed()) {
            // 使用Activity的上下文
            AppDialogConfig config = new AppDialogConfig(SystemSettingActivity.this);
            config.setTitle("版本升级提示")
                    .setConfirm("升级") // 旧版本使用 setOk
//                    .setContent("版本号: " + versionNumber + "\n" + "\n"+ "App版本更新内容: "+"\n"  + UpdateContentExtractor.extractAndFormatUpdateContent(versionContent))
                    .setContent( "App版本更新内容: "+"\n"  + UpdateContentExtractor.extractAndFormatUpdateContent(versionContent)+"\n"+"\n"+"版本号: " + versionNumber)
                    .setOnClickConfirm(new View.OnClickListener() { // 旧版本使用 setOnClickOk
                        @Override
                        public void onClick(View v) {
                            UpdateConfig config = new UpdateConfig();
                            config.setUrl(apkUrl);
                            config.addHeader("token", "xxxxxx");
                            mAppUpdater = new AppUpdater(getContext(), config)
                                    .setHttpManager(OkHttpManager.getInstance())
                                    .setUpdateCallback(new UpdateCallback() {

                                        @Override
                                        public void onDownloading(boolean isDownloading) {
                                            if (isDownloading) {
                                                showToast("已经在下载中,请勿重复下载。");
                                            } else {
                                                View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_progress, null);
                                                tvProgress = view.findViewById(R.id.tvProgress);
                                                progressBar = view.findViewById(R.id.progressBar);
                                                AppDialog.INSTANCE.showDialog(getContext(), view, false);
                                            }
                                        }

                                        @Override
                                        public void onStart(String url) {

                                        }

                                        @Override
                                        public void onProgress(long progress, long total, boolean isChanged) {
                                            if (isChanged) {
                                                updateProgress(progress, total);
                                            }
                                        }

                                        @Override
                                        public void onFinish(File file) {
                                            AppDialog.INSTANCE.dismissDialog();
//                                            showToast("下载完成");
                                            ToastUtils.showSuccess("下载完成");
                                        }

                                        @Override
                                        public void onError(Exception e) {
                                            AppDialog.INSTANCE.dismissDialog();
//                                            showToast("下载失败");
                                            ToastUtils.showError("下载失败");
                                        }

                                        @Override
                                        public void onCancel() {
                                            AppDialog.INSTANCE.dismissDialog();
                                            ToastUtils.showInfo("取消下载");
//                                            showToast("取消下载");
                                        }
                                    });
                            mAppUpdater.start();


                            // 关闭对话框
                            AppDialog.INSTANCE.dismissDialog();
                        }
                    });

            // 显示Dialog
            AppDialog.INSTANCE.showDialog(SystemSettingActivity.this, config);
        }
    }

       public void updateVersion( ) {
        updateVersionExecutor.execute(new Runnable() {
            @Override
            public void run() {
                Log.i(GetTokenUtil.COMMON_TAG, "开始查询版本信息:" );
                versionUpgradeModel.checkVersion();
            }
        });
    }


      // 获取本地版本号
    private String getLocalVersion() {
        try {
            PackageManager pm = getPackageManager();
            PackageInfo pi = pm.getPackageInfo(getPackageName(), 0);
            return pi.versionName;  // 获取应用版本号
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
            return "0.0.0";
        }
    }

     //升级进度条
    private void updateProgress(long progress, long total) {
        if (tvProgress == null || progressBar == null) {
            return;
        }
        if (progress > 0) {
            int currProgress = (int) (progress * 1.0f / total * 100.0f);
            tvProgress.setText(getString(R.string.app_updater_progress_notification_content) + currProgress + "%");
            progressBar.setProgress(currProgress);
        } else {
            tvProgress.setText(getString(R.string.app_updater_start_notification_content));
        }

    } 
相关推荐
Victor35636 分钟前
Redis(25)Redis的RDB持久化的优点和缺点是什么?
后端
Victor35637 分钟前
Redis(24)如何配置Redis的持久化?
后端
海的天空16612 小时前
Flutter旧版本升级-> Android 配置、iOS配置
android·flutter·ios
ningqw8 小时前
SpringBoot 常用跨域处理方案
java·后端·springboot
你的人类朋友8 小时前
vi编辑器命令常用操作整理(持续更新)
后端
胡gh8 小时前
简单又复杂,难道只能说一个有箭头一个没箭头?这种问题该怎么回答?
javascript·后端·面试
一只叫煤球的猫9 小时前
看到同事设计的表结构我人麻了!聊聊怎么更好去设计数据库表
后端·mysql·面试
uzong9 小时前
技术人如何对客做好沟通(上篇)
后端
颜如玉10 小时前
Redis scan高位进位加法机制浅析
redis·后端·开源
Moment10 小时前
毕业一年了,分享一下我的四个开源项目!😊😊😊
前端·后端·开源