App Center 分发 - Android 应用内更新

重要

Visual Studio App Center 计划于 2025 年 3 月 31 日停用。 虽然可以继续使用 Visual Studio App Center,直到它完全停用,但你可以考虑迁移到几个建议的替代方法。

详细了解支持时间线和替代方案。

当你通过 App Center 分发应用时,App Center 分发将允许用户安装新版本的应用。 新版本的应用可用后,SDK 将向用户显示一个更新对话框,以下载或推迟新版本。 选择更新后,SDK 将开始更新应用程序。

警告

Google Play 会将应用内更新代码视为恶意行为,即使运行时未使用该代码。 按照 本部分中 的指示使用分发 SDK 的变体,或在将应用提交到 Google Play 之前完全删除包含应用内更新代码的分发 SDK。 如果不这样做,可能会导致不符合要求,并从 Google Play 中删除该应用。

注意

如果运行的是自动 UI 测试,则启用的应用内更新将阻止自动化 UI 测试,因为它们将尝试针对 App Center 后端进行身份验证。 建议不要为 UI 测试启用 App Center 分发。

将应用内更新添加到应用

如果尚未在应用程序中设置并启动 SDK,请按照 入门 部分进行操作。

1. 添加 App Center 分发模块

App Center SDK 采用模块化方法设计 - 开发人员只需集成他们感兴趣的服务的模块。

  1. 打开项目的应用级别 build.gradle 文件 (app/build.gradle) ,并在 后面 apply plugin添加以下行。

    dependencies {
       def appCenterSdkVersion = '5.0.4'
       implementation "com.microsoft.appcenter:appcenter-distribute:${appCenterSdkVersion}"
    }
    

    注意

    如果 Android Gradle 插件的版本低于 3.0.0,则需要将 实现 替换为 compile

  2. 保存 build.gradle 文件,并确保在 Android Studio 中触发 Gradle 同步。

  3. DownloadManager 用于下载更新。 App Center SDK 强制实施 TLS 1.2 以提高安全性。

2. 启动 App Center 分发

若要使用 App Center,请选择要使用的模块 () 。 默认情况下,不会启动任何模块,必须在启动 SDK 时显式调用每个模块。

将 Distribut 类添加到 AppCenter.start() 方法以启动 App Center Distribut 服务。

AppCenter.start(getApplication(), "{Your App Secret}", Distribute.class);
AppCenter.start(application, "{Your App Secret}", Distribute::class.java)

请确保在上面的代码示例中已 {Your App Secret} 将 替换为应用机密。 向 方法添加对 Distributestart() 的引用后,Android Studio 会自动建议所需的导入语句,但如果看到无法识别类名的错误,请将以下行添加到活动类中的 import 语句:

import com.microsoft.appcenter.AppCenter;
import com.microsoft.appcenter.distribute.Distribute;
import com.microsoft.appcenter.AppCenter
import com.microsoft.appcenter.distribute.Distribute

注意

Android 10 或更高版本对从后台启动活动有限制。 请参阅有关 从后台启动活动的限制的文章。

注意

在 Android 10 (Go 版本) 上运行的应用无法接收 SYSTEM_ALERT_WINDOW 权限。 请参阅有关 Go 设备上SYSTEM_ALERT_WINDOW的文章。

注意

从 Android 11 开始, ACTION_MANAGE_OVERLAY_PERMISSION 意向始终将用户带到顶级“设置”屏幕,用户可以在此屏幕中授予或撤销 SYSTEM_ALERT_WINDOW 应用的权限。 请参阅有关 Android 11 中权限更新的文章。

准备 Google Play 版本

Google Play 会将应用内更新代码视为恶意行为,即使运行时未使用该代码。 按照本部分中的指示使用分发 SDK 的变体,或在将应用提交到 Google Play 之前完全删除包含应用内更新代码的分发 SDK。 如果不这样做,可能会导致不符合要求,并从 Google Play 中删除该应用。 为了简化操作,我们提供了 App Center 分发 SDK 版本,其中包含基元 API,因此唯一的更改是进行依赖项交换。

  1. 打开项目的应用级别 build.gradle 文件 (app/build.gradle) 。

  2. 通过添加产品风格配置生成变体:

    android {
        flavorDimensions "distribute"
        productFlavors {
            appCenter {
                dimension "distribute"
            }
            googlePlay {
                dimension "distribute"
            }
        }
    }
    
  3. 修改依赖项块以根据产品风格使用不同的依赖项:

    dependencies {
        def appCenterSdkVersion = "5.0.4"
        appCenterImplementation "com.microsoft.appcenter:appcenter-distribute:${appCenterSdkVersion}"
        googlePlayImplementation "com.microsoft.appcenter:appcenter-distribute-play:${appCenterSdkVersion}"
    }
    
  4. 保存 build.gradle 文件,并确保在 Android Studio 中触发 Gradle 同步。

  5. 可以在“ 生成 > ”下拉菜单 或工具窗口栏中的“ 生成变体 ”中更改生成变体。

