有新版本可用时,强制更新Android应用


100

我在Google Play商店中有一个应用。当有可用的更新版本时,较旧的版本将不可用–也就是说,如果用户不更新应用程序,则他们不会输入应用程序。当有新版本可用时,如何强制用户更新应用程序?


1
这个开源GitHub项目(MAHAndroidUpdater)完全提供了此功能。试试吧,非常简单。github.com/hummatli/MAHAndroidUpdater
Sattar Hummatli,2016年

Answers:


99

我同意Scott Helme在这里的另一个回答。但是在某些极端情况(安全问题,API重大更改...)中,您绝对需要用户进行更新才能继续使用该应用程序时,可以提供一个简单的版本控制API。该API如下所示:

versionCheck API:

请求参数:

  • int appVersion

响应

  1. boolean forceUpgrade
  2. boolean recommendUpgrade

当您的应用启动时,您可以调用传入当前应用版本的此API,并检查版本控制API调用的响应。

如果forceUpgradetrue,则显示一个弹出对话框,其中包含允许用户退出该应用程序或前往Google Play商店升级该应用程序的选项。

否则,如果recommendUpgradetrue,显示的选项弹出对话框更新或继续使用应用程序。

即使有了这种强制升级功能,除非绝对需要,您仍应继续支持旧版本。


您是否会在应用启动时每次都显示弹出窗口,并且recommendedUpgrade为true?(从UX的角度来看),或者仅显示一次弹出窗口,如果用户拒绝升级,则不再显示。
Aneesh

8
对于建议的升级,我认为如果用户拒绝,您不应再显示。您还可以“在用户打开应用程序后每n次显示一次”。您可以自己决定n值并实现。
迈克尔

3
更好的imo解决方案是在所有API调用的标头中系统地传递应用程序的版本,然后如果API检测到版本已过时,则使该API返回特定的状态代码。然后在应用程序中处理它。这比必须再调用另一个专用API进行此检查,以及必须准确确定应在应用程序/服务中何时/何处执行此API调用更好。
拉斐尔C

@RaphaelC这实际上取决于您的服务器端实现。在某些情况下,添加检查所有API版本的过滤器也不是一件完美的事情。
9

4
@SepGH之后发生了变化,Android现在提供了一个API,您可以使用该API强制用户升级,如果用户未升级到最低版本,可以阻止他继续使用该应用程序: developer.android.com/guide/ app-bundle / in-app-updates
Raphael C

55

尝试以下操作:首先,您需要对playstore链接进行请求调用,从那里获取当前版本,然后将其与当前版本进行比较。

String currentVersion, latestVersion;
Dialog dialog;
private void getCurrentVersion(){
 PackageManager pm = this.getPackageManager();
        PackageInfo pInfo = null;

        try {
            pInfo =  pm.getPackageInfo(this.getPackageName(),0);

        } catch (PackageManager.NameNotFoundException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        currentVersion = pInfo.versionName;

   new GetLatestVersion().execute();

}

private class GetLatestVersion extends AsyncTask<String, String, JSONObject> {

        private ProgressDialog progressDialog;

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected JSONObject doInBackground(String... params) {
            try {
//It retrieves the latest version by scraping the content of current version from play store at runtime
                Document doc = Jsoup.connect(urlOfAppFromPlayStore).get();
                latestVersion = doc.getElementsByClass("htlgb").get(6).text();

            }catch (Exception e){
                e.printStackTrace();

            }

            return new JSONObject();
        }

        @Override
        protected void onPostExecute(JSONObject jsonObject) {
            if(latestVersion!=null) {
                if (!currentVersion.equalsIgnoreCase(latestVersion)){
                   if(!isFinishing()){ //This would help to prevent Error : BinderProxy@45d459c0 is not valid; is your activity running? error
                    showUpdateDialog();
                    }
                }
            }
            else
                background.start();
            super.onPostExecute(jsonObject);
        }
    }

private void showUpdateDialog(){
        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("A New Update is Available");
        builder.setPositiveButton("Update", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse
                        ("market://details?id=yourAppPackageName")));
                dialog.dismiss();
            }
        });

        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                background.start();
            }
        });

        builder.setCancelable(false);
        dialog = builder.show();
    }

1
我想这将需要使用添加外部库JSoup吗?
亚伯拉罕·菲利普

