【问题标题】:Prompt Android App User to Update App if current version <> market version如果当前版本 <> 市场版本,则提示 Android 应用用户更新应用
【发布时间】:2011-04-05 16:22:05
【问题描述】:

假设我的 Android 应用程序 0.1 版当前安装在用户的手机上。每次他们启动我的应用程序时,我都想检查 Android Market 中是否有不同的版本,假设这个版本是 0.2。如果这两个版本不匹配,我想显示一个对话框,提示用户升级应用程序。

我完全理解 Android Market 本身有一个通知程序给用户,但就我的分析数据而言,它在提醒用户升级到应用程序的新版本方面不是很有效。

任何见解都会非常有帮助。感谢 StackOverflowers,你们摇滚!

【问题讨论】:

标签: java android google-play


【解决方案1】:

截至 2019 年,更新应用的最佳方式是使用 Play Core 库 (1.5.0+) 提供的应用内更新。它适用于 Lollipop 和更新版本,但公平地说,Kit-Kat 到今天还不到 7%,很快就会永远消失。您可以在 Kit-Kat 上安全地运行此代码,无需版本检查,它不会崩溃。

官方文档:https://developer.android.com/guide/app-bundle/in-app-updates

应用内更新有两种类型:灵活即时

灵活会在对话窗口中很好地询问您:

立即 将要求您更新应用程序才能继续使用全屏消息(此页面可以关闭):

重要提示:目前,您无法在 Developer Play Console 的“应用发布”部分选择推出哪种类型的更新。但显然,他们很快就会给我们这个选择。 根据我的测试,目前,onSuccessListener 提供了这两种类型。

让我们在代码中实现两种类型。

在模块build.gradle中添加如下依赖:

dependencies {
    implementation 'com.google.android.play:core:1.6.1'//for new version updater
}

MainActivity.class:

private static final int REQ_CODE_VERSION_UPDATE = 530;
private AppUpdateManager appUpdateManager;
private InstallStateUpdatedListener installStateUpdatedListener;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

   checkForAppUpdate();
}

@Override
protected void onResume() {
    super.onResume();
    checkNewAppVersionState();
}

@Override
public void onActivityResult(int requestCode, final int resultCode, Intent intent) {
    super.onActivityResult(requestCode, resultCode, intent);

    switch (requestCode) {

        case REQ_CODE_VERSION_UPDATE:
            if (resultCode != RESULT_OK) { //RESULT_OK / RESULT_CANCELED / RESULT_IN_APP_UPDATE_FAILED
                L.d("Update flow failed! Result code: " + resultCode);
                // If the update is cancelled or fails,
                // you can request to start the update again.
                unregisterInstallStateUpdListener();
            }

            break;
    }
}

@Override
protected void onDestroy() {
    unregisterInstallStateUpdListener();
    super.onDestroy();
}


private void checkForAppUpdate() {
    // Creates instance of the manager.
    appUpdateManager = AppUpdateManagerFactory.create(AppCustom.getAppContext());

    // Returns an intent object that you use to check for an update.
    Task<AppUpdateInfo> appUpdateInfoTask = appUpdateManager.getAppUpdateInfo();

    // Create a listener to track request state updates.
    installStateUpdatedListener = new InstallStateUpdatedListener() {
        @Override
        public void onStateUpdate(InstallState installState) {
            // Show module progress, log state, or install the update.
            if (installState.installStatus() == InstallStatus.DOWNLOADED)
                // After the update is downloaded, show a notification
                // and request user confirmation to restart the app.
                popupSnackbarForCompleteUpdateAndUnregister();
        }
    };

    // Checks that the platform will allow the specified type of update.
    appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> {
        if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) {
            // Request the update.
            if (appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {

                // Before starting an update, register a listener for updates.
                appUpdateManager.registerListener(installStateUpdatedListener);
                // Start an update.
                startAppUpdateFlexible(appUpdateInfo);
            } else if (appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) ) {
                // Start an update.
                startAppUpdateImmediate(appUpdateInfo);
            }
        }
    });
}