可以在 Android 文档中阅读有关配置生成变体的详细信息。

使用专用通讯组

默认情况下,分发使用公共通讯组。 如果要使用专用通讯组,则需要通过 setUpdateTrack API 显式设置它。

Distribute.setUpdateTrack(UpdateTrack.PRIVATE);
Distribute.setUpdateTrack(UpdateTrack.PRIVATE)

注意

默认值为 UpdateTrack.PUBLIC。 只能在方法调用之前 AppCenter.start 调用此方法。 当应用程序进程重启时,不会保留对更新跟踪所做的更改,因此,如果方法在调用之前 AppCenter.start 不始终调用,默认情况下,它将是公开的。

当应用在前台 () AppCenter.startDistribute.setUpdateTrack(UpdateTrack.PRIVATE);,将打开浏览器窗口对用户进行身份验证。 所有后续更新检查都将在专用轨道上获取最新版本。

如果用户处于 专用通道上,则意味着身份验证成功后,他们将从其所属的任何专用通讯组获取最新版本。 如果用户处于 公共轨道上,则意味着他们将从任何公共通讯组获取最新版本。

禁用更新的自动检查

默认情况下,SDK 会自动检查新版本:

  • 应用程序启动时。
  • 应用程序在转到后台后进入前台时。
  • 启用分发模块(如果以前禁用)。

如果要手动检查新版本,可以禁用自动更新检查。 为此,请在 SDK 启动之前调用以下方法:

Distribute.disableAutomaticCheckForUpdate();
Distribute.disableAutomaticCheckForUpdate()

注意

在调用方法之前 AppCenter.start ,必须调用此方法。

然后,可以使用 API checkForUpdate ,该 API 将在以下部分进行介绍。

手动检查更新

Distribute.checkForUpdate();
Distribute.checkForUpdate()

这会向 App Center 发送请求,并在有新版本可用时显示更新对话框。

注意

即使启用了自动更新,更新调用的手动检查也有效。 如果已执行其他检查,则会忽略更新的手动检查。 如果用户已推迟更新 (,则不会处理更新的手动检查,除非最新版本是强制更新) 。

自定义或本地化应用内更新对话框

1. 自定义或本地化文本

如果要更改或本地化更新对话框中显示的文本,可以轻松提供自己的资源字符串。 查看 此资源文件中的字符串文件。 使用相同的字符串名称/键,并指定要在你自己的应用资源文件中的对话框中反映的本地化值。

2.自定义更新对话框

可以通过实现 DistributeListener 接口来自定义默认更新对话框的外观。 在调用 AppCenter.start 之前,需要注册侦听器,如以下示例所示:

Distribute.setListener(new MyDistributeListener());
AppCenter.start(...);
Distribute.setListener(MyDistributeListener())
AppCenter.start(...)

下面是侦听器实现的示例,该实现将 SDK 对话框替换为自定义对话:

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.net.Uri;

import com.microsoft.appcenter.distribute.Distribute;
import com.microsoft.appcenter.distribute.DistributeListener;
import com.microsoft.appcenter.distribute.ReleaseDetails;
import com.microsoft.appcenter.distribute.UpdateAction;

public class MyDistributeListener implements DistributeListener {

    @Override
    public boolean onReleaseAvailable(Activity activity, ReleaseDetails releaseDetails) {

        // Look at releaseDetails public methods to get version information, release notes text or release notes URL
        String versionName = releaseDetails.getShortVersion();
        int versionCode = releaseDetails.getVersion();
        String releaseNotes = releaseDetails.getReleaseNotes();
        Uri releaseNotesUrl = releaseDetails.getReleaseNotesUrl();

        // Build our own dialog title and message
        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(activity);
        dialogBuilder.setTitle("Version " + versionName + " available!"); // you should use a string resource instead, this is just a simple example
        dialogBuilder.setMessage(releaseNotes);

        // Mimic default SDK buttons
        dialogBuilder.setPositiveButton(com.microsoft.appcenter.distribute.R.string.appcenter_distribute_update_dialog_download, new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {

                // This method is used to tell the SDK what button was clicked
                Distribute.notifyUpdateAction(UpdateAction.UPDATE);
            }
        });

