🐳 《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));
        }

    } 
相关推荐
IT技术图谱2 分钟前
【绝非标题党】Lifecycle的原理及使用,看这篇就够了
android·android jetpack
鱼洗竹3 分钟前
协程异常处理(二)
android
顾林海4 分钟前
深度解析TreeSet工作原理
android·java·面试
缘来的精彩12 分钟前
Kotlin FragmentTransaction多容器管理多个fragment
android·kotlin·transaction·fragment
uhakadotcom12 分钟前
Cloudflare RealtimeKit:让实时应用开发更简单
后端·面试·github
uhakadotcom14 分钟前
分享微软刚开源的Retina:云原生网络可观察性平台
后端·面试·github
黄林晴19 分钟前
鸿蒙的卓易通,让我踩了一次坑
android
uhakadotcom23 分钟前
Nuclei扫描器:快速、可定制的漏洞检测引擎
后端·面试·github
猿java23 分钟前
为什么说缓存是把双刃剑?
java·后端·面试
AronTing24 分钟前
11-Java并发编程终极指南:ThreadLocal与并发设计模式实战
后端