1
是的,但这将用于获取html div页面。我的意思是说,您可能不需要使用任何第三方库来实现此功能,如果您可以在没有JSoup的情况下抓取该html元素,那么它仍然可以工作
AshuKingSharma 2015年

1
如果他们更改itemprop值怎么办?
asok Buzz

1
如果他们更改了itemprop值,那么它也将影响我的应用程序,我将编辑我的答案以立即提供支持,所以请放心,直到那时:)
AshuKingSharma

尼斯setCancelable(false)
王凯

46

新版本问世后,您不应该停止支持旧版本。这将导致糟糕的用户体验。我不知道有任何软件供应商会这样做,这是有充分的理由的。

如果用户当时无法更新或不愿意怎么办?他们根本无法使用您的应用程序,这很糟糕。

Google没有提供类似的版本跟踪选项,因此您必须自己滚动。一个简单的Web服务返回您的应用程序可以检查的当前实时版本就足够了。然后,您可以更新版本,应用程序将知道它已过时。与使用Google Play相比,我只建议使用此方法让您的用户更快地知道有更新。它不应真正用来阻止应用程序正常运行,仅用于提示用户进行更新。


2
@ashraful如果此问题或任何答案已解决您的问题,请考虑通过单击复选标记来接受它。这向更广泛的社区表明您已经找到了解决方案,并为答疑者和您自己赢得了一定声誉。没有义务这样做。
苏菲安

答案取决于您知道多少软件供应商。你能量化吗?
Lou Morda

1
是否可以从应用商店或Play商店获取当前应用版本?还是我需要在服务器中保留最新的应用程序版本?因为现在我的服务器中有服务呼叫来返回最新的应用程序版本,但是困难是每次发布应用程序时都需要更新服务器。因此,可以从各个商店获取当前版本号吗?
Subha

2
Supercell仅支持其游戏的最新版本。将新版本推送到Google Play后,任何尝试连接的旧客户端都会从服务器收到“需要更新”响应,并看到连接已关闭。他们每年都要这样做多次。因此,除非您不希望将“游戏公司”算作“软件供应商”,否则您现在知道了:)考虑到他们每天仍能赚几百万美元,显然,这种糟糕的用户体验并不是停滞不前的。
Arjan

@Subha是的,有可能。但是没有官方的API,因此您需要抓取网页。这也意味着如果网页更改,它可能会中断。我在编写的第三方应用程序中做到了这一点,并在此处发布了相当简单的代码作为答案。目前,该代码将同时适用于Play和iTunes页面。但是,在生产环境中,最好是寻找一种可以自动更新服务器上最新应用程序版本的解决方案,而不必依赖Google或Apple上页面的格式?
Arjan

27

Google团队的解决方案

从Android 5.0开始,可以通过新的Google Play In App更新机制轻松实现。要求是拥有1.5.0+版的Play Core库,并使用App Bundles idstribution而不是apks。

该库为您提供了两种不同的方式来通知用户有关更新的信息:

  1. 灵活,当用户在后台对其进行更新时可以继续使用该应用程序时。

  2. 立即-阻止屏幕,直到用户对其进行更新,用户才可以进入该应用。

有实现它的下一步:

  1. 检查更新可用性
  2. 开始更新
  3. 获取更新状态的回调
  4. 处理更新

所有这些步骤的实现均在官方网站上进行了详细描述:https//developer.android.com/guide/app-bundle/in-app-updates


16

斯科特和迈克尔的答案是正确的。托管提供您支持的最低版本号的服务,并将其与已安装的版本进行比较。希望您永远不需要使用此功能,但是如果存在某些版本,则由于某些严重的缺陷而绝对必须杀死它,这可以节省生命。

我只想添加下一步的代码。提示用户必须升级后,您可以按照以下方法启动Google Play意图并将其带到商店中的新版本。

public class UpgradeActivity extends Activity {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_upgrade);
        final String appName = "com.appname";
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                    startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id="+appName)));

            }
        });
    }
}

如果必须在每个版本上强制升级,则应重新考虑设计。


11