        // We can postpone the release only if the update isn't mandatory
        if (!releaseDetails.isMandatoryUpdate()) {
            dialogBuilder.setNegativeButton(com.microsoft.appcenter.distribute.R.string.appcenter_distribute_update_dialog_postpone, new DialogInterface.OnClickListener() {

                @Override
                public void onClick(DialogInterface dialog, int which) {

                    // This method is used to tell the SDK what button was clicked
                    Distribute.notifyUpdateAction(UpdateAction.POSTPONE);
                }
            });
        }
        dialogBuilder.setCancelable(false); // if it's cancelable you should map cancel to postpone, but only for optional updates
        dialogBuilder.create().show();

        // Return true if you're using your own dialog, false otherwise
        return true;
    }
    
    @Override
    public void onNoReleaseAvailable(Activity activity) {
        Toast.makeText(activity, activity.getString(R.string.no_updates_available), Toast.LENGTH_LONG).show();
    }
}
import android.app.Activity
import android.app.AlertDialog
import com.microsoft.appcenter.distribute.Distribute
import com.microsoft.appcenter.distribute.DistributeListener
import com.microsoft.appcenter.distribute.ReleaseDetails
import com.microsoft.appcenter.distribute.UpdateAction

class MyDistributeListener : DistributeListener {

    override fun onReleaseAvailable(activity: Activity, releaseDetails: ReleaseDetails): Boolean {

        // Look at releaseDetails public methods to get version information, release notes text or release notes URL
        val versionName = releaseDetails.shortVersion
        val versionCode = releaseDetails.version
        val releaseNotes = releaseDetails.releaseNotes
        val releaseNotesUrl = releaseDetails.releaseNotesUrl

        // Build our own dialog title and message
        val dialogBuilder = AlertDialog.Builder(activity)
        dialogBuilder.setTitle("Version $versionName available!") // you should use a string resource instead, this is just a simple example
        dialogBuilder.setMessage(releaseNotes)

        // Mimic default SDK buttons
        dialogBuilder.setPositiveButton(
            com.microsoft.appcenter.distribute.R.string.appcenter_distribute_update_dialog_download
        ) { dialog, which ->
            // This method is used to tell the SDK what button was clicked
            Distribute.notifyUpdateAction(UpdateAction.UPDATE)
        }

        // We can postpone the release only if the update isn't mandatory
        if (!releaseDetails.isMandatoryUpdate) {
            dialogBuilder.setNegativeButton(
                com.microsoft.appcenter.distribute.R.string.appcenter_distribute_update_dialog_postpone
            ) { dialog, which ->
                // This method is used to tell the SDK what button was clicked
                Distribute.notifyUpdateAction(UpdateAction.POSTPONE)
            }
        }
        dialogBuilder.setCancelable(false) // if it's cancelable you should map cancel to postpone, but only for optional updates
        dialogBuilder.create().show()

        // Return true if you're using your own dialog, false otherwise
        return true
    }

    override fun onNoReleaseAvailable(activity: Activity) {
        Toast.makeText(activity, activity.getString(R.string.no_updates_available), Toast.LENGTH_LONG).show()
    }
}

如示例所示,如果侦听器返回 true,则必须调用 Distribute.notifyUpdateAction(UpdateAction.UPDATE);Distribute.notifyUpdateAction(UpdateAction.POSTPONE);

如果不调用 notifyUpdateAction,回调将在每次活动更改时重复。

如果在将用户操作通知到 SDK 之前活动发生更改,则可以使用同一版本再次调用侦听器。

需要此行为来涵盖以下方案:

  • 应用程序会发送到后台 (,例如按 HOME) 然后在其他活动中恢复。
  • 你的活动由另一个活动覆盖,而不离开应用程序 (如单击一些通知) 。
  • 其他类似方案。

在这种情况下,托管对话框的活动可能会在没有用户交互的情况下被替换。 因此,SDK 再次调用侦听器,以便可以还原自定义对话框。

如果 SDK 检查更新,但找不到比当前使用的 onNoReleaseAvailable 更新,则会调用 from DistributeListener 接口回调。 这样,就可以在此类方案中执行自定义代码。 上面的示例演示如何在找不到任何更新时显示 Toast 消息。

在运行时启用或禁用 App Center Distribute

可以在运行时启用和禁用 App Center Distribute。 如果禁用它,SDK 不会提供任何应用内更新功能,但你仍然可以在 App Center 门户中使用分发服务。

