如何使用Android';什么是应用程序内更新API?
我最近遇到了一种新的应用程序更新流,它是由Google Play API提供的。我喜欢这种无缝流程来更新Android应用程序。我在Hotstar应用程序中观察到了以下提到的步骤如何使用Android';什么是应用程序内更新API?,android,google-play,auto-update,in-app-update,Android,Google Play,Auto Update,In App Update,我最近遇到了一种新的应用程序更新流,它是由Google Play API提供的。我喜欢这种无缝流程来更新Android应用程序。我在Hotstar应用程序中观察到了以下提到的步骤 从底部弹出一张卡片,显示更新可用 当我点击“更新Hotstar”按钮时,弹出一个对话框(好像是由Google Play提供的) 下载是在应用程序运行时在后台启动的 下载完成后,弹出一个SnackBar,显示应用程序已准备好安装 安装后应用程序重新启动 我怎样才能做到这一点?必须有一种与Google Play沟通的方
我怎样才能做到这一点?必须有一种与Google Play沟通的方式。我浏览了很多博客。但是,没有找到任何解决方案。如果用户禁用了自动应用程序更新,这对开发人员来说可能是一个很棒的功能。我猜它是由应用程序本身控制的,而不是由Google Play控制的。我开发了一些应用程序,可以在启动时调用API来读取“最新”版本号以及该版本是否为“强制”更新,并将其与正在运行的应用程序版本进行比较。如果有新版本可用,则会向用户显示一个与您显示的对话框类似的对话框(尽管他们的对话框更好),提醒用户更新可用。如果更新是“强制”的,则消息会告诉他们在继续之前必须更新应用程序。如果他们单击更新,那么他们将被带到应用商店页面,在那里他们像往常一样开始下载更新,应用程序将退出。如果他们单击“关闭”,应用程序将退出。如果更新不是强制性的,则会询问他们是现在更新还是继续。如果他们单击更新,那么他们将被带到应用商店页面,在那里他们像往常一样开始下载更新,应用程序将退出。如果他们单击“继续”,那么他们将被带到应用程序的现有版本中
我不知道他们是如何管理后台下载,然后在退出应用程序之前启动应用程序更新的。这将非常好,但我们上面的方法也非常简单,为开发人员提供了很多功能。谷歌正在测试应用程序更新API的早期版本,如中所述
它现在只对一些早期测试合作伙伴可用,但最终应该对所有开发人员可用。请关注Android开发者博客和Play console中的公告。Android今天正式向所有人发布了应用内更新 更新: 在单个活动中处理即时和灵活的更新;科特林路
import android.app.Activity
import android.content.Intent
import android.content.IntentSender
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import com.google.android.material.snackbar.Snackbar
import com.google.android.play.core.appupdate.AppUpdateManager
import com.google.android.play.core.appupdate.AppUpdateManagerFactory
import com.google.android.play.core.install.InstallState
import com.google.android.play.core.install.InstallStateUpdatedListener
import com.google.android.play.core.install.model.AppUpdateType
import com.google.android.play.core.install.model.InstallStatus
import com.google.android.play.core.install.model.UpdateAvailability
import timber.log.Timber
class BaseUpdateCheckActivity : AppCompatActivity() {
private val appUpdateManager: AppUpdateManager by lazy { AppUpdateManagerFactory.create(this) }
private val appUpdatedListener: InstallStateUpdatedListener by lazy {
object : InstallStateUpdatedListener {
override fun onStateUpdate(installState: InstallState) {
when {
installState.installStatus() == InstallStatus.DOWNLOADED -> popupSnackbarForCompleteUpdate()
installState.installStatus() == InstallStatus.INSTALLED -> appUpdateManager.unregisterListener(this)
else -> Timber.d("InstallStateUpdatedListener: state: %s", installState.installStatus())
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_ad_view)
checkForAppUpdate()
}
private fun checkForAppUpdate() {
// Returns an intent object that you use to check for an update.
val appUpdateInfoTask = appUpdateManager.appUpdateInfo
// Checks that the platform will allow the specified type of update.
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) {
// Request the update.
try {
val installType = when {
appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE) -> AppUpdateType.FLEXIBLE
appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) -> AppUpdateType.IMMEDIATE
else -> null
}
if (installType == AppUpdateType.FLEXIBLE) appUpdateManager.registerListener(appUpdatedListener)
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
installType!!,
this,
APP_UPDATE_REQUEST_CODE)
} catch (e: IntentSender.SendIntentException) {
e.printStackTrace()
}
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == APP_UPDATE_REQUEST_CODE) {
if (resultCode != Activity.RESULT_OK) {
Toast.makeText(this,
"App Update failed, please try again on the next app launch.",
Toast.LENGTH_SHORT)
.show()
}
}
}
private fun popupSnackbarForCompleteUpdate() {
val snackbar = Snackbar.make(
findViewById(R.id.drawer_layout),
"An update has just been downloaded.",
Snackbar.LENGTH_INDEFINITE)
snackbar.setAction("RESTART") { appUpdateManager.completeUpdate() }
snackbar.setActionTextColor(ContextCompat.getColor(this, R.color.accent))
snackbar.show()
}
override fun onResume() {
super.onResume()
appUpdateManager
.appUpdateInfo
.addOnSuccessListener { appUpdateInfo ->
// If the update is downloaded but not installed,
// notify the user to complete the update.
if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
popupSnackbarForCompleteUpdate()
}
//Check if Immediate update is required
try {
if (appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
// If an in-app update is already running, resume the update.
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
AppUpdateType.IMMEDIATE,
this,
APP_UPDATE_REQUEST_CODE)
}
} catch (e: IntentSender.SendIntentException) {
e.printStackTrace()
}
}
}
companion object {
private const val APP_UPDATE_REQUEST_CODE = 1991
}
}
源代码要点:步骤1:添加依赖项(build.gradle(app)):
dependencies {
implementation 'com.google.android.play:core:1.7.3'
...
}
步骤2:检查更新可用性,如果可用,则启动更新
private AppUpdateManager mAppUpdateManager;
private static final int RC_APP_UPDATE = 11;
InstallStateUpdatedListener installStateUpdatedListener = new
InstallStateUpdatedListener() {
@Override
public void onStateUpdate(InstallState state) {
if (state.installStatus() == InstallStatus.DOWNLOADED){
//CHECK THIS if AppUpdateType.FLEXIBLE, otherwise you can skip
popupSnackbarForCompleteUpdate();
} else if (state.installStatus() == InstallStatus.INSTALLED){
if (mAppUpdateManager != null){
mAppUpdateManager.unregisterListener(installStateUpdatedListener);
}
} else {
Log.i(TAG, "InstallStateUpdatedListener: state: " + state.installStatus());
}
}
};
private void popupSnackbarForCompleteUpdate() {
Snackbar snackbar =
Snackbar.make(
findViewById(R.id.coordinatorLayout_main),
"New app is ready!",
Snackbar.LENGTH_INDEFINITE);
snackbar.setAction("Install", view -> {
if (mAppUpdateManager != null){
mAppUpdateManager.completeUpdate();
}
});
snackbar.setActionTextColor(getResources().getColor(R.color.install_color));
snackbar.show();
}
在onStart()方法中:
步骤3:收听更新状态
private AppUpdateManager mAppUpdateManager;
private static final int RC_APP_UPDATE = 11;
InstallStateUpdatedListener installStateUpdatedListener = new
InstallStateUpdatedListener() {
@Override
public void onStateUpdate(InstallState state) {
if (state.installStatus() == InstallStatus.DOWNLOADED){
//CHECK THIS if AppUpdateType.FLEXIBLE, otherwise you can skip
popupSnackbarForCompleteUpdate();
} else if (state.installStatus() == InstallStatus.INSTALLED){
if (mAppUpdateManager != null){
mAppUpdateManager.unregisterListener(installStateUpdatedListener);
}
} else {
Log.i(TAG, "InstallStateUpdatedListener: state: " + state.installStatus());
}
}
};
private void popupSnackbarForCompleteUpdate() {
Snackbar snackbar =
Snackbar.make(
findViewById(R.id.coordinatorLayout_main),
"New app is ready!",
Snackbar.LENGTH_INDEFINITE);
snackbar.setAction("Install", view -> {
if (mAppUpdateManager != null){
mAppUpdateManager.completeUpdate();
}
});
snackbar.setActionTextColor(getResources().getColor(R.color.install_color));
snackbar.show();
}
步骤4:获取更新状态的回调
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_APP_UPDATE) {
if (resultCode != RESULT_OK) {
Log.e(TAG, "onActivityResult: app download failed");
}
}
}
步骤5:灵活更新
private AppUpdateManager mAppUpdateManager;
private static final int RC_APP_UPDATE = 11;
InstallStateUpdatedListener installStateUpdatedListener = new
InstallStateUpdatedListener() {
@Override
public void onStateUpdate(InstallState state) {
if (state.installStatus() == InstallStatus.DOWNLOADED){
//CHECK THIS if AppUpdateType.FLEXIBLE, otherwise you can skip
popupSnackbarForCompleteUpdate();
} else if (state.installStatus() == InstallStatus.INSTALLED){
if (mAppUpdateManager != null){
mAppUpdateManager.unregisterListener(installStateUpdatedListener);
}
} else {
Log.i(TAG, "InstallStateUpdatedListener: state: " + state.installStatus());
}
}
};
private void popupSnackbarForCompleteUpdate() {
Snackbar snackbar =
Snackbar.make(
findViewById(R.id.coordinatorLayout_main),
"New app is ready!",
Snackbar.LENGTH_INDEFINITE);
snackbar.setAction("Install", view -> {
if (mAppUpdateManager != null){
mAppUpdateManager.completeUpdate();
}
});
snackbar.setActionTextColor(getResources().getColor(R.color.install_color));
snackbar.show();
}
步骤6:不要忘记注销侦听器(在onStop方法中)
注意:在应用程序的任何一个活动中添加此侦听器,最好是在MainActivity(主页)中添加。
对于测试,您可以使用FakeAppUpdateManager
约束:应用内更新仅适用于运行Android 5.0(API级别21)或更高版本的设备
官方文档:为了实现这一点,在接受的答案中引用的谷歌官方文档在语法上是不正确的。经过一些研究,我终于找到了正确的语法: 而不是:
// Creates an instance of the manager.
AppUpdateManager appUpdateManager = AppUpdateManagerFactory.create(context);
// Returns an intent object that you use to check for an update.
Task<AppUpdateInfo> appUpdateInfo = appUpdateManager.getAppUpdateInfo();
// Checks that the platform will allow the specified type of update.
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
// For a flexible update, use AppUpdateType.FLEXIBLE
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
// Request the update.
appUpdateManager.startUpdateFlowForResult(
// Pass the intent that is returned by 'getAppUpdateInfo()'.
appUpdateInfo,
// Or 'AppUpdateType.FLEXIBLE' for flexible updates.
AppUpdateType.IMMEDIATE,
// The current activity making the update request.
this,
// Include a request code to later monitor this update request.
MY_REQUEST_CODE);
}
请试试这个
步骤1:在build.gradle文件中添加以下库(请检查并更新最新的播放代码插件版本)
步骤2:在类中声明以下变量(Ex MainActivity.java)
步骤4:在活动的onDestroy()方法中,只需注销侦听器
@Override
protected void onDestroy() {
mAppUpdateManager.unregisterListener(installStateUpdatedListener);
super.onDestroy();
}
步骤5:在onResume()中,我们需要通过下面的代码收听灵活和即时的更新
@Override
protected void onResume() {
try {
mAppUpdateManager.getAppUpdateInfo().addOnSuccessListener(appUpdateInfo -> {
if (appUpdateInfo.updateAvailability() ==
UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
// If an in-app update is already running, resume the update.
try {
mAppUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
inAppUpdateType,
this,
RC_APP_UPDATE);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
}
});
mAppUpdateManager.getAppUpdateInfo().addOnSuccessListener(appUpdateInfo -> {
//For flexible update
if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
popupSnackbarForCompleteUpdate();
}
});
} catch (Exception e) {
e.printStackTrace();
}
super.onResume();
}
步骤6:在onActivityResult()中,我们需要处理用户单击操作(仅适用于灵活更新)
步骤7:创建一个方法来检查更新是否可用并启动更新(立即更新)
步骤9:现在,无论何时开始检查更新,都可以使用应用程序内更新类型(灵活或立即)调用该方法
//For Immediate
inAppUpdateType = AppUpdateType.IMMEDIATE; //1
inAppUpdate();
//For Flexible
inAppUpdateType = AppUpdateType.FLEXIBLE; //0
inAppUpdate();
要记住的要点:
尝试使用这些库,在这些库中只需几行代码即可实现
谢谢您抽出时间。我们可以在应用程序中轻松开发此功能。但是你必须把用户带到游戏商店。然而,如果你能猜到的话,Google Play也有一些回调。这就是为什么他们能够在下载完成时显示SnackBar。酷!Google Play似乎正在与选定的合作伙伴测试应用内更新API。想在它可用时查看它。。。它被称为应用程序内更新API,并由选定的合作伙伴进行测试。我也在等这个。不幸的是,谷歌喜欢发布需要很长时间才能发布的功能和产品。我正在运行一些测试,但似乎无法正常工作。我总是得到UpdateAvailability.UPDATE\u作为响应。有什么我遗漏的吗?@kevintresuelo我想你的应用应该发布在Play store上。对于测试,请降低版本
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_APP_UPDATE) {
//when user clicks update button
if (resultCode == RESULT_OK) {
Toast.makeText(MainActivity.this, "App download starts...", Toast.LENGTH_LONG).show();
} else if (resultCode != RESULT_CANCELED) {
//if you want to request the update again just call checkUpdate()
Toast.makeText(MainActivity.this, "App download canceled.", Toast.LENGTH_LONG).show();
} else if (resultCode == RESULT_IN_APP_UPDATE_FAILED) {
Toast.makeText(MainActivity.this, "App download failed.", Toast.LENGTH_LONG).show();
}
}
}
private void inAppUpdate() {
try {
// Checks that the platform will allow the specified type of update.
appUpdateInfoTask.addOnSuccessListener(new OnSuccessListener<AppUpdateInfo>() {
@Override
public void onSuccess(AppUpdateInfo appUpdateInfo) {
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
// For a flexible update, use AppUpdateType.FLEXIBLE
&& appUpdateInfo.isUpdateTypeAllowed(inAppUpdateType)) {
// Request the update.
try {
mAppUpdateManager.startUpdateFlowForResult(
// Pass the intent that is returned by 'getAppUpdateInfo()'.
appUpdateInfo,
// Or 'AppUpdateType.FLEXIBLE' for flexible updates.
inAppUpdateType,
// The current activity making the update request.
MainActivity.this,
// Include a request code to later monitor this update request.
RC_APP_UPDATE);
} catch (IntentSender.SendIntentException ignored) {
}
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
private void popupSnackbarForCompleteUpdate() {
try {
Snackbar snackbar =
Snackbar.make(
findViewById(R.id.id_of_root_loyout),
"An update has just been downloaded.\nRestart to update",
Snackbar.LENGTH_INDEFINITE);
snackbar.setAction("INSTALL", view -> {
if (mAppUpdateManager != null){
mAppUpdateManager.completeUpdate();
}
});
snackbar.setActionTextColor(getResources().getColor(R.color.install_color));
snackbar.show();
} catch (Resources.NotFoundException e) {
e.printStackTrace();
}
}
//For Immediate
inAppUpdateType = AppUpdateType.IMMEDIATE; //1
inAppUpdate();
//For Flexible
inAppUpdateType = AppUpdateType.FLEXIBLE; //0
inAppUpdate();