Google引入了应用内更新库(https://developer.android.com/guide/app-bundle/in-app-updates),该库可在Lollipop +上运行,并且使您可以向用户要求更新的功能对话框(FLEXIBLE)或带有强制性全屏消息(IMMEDIATE)。

您需要实现后者。它是这样的: 在此处输入图片说明

我在此答案中涵盖了所有代码:https : //stackoverflow.com/a/56808529/5502121


1
这是最好的答案!
fvaldivia

此功能实际上依赖于用户启动您的应用程序时Play缓存是最新的,即,如果您的应用程序的版本号与您在Playstore上最新部署的版本号不同,则它不会提示用户。
kiran01bm

6

我强烈建议您为此检查Firebase的Remote Config功能。

我使用参数app_version_enabled来实现它,条件为“ Disabled Android Versions”,如下所示:

applies if App ID == com.example.myapp and App version regular expression ^(5.6.1|5.4.2) 

参数的默认值为“ true”,但“ Disabled Android Versions”的值为false。在禁用Android版本的正则表达式中,您可以添加更多禁用版本,只需|{version name}在括号内添加另一个即可。

然后,我只是检查配置是否显示了版本已启用-我启动了一项活动,该活动会强制用户进行升级。我检查了可以从外部启动应用程序的仅有两个位置(我的默认启动器活动和意图处理活动)。由于Remote Config是基于缓存工作的,因此,如果没有经过必要的时间才能使缓存无效,则它不会立即捕获应用程序的“已禁用”版本,但是如果您经过的时间最多为12个小时,他们推荐的缓存过期值。


这是一个非常好的选择。通过指定“禁用”版本的子集,可以在应用程序出现安全问题或主要性能问题时快速保护自己。它使您可以继续支持每个版本,但在需要时禁止特定版本。
特里

6

谷歌正式为此提供了一个Android API。

该API目前正在与一些合作伙伴进行测试,并将很快对所有开发人员开放。

更新 API现在可用-https: //developer.android.com/guide/app-bundle/in-app-updates



1
现在可以使用此api吗?
Yayayaya

我在Google上进行了搜索,找到了这个developer.android.com/guide/app-bundle/in-app-updates
Chulo

此功能实际上依赖于用户启动您的应用程序时Play缓存是最新的,即,如果您的应用程序的版本号与您在Playstore上最新部署的版本号不同,则它不会提示用户。
kiran01bm

3

检查本地版本代码并播放商店apk

try {
        versionChecker VersionChecker = new versionChecker();
        String versionUpdated = VersionChecker.execute().get().toString();
        Log.i("version code is", versionUpdated);


        PackageInfo packageInfo = null;
        try {
            packageInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        int version_code = packageInfo.versionCode;
        String version_name = packageInfo.versionName;
        Log.i("updated version code", String.valueOf(version_code) + "  " + version_name);
        if (version_name != versionUpdated) {
            String packageName = getApplicationContext().getPackageName();//
            UpdateMeeDialog updateMeeDialog = new UpdateMeeDialog();
            updateMeeDialog.showDialogAddRoute(MainActivity.this, packageName);
            Toast.makeText(getApplicationContext(), "please updated", Toast.LENGTH_LONG).show();
        }
    } catch (Exception e) {
        e.getStackTrace();
    }

实现类以进行版本检查

class versionChecker extends AsyncTask<String, String, String> {
String newVersion;

@Override
protected String doInBackground(String... params) {

    try {
        newVersion = Jsoup.connect("https://play.google.com/store/apps/details?id=+YOR_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();
    } catch (IOException e) {
        e.printStackTrace();
    }

    return newVersion;
}
}

拨号框用于更新

public class UpdateMeeDialog {

ActivityManager am;
TextView rootName;
Context context;
Dialog dialog;
String key1,schoolId;
public void showDialogAddRoute(Activity activity, final String packageName){
    context=activity;
    dialog = new Dialog(context);

    dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
    dialog.setCancelable(false);
    dialog.setContentView(R.layout.dialog_update);
    am = (ActivityManager)activity.getSystemService(Context.ACTIVITY_SERVICE);

    Button cancelDialogue=(Button)dialog.findViewById(R.id.buttonUpdate);
    Log.i("package name",packageName);
    cancelDialogue.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent=new Intent(Intent.ACTION_VIEW);                
    intent.setData(Uri.parse("https://play.google.com/store/apps/details?
    id="+packageName+"&hl=en"));
            context.startActivity(intent);
        }
    });
    dialog.show();
}
}

对话框布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#d4e9f2">


<TextView
    android:layout_width="match_parent"
    android:layout_height="40dp"

    android:text="Please Update First..!!"
    android:textSize="20dp"
    android:textColor="#46a5df"
    android:textAlignment="center"
    android:layout_marginTop="50dp"
    android:id="@+id/textMessage"
    />
<LinearLayout
    android:layout_width="match_parent"
    android:orientation="horizontal"
    android:weightSum="1"
    android:layout_marginTop="50dp"
    android:layout_below="@+id/textMessage"
    android:layout_height="50dp">

    <Button
        android:id="@+id/buttonUpdate"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="Update"
        android:background="#67C6F1"
        android:textAlignment="center" />
</LinearLayout>


2

最好定义我们自己的升级过程。

  1. 创建一个Web服务,从我们的服务器为应用程序(ios,android)提供最新版本。
  2. 或您在应用程序中使用的任何Web服务(例如,登录名)都会从服务器返回最新的应用程序版本。
  3. 一旦应用程序从#1或2获得版本,应用程序将使用本地/当前appversion对它进行交叉检查。如果存在差异,我们可以如下显示警报,

Android&iOS: 如果有可用的最新应用程序版本,则它将显示为“具有更多功能的最新版本,要升级,请单击升级按钮”(带有“ Upgarde”和“ No.谢谢”按钮的警告。)然后,应用程序将重定向到playstore / Appstore,它将打开最新版本。

 --- we can do upgrade compulsory or optionally.

如果有任何数据库架构更改,请在升级过程之前确保已处理正确的数据库迁移过程。


1

要在市场上有可用更新时强制应用程序用户进行更新,您应该首先检查市场上的应用程序版本,并将其与设备上的应用程序版本进行比较。如果它们不同,则可能是可用的更新。在这篇文章中,我写下了获取市场最新版本和设备上当前版本并将它们进行比较的代码。我还展示了如何显示更新对话框以及如何将用户重定向到更新页面。请访问此链接:https : //stackoverflow.com/a/33925032/5475941只要确保在对话框中仅显示用户更新按钮,而不向他显示取消按钮即可。在这种情况下,他将被迫更新,然后才能使用该应用程序。


1

这里肯定要提到的是即将发布的In-app Updates API

您将有两个使用此API的选项;第一种是当您期望用户等待立即应用更新时对关键更新的全屏体验。第二个选项是灵活的更新,这意味着用户可以在下载更新时继续使用该应用程序。您可以完全自定义更新流程,因此感觉就像您的应用程序的一部分。



1

您可以使用Play Core Library应用内更新来解决此问题。您可以检查更新是否可用,如果可以无缝安装,请安装它们。

应用内更新与使用APK扩展文件(.obb文件)的应用不兼容。您可以选择灵活下载或立即更新,Google Play会负责为您下载和安装更新。

dependencies {
    implementation 'com.google.android.play:core:1.5.0'
    ...
}

请参阅此答案https://stackoverflow.com/a/58212818/7579041


0

您可以通过在变量中保留在应用程序中的版本号与服务器端保留的当前应用程序版本之间的匹配进行匹配,每次用户打开该应用程序时,都应发送第一个请求以检查是否找到了该请求匹配什么也不做,只会让用户使用您的应用,否则会向Google Playstore发出意图或向您的应用打开网络视图窗口(window.open(“ https://play.google.com/store/apps/details?id= package_name “,'_system','location = yes');),在那里,如果您没有自动更新,他们将自动具有询问更新的按钮,这就是google playstore为您执行的操作。


0

您可以通知您的用户,当前应用程序有新版本可更新。另外,如果满足此条件,则可以阻止该应用程序中的登录。

请查看这是否为您提供了解决方案。


0

嗯,可能有很多解决此问题的方法,例如从App Page(Google Play App页面)中抓取版本代码等。

但是,我将向您展示最终的解决方案,该解决方案不会花费一分钱,并且会像魔术一样起作用。

  1. 只需在Firebase远程
    配置面板上保存您应用的最新版本代码
  2. 每当打开应用程序时,获取该版本代码值
  3. 将其与应用程序的当前版本代码进行比较,您可以通过以下代码获得

    private int getCurrentVersionCode() {
    try {
        return getPackageManager().getPackageInfo(getPackageName(), 0).versionCode;
    } catch (NameNotFoundException e) {
        e.printStackTrace();
    }
    return -1;

    }

如果获取的版本代码大于当前版本,请显示AlertDialog要求更新应用程序。否则,该应用程序已更新。

因此,每当推出新版本时,都需要将该新版本代码放入Firebase远程配置面板中

您可以阅读有关如何使用Firebase Remote Config强制用户更新应用程序的整个教程。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.