Distribute.setEnabled(false);
Distribute.setEnabled(false)

若要再次启用 App Center Distribute,请使用同一 API,但将 作为参数传递 true

Distribute.setEnabled(true);
Distribute.setEnabled(true)

在应用程序启动时,状态将保留在设备的存储中。

此 API 是异步的,可以在 App Center 异步 API 指南中详细了解此 API。

注意

只有在启动之后 Distribute 才能使用此方法。

检查是否启用了 App Center 分发

如果启用或未启用 App Center Distribute,还可以检查:

Distribute.isEnabled();
Distribute.isEnabled()

此 API 是异步的,可以在 App Center 异步 API 指南中详细了解此 API。

注意

此方法只能在启动后 Distribute 使用,它将始终在启动之前返回 false

为调试版本启用应用内更新

默认情况下,App Center 仅对发布版本启用应用内更新。

若要在调试版本中启用应用内更新,请在 之前 AppCenter.start调用以下方法:

Distribute.setEnabledForDebuggableBuild(true);
Distribute.setEnabledForDebuggableBuild(true)

注意

此方法仅影响调试生成,对发布版本没有影响。 调试生成意味着标志 android:debuggable 设置为 true (通常由 gradle 预定义的调试生成变体) 自动设置。 否则,这是发布版本。

应用内更新的工作原理是什么?

注意

若要运行应用内更新,应从链接下载应用内部版本。 如果从 IDE 安装或手动安装,它将不起作用。

应用内更新功能的工作原理如下:

  1. 默认情况下,此功能仅适用于使用 App Center 分发服务分发的 release 版本 () 。

  2. 集成 SDK、生成应用的发布版本并上传到 App Center 后,该通讯组中的用户将收到有关新版本的电子邮件通知。

  3. 当每个用户在其电子邮件中打开链接时,该应用程序将安装在其设备上。 请务必使用电子邮件链接进行安装 - 我们不支持旁加载。 从链接下载应用程序时,SDK 会将 cookie 中的重要信息保存到检查以供以后更新,否则 SDK 没有该密钥信息。

  4. 如果应用程序将跟踪设置为专用,浏览器将打开以对用户进行身份验证并启用应用内更新。 只要身份验证信息保持有效,浏览器就不会再次打开,即使稍后切换回公共轨道并再次切换回专用。 如果浏览器身份验证成功,用户会自动重定向回应用程序。 如果曲目是公共 (这是默认) ,则直接执行下一步。

  5. 新版本的应用显示应用内更新对话框,要求用户在以下情况更新应用程序:

    • 或 的较高值versionCode
    • 的等值 versionCode ,但 值 versionName不同。

提示

如果再次上传同一 APK,对话框 将不会 显示,因为版本相同。

如何实现测试应用内更新?

需要将发布版本上传到 (,这些版本使用 App Center SDK 的分发模块) 到 App Center Portal 来测试应用内更新,每次都会增加版本号。

  1. 在 App Center 门户中创建应用(如果尚未创建)。
  2. 创建新的通讯组并将其命名,以便可以识别它用于测试应用内更新功能。
  3. 将自己 (或要包含在应用内更新功能测试) 中的所有人员。 为此,请使用新的或丢弃的电子邮件地址,该地址未用于 App Center 上的该应用。 这可确保你的体验接近于真实测试人员的体验。
  4. 创建应用的新内部版本,其中包含 App Center Distribute 并包含如上所述的设置逻辑。 如果组是专用组,在开始使用 setUpdateTrack API 之前,不要忘记设置专用应用内更新跟踪。
  5. 单击门户中的“ 分发新版本 ”按钮,然后上传应用的内部版本。
  6. 上传完成后,单击“下一步”,然后选择创建为该应用分发的目标通讯组
  7. 查看分发并将生成分发到应用内测试组。
  8. 该组中人员将收到成为应用测试人员的邀请。 接受邀请后,即可从其移动设备从 App Center 门户下载应用。 安装应用内更新后,即可测试应用内更新。
  9. versionCode颠簸应用的 。
  10. 生成应用的发布版本并上传应用的新版本,就像在上一步中所做的那样,并将其分发到前面创建的 通讯组 。 下次启动应用时,将提示通讯组成员输入新版本。

提示

查看有关如何 利用 App Center 分发 的信息,了解有关 通讯组 等的更多详细信息。虽然可以在不添加任何代码的情况下使用 App Center Distribute 来分发应用的新版本,但将 App Center Distribute 添加到应用的代码将为测试人员和用户带来更无缝的体验,因为他们可以获得应用内更新体验。