private void startAppUpdateImmediate(AppUpdateInfo appUpdateInfo) {
    try {
        appUpdateManager.startUpdateFlowForResult(
                appUpdateInfo,
                AppUpdateType.IMMEDIATE,
                // The current activity making the update request.
                this,
                // Include a request code to later monitor this update request.
                MainActivity.REQ_CODE_VERSION_UPDATE);
    } catch (IntentSender.SendIntentException e) {
        e.printStackTrace();
    }
}

private void startAppUpdateFlexible(AppUpdateInfo appUpdateInfo) {
    try {
        appUpdateManager.startUpdateFlowForResult(
                appUpdateInfo,
                AppUpdateType.FLEXIBLE,
                // The current activity making the update request.
                this,
                // Include a request code to later monitor this update request.
                MainActivity.REQ_CODE_VERSION_UPDATE);
    } catch (IntentSender.SendIntentException e) {
        e.printStackTrace();
        unregisterInstallStateUpdListener();
    }
}

/**
 * Displays the snackbar notification and call to action.
 * Needed only for Flexible app update
 */
private void popupSnackbarForCompleteUpdateAndUnregister() {
    Snackbar snackbar =
            Snackbar.make(drawerLayout, getString(R.string.update_downloaded), Snackbar.LENGTH_INDEFINITE);
    snackbar.setAction(R.string.restart, new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            appUpdateManager.completeUpdate();
        }
    });
    snackbar.setActionTextColor(getResources().getColor(R.color.action_color));
    snackbar.show();

    unregisterInstallStateUpdListener();
}

/**
 * Checks that the update is not stalled during 'onResume()'.
 * However, you should execute this check at all app entry points.
 */
private void checkNewAppVersionState() {
    appUpdateManager
            .getAppUpdateInfo()
            .addOnSuccessListener(
                    appUpdateInfo -> {
                        //FLEXIBLE:
                        // If the update is downloaded but not installed,
                        // notify the user to complete the update.
                        if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
                            popupSnackbarForCompleteUpdateAndUnregister();
                        }

                        //IMMEDIATE:
                        if (appUpdateInfo.updateAvailability()
                                == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
                            // If an in-app update is already running, resume the update.
                            startAppUpdateImmediate(appUpdateInfo);
                        }
                    });

}

/**
 * Needed only for FLEXIBLE update
 */
private void unregisterInstallStateUpdListener() {
    if (appUpdateManager != null && installStateUpdatedListener != null)
        appUpdateManager.unregisterListener(installStateUpdatedListener);
}

我们完成了!

测试。 请阅读docs,以便您知道如何使用 Google Play 上的测试曲目正确测试它。

长话短说:

  1. 使用发布证书签署您的应用,并将其上传到开发者 Play 控制台中应用发布下的发布轨道之一(alpha/beta/其他自定义封闭轨道)。

  2. 在“管理测试人员”部分的发布跟踪页面中,创建并添加测试人员列表,并确保您选中复选框! - 此步骤是可选的,因为您的开发者帐户电子邮件也是测试者帐户,您可以使用它进行测试。

  3. 在测试人员列表下,您将找到“选择加入 URL” - 复制此 URL 并将其提供给您的测试人员或自行打开。转到该页面并接受测试建议。将有一个指向该应用程序的链接。 (您将无法在 Play 商店中搜索该应用,因此请为其添加书签)

  4. 通过该链接在您的设备上安装应用程序。

  5. build.gradle 中增加defaultConfig { versionCode k+1 } 的版本并构建另一个签名的apk Build > Generate Signed Bundle / APK... 并将其上传到您的发布轨道。

  6. 等待... 1 小时? 2小时?或更多,才能在赛道上发布。

  7. 清除您设备上 Play 商店应用的缓存。问题是 Play 应用会缓存有关已安装应用及其可用更新的详细信息,因此您需要清除缓存。为此,采取两个步骤:

7.1。转到设置 > 应用程序 > Google PLay 商店 > 存储 > 清除缓存。

7.2。打开 Play 商店应用 > 打开主菜单 > 我的应用和游戏 > 你应该会看到你的应用有新的更新。

如果您没有看到它,请确保您的新更新已经发布在轨道上(转到您的书签页面并使用它打开您在 Play 商店中的应用列表,以查看那里显示的版本)。此外,当您的更新上线时,您会在 Developer Play Console 的右上角看到一条通知(铃铛图标将带有一个红点)。

希望对你有帮助。

【讨论】:

  • 是的,但是这个应用内更新功能也必须作为对现有应用的更新来实现。如果用户之前没有更新应用程序,他为什么要现在这样做呢?仅当用户首次更新到应用更新 API 中包含的版本时,才会提示用户更新。
  • @KirillKarmazin 嗨,如何在 react native mainactivity.java 文件中实现这一点?
  • 如果用户立即点击退出按钮怎么办?除了 UI 之外,我认为与灵活的没有任何不同。我想根据他们的应用版本为我的用户提供两种类型
  • @Kirill Karmazin,我试过上面的解决方案,它不能正常工作,想要实现灵活更新,上面的解决方案没有显示任何对话框或要求用户更新,我正在使用 Kotlin
  • 是否可以显示我的自定义对话框而不是默认对话框?
【解决方案2】:

Android Market 是一个封闭的系统,只有一个非官方的 api,随时可能崩溃。

您最好的选择是在您的网络服务器上托管一个文件(xml、json 或简单文本),当您将应用程序发布到市场时,您只需在其中更新应用程序的当前版本。

然后,您的应用只需在启动时获取该文件,检查当前安装的应用是否具有较低的版本号,并显示一个对话框以警告用户他落后了。

【讨论】:

  • 这和我想的差不多……只是想检查一下是否有 Google 已经提供的 API 可以在需要时进行检查。
  • 这里的问题是 google play store 缓存。有时您可能会发布并更新 json 中的版本,但 Play 商店缓存仍指向较旧的 apk。除非您清除 Play 商店应用设置中的缓存,否则它将无法使用
【解决方案3】:

如果您想避免让后端服务器像已接受的答案中建议的那样存储当前应用程序版本,您可以使用的另一个选项是使用Google Tag Manager (GTM)。

如果您已经在使用 Google Analytics SDK,那么您也可以使用 GTM。

在 GTM 中,您可以在容器中为您的应用定义一个值,以指定您最新发布的版本。例如:

{
   "latestAppVersion": 14,
   ...
}

然后,您可以在应用启动时查询该值,并在有更新版本时显示用户更新对话框提醒。

Container container = TagManager.getInstance(context).openContainer(myContainerId);
long latestVersionCode = container.getLong("latestAppVersion");

// get currently running app version code
PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
long versionCode = pInfo.versionCode;

// check if update is needed
if(versionCode < latestVersionCode) {
   // remind user to update his version
}

【讨论】:

  • 您使用了标签还是宏?如果宏你使用值集合吗?因为我似乎无法让它发挥作用。
  • 我使用宏值集合,这里完全是从集合中复制粘贴:{ "latestAppVersion": 30 }
【解决方案4】:

看看这个可用于查询 Android Market API 的库

http://code.google.com/p/android-market-api/

【讨论】:

  • 我看到这可能有效,但不是 Google 的“官方”API,这意味着它可能随时失去支持或中断。我的推断是对的吗?
【解决方案5】:

您可以使用此 Android 库:https://github.com/danielemaddaluno/Android-Update-Checker。它旨在提供一种可重用的工具来异步检查应用商店中是否存在任何较新发布的应用更新。 它是基于使用 Jsoup (http://jsoup.org/) 来测试是否真的存在新更新解析 Google Play Store 上的应用页面:

private boolean web_update(){
    try {       
        String curVersion = applicationContext.getPackageManager().getPackageInfo(package_name, 0).versionName; 
        String newVersion = curVersion;
        newVersion = Jsoup.connect("https://play.google.com/store/apps/details?id=" + package_name + "&hl=en")
                .timeout(30000)
                .userAgent("Mozilla/5.0 (Windows; U; WindowsNT 5.1; en-US; rv1.8.1.6) Gecko/20070725 Firefox/2.0.0.6")
                .referrer("http://www.google.com")
                .get()
                .select("div[itemprop=softwareVersion]")
                .first()
                .ownText();
        return (value(curVersion) < value(newVersion)) ? true : false;
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}

并且作为“值”函数如下(如果值在 0-99 之间有效):

private long value(String string) {
    string = string.trim();
    if( string.contains( "." )){ 
        final int index = string.lastIndexOf( "." );
        return value( string.substring( 0, index ))* 100 + value( string.substring( index + 1 )); 
    }
    else {
        return Long.valueOf( string ); 
    }
}

如果您只想验证版本之间的不匹配,您可以更改:

"value(curVersion)

【讨论】:

  • 在这里解析应用商店中的值是不是有点矫枉过正?我建议使用上面列出的方法之一——只需将密钥存储在服务器上并通过 API 调用获取它。
  • 目标是检查商店中当前可用的最新版本(因为发布应用程序可能需要一段时间才能出现)。但是,整个 html 大约有 350KB 或更多(这就是 @Nitin 说“过分杀伤”的原因)。如果有人更喜欢这个解决方案,请确保您的应用仅在 WiFi 连接可用时检查。
【解决方案6】:

如果当前版本不等于市场版本,则提示Android App用户更新应用程序,您应首先检查市场上的应用程序版本,并将其与设备上的应用程序版本进行比较。如果它们不同,则可能是可用的更新。在这篇文章中,我写下了获取当前市场版本和设备上当前版本的代码,并将它们放在一起进行比较。我还展示了如何显示更新对话框并将用户重定向到更新页面。请访问此链接:https://stackoverflow.com/a/33925032/5475941

【讨论】:

    【解决方案7】:

    我用于强制应用更新的 Kotlin 代码:

    const val FLEXIABLE_UPADTE: Int = 101
    const val FORCE_UPDATE: Int = 102
    const val APP_UPDATE_CODE: Int = 500
    
    override fun onCreate {
        // Get updateType from Webservice.
        updateApp(updateType)
    }
    
    private fun updateApp(statusCode: Int) {
        appUpdateManager = AppUpdateManagerFactory.create(this @MainActivity)
    
        val appUpdateInfoTask = appUpdateManager ? .appUpdateInfo
    
        appUpdateInfoTask ? .addOnSuccessListener {
            appUpdateInfo - >
                if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) {
                    if ((statusCode == Constants.FORCE_UPDATE))
                        appUpdateManager ? .startUpdateFlowForResult(
                            appUpdateInfo, AppUpdateType.IMMEDIATE, this, Constants.APP_UPDATE_CODE
                        )
                    else if (statusCode == Constants.FLEXIABLE_UPADTE)
                        appUpdateManager ? .startUpdateFlowForResult(
                            appUpdateInfo, AppUpdateType.FLEXIBLE, this, Constants.FLEXIABLE_UPADTE
                        )
                }
        }
    }
    
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent ? ) {
    
        try {
            if (requestCode == Constants.APP_UPDATE_CODE && resultCode == Activity.RESULT_OK) {
                if (resultCode != RESULT_OK) {
                    appUpdateCompleted()
                }
            }
        } catch (e: java.lang.Exception) {
    
        }
    }
    
    private fun appUpdateCompleted() {
        Snackbar.make(
            findViewById(R.id.activity_main_layout),
            "An update has just been downloaded.",
            Snackbar.LENGTH_INDEFINITE
        ).apply {
            setAction("RESTART") {
                appUpdateManager.completeUpdate()
            }
            setActionTextColor(resources.getColor(R.color.snackbar_action_text_color))
            show()
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-05
      • 2021-09-14
      • 2023-03-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多