Flutter应用内更新插件flutter_rustore_update的使用

Flutter应用内更新插件flutter_rustore_update的使用

RuStore文档

通用

RuStore In-app updates SDK帮助您保持用户设备上的应用程序为最新版本。

当用户维护应用程序处于最新状态时,他们可以尝试新功能,并且还可以利用性能改进和错误修复。

您可以使用RuStore In-app updates SDK来显示应用程序更新过程,该过程支持后台下载和安装更新,并具有状态控制。在下载过程中,用户仍可以继续使用您的应用程序。

用户场景示例

Update flow

准备必需参数

为了运行示例,您需要以下参数:

  1. applicationId - 您发布到RuStore控制台的应用程序ID,位于项目的build.gradle文件中:

    android {
        defaultConfig {
            applicationId = "ru.rustore.sdk.updateexample"
        }
    }
    
  2. release.keystore - 应用程序发布的签名。您可以在RuStore控制台中找到该签名。

设置示例应用程序

  1. 为了测试示例,您需要将两个版本的应用程序上传到控制台,每个版本具有不同的versionCode。在测试时,请指定比控制台中更小的versionCode。

    defaultConfig {
        versionCode 1
    }
    
  2. 在example/android/app/build.gradle文件中替换applicationId为您在RuStore控制台上发布的apk文件的applicationId:

    android {
        defaultConfig {
            applicationId = "ru.rustore.sdk.updateexample" // 通常在buildTypes后附加.debug
        }
    }
    
  3. 将签名替换为您的应用程序签名。设置key_aliaskey_passwordstore_password参数:

    android{
        signingConfigs {
            release {
                keyAlias keystoreProperties['keyAlias']
                keyPassword keystoreProperties['keyPassword']
                storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
                storePassword keystoreProperties['storePassword']
            }
        }
    }
    

SDK正常工作的条件

为了使RuStore In-app updates SDK正常工作,必须满足以下条件:

  • Android操作系统版本7.0或更高。
  • 用户设备上应安装有RuStore。
  • 设备上的RuStoreApp版本应是最新的。
  • RuStore应用程序应被允许安装应用程序。

实现示例

要了解如何正确集成用于推送通知的包,建议查看示例应用程序。

示例应用程序

在项目中添加插件

要将插件添加到项目中,请执行以下命令:

flutter pub add flutter_rustore_update

这将在pubspec.yaml文件中添加以下行:

dependencies:
    flutter_rustore_update: ^7.0.1

检查更新

在请求更新之前,请检查您的应用程序是否有可用更新。为此,请调用info()方法。调用此方法会检查以下条件:

  • 用户设备上应安装有RuStore。
  • 设备上的RuStoreApp版本应是最新的。
  • 用户和应用程序不应在RuStore中被阻止。

响应此方法时,您将收到一个包含更新信息的对象。

RustoreUpdateClient.info().then((info) {
    print(info);
}).catchError((err) {
    print(err);
});

info对象包含一组参数,用于确定是否有更新可用:

  • updateAvailability - 更新的可用性:
    • UPDATE_AILABILITY_NOT_AVAILABLE - 不需要更新。
    • UPDATE_AILABILITY_AVAILABLE - 需要下载更新或更新已下载到用户设备。
    • UPDATE_AILABILITY_IN_PROGRESS - 更新正在下载或安装已经开始。
    • UPDATE_AILABILITY_UNKNOWN - 默认状态。
  • installStatus - 更新安装状态,如果用户当前正在安装更新:
    • INSTALL_STATUS_DOWNLOADED - 已下载。
    • INSTALL_STATUS_DOWNLOADING - 正在下载。
    • INSTALL_STATUS_FAILED - 发生错误。
    • INSTALL_STATUS_INSTALLING - 正在安装。
    • INSTALL_STATUS_PENDING - 等待中。
    • INSTALL_STATUS_UNKNOWN - 默认状态。

只有当updateAvailability字段包含UPDATE_AILABILITY_AVAILABLE值时,才能启动下载。

该方法可能会返回错误。有关错误列表的详细信息,请参见可能的错误部分。

下载更新

确认更新可用后,您可以请求用户下载更新,但在此之前,您需要使用listener()方法启动下载状态监听器:

RustoreUpdateClient.listener((value) {
  print("listener installStatus ${value.installStatus}");
  print("listener bytesDownloaded ${value.bytesDownloaded}");
  print("listener totalBytesToDownload ${value.totalBytesToDownload}");
  print("listener installErrorCode ${value.installErrorCode}");

  if (value.installStatus == INSTALL_STATUS_DOWNLOADED) {
    // 可以在此处调用complete()方法
  }
});

state对象描述了更新下载的当前状态。它包含以下内容:

  • installStatus - 更新安装状态,如果用户当前正在安装更新:
    • INSTALL_STATUS_DOWNLOADED - 已下载。
    • INSTALL_STATUS_DOWNLOADING - 正在下载。
    • INSTALL_STATUS_FAILED - 发生错误。
    • INSTALL_STATUS_INSTALLING - 正在安装。
    • INSTALL_STATUS_PENDING - 等待中。
    • INSTALL_STATUS_UNKNOWN - 默认状态。
  • bytesDownloaded - 已下载的字节数。
  • totalBytesToDownload - 总共需要下载的字节数。
  • installErrorCode - 下载期间的错误代码。有关可能的错误的详细信息,请参见可能的错误部分。

延迟更新

从RuStore UI开始下载

要启动应用程序更新的下载,请调用download()方法。

RustoreUpdateClient.download().then((value) {
  print("download code ${value.code}");
}).catchError((err) {
  print("download err ${err}");
});

如果用户确认下载更新,则value.code = ACTIVITY_RESULT_OK;如果拒绝,则value.code = ACTIVITY_RESULT_CANCELED

调用该方法后,您可以使用监听器跟踪更新下载的状态。如果在监听器中收到INSTALL_STATUS_DOWNLOADED状态,您可以调用complete()方法来安装更新。建议通知用户更新已准备好安装。

该方法可能会返回错误。有关错误列表的详细信息,请参见可能的错误部分。

强制更新

从RuStore UI开始下载

要启动强制更新应用程序的下载,请调用immediate()方法。

RustoreUpdateClient.immediate().then((value) {
  print("silent code ${value.code}");
}).catchError((err) {
  print("immediate err ${err}");
});

resultCode (Int):

  • ACTIVITY_RESULT_OK (-1) - 更新完成,由于应用程序在更新过程中终止,可能不会收到代码。
  • ACTIVITY_RESULT_CANCELED (0) - 用户中断流程,或发生错误。假定在这种情况下应终止应用程序。
  • ACTIVITY_RESULT_NOT_FOUND (2) - RuStore未安装,或安装的版本不支持强制更新(RuStore versionCode < 191)。

throwable - 更新开始场景的错误。

成功更新后无需进一步操作。

静默更新

从RuStore UI无UI下载

对于这种类型的更新,建议实现自己的界面。

要启动静默更新应用程序的下载,请调用silent()方法。

RustoreUpdateClient.silent().then((value) {
  print("silent code ${value.code}");
}).catchError((err) {
  print("silent err ${err}");
});

调用then时,如果code = ACTIVITY_RESULT_OK,则会注册下载更新的任务。

在这种场景下,只能通过then调用ACTIVITY_RESULT_OK,或者通过catchError

调用该方法后,您可以使用监听器跟踪更新下载的状态。

在收到INSTALL_STATUS_DOWNLOADED状态后,您可以调用安装更新的方法。建议通知用户更新已准备好安装。

安装更新

灵活的更新完成

从RuStore UI开始更新

下载完更新的apk文件后,您可以启动更新安装。要启动更新安装,请调用completeUpdateFlexible()方法。

RustoreUpdateClient.completeUpdateFlexible().catchError((err) {
  print("completeUpdateFlexible err ${err}");
});
  1. 用户将看到更新完成的UI对话框。
  2. 成功更新后,应用程序将重新启动。

更新通过Android原生工具进行。成功更新后,应用程序将重新启动。

静默的更新完成

从RuStore UI无UI开始更新

下载完更新的apk文件后,您可以启动更新安装。要启动更新安装,请调用completeUpdateSilent()方法。

RustoreUpdateClient.completeUpdateSilent().catchError((err) {
  print("completeUpdateSilent err ${err}");
});
  1. 不会显示更新完成的UI对话框。
  2. 成功更新后,应用程序将关闭。

更新通过Android原生工具进行。成功更新后,应用程序将关闭。

在更新过程中可能会出现错误。有关详细信息,请参见可能的错误部分。

可能的错误

如果您收到onFailure响应,则不建议自行向用户显示错误。错误显示可能会影响用户体验。

可能的错误列表:

  • UPDATE_ERROR_DOWNLOAD - 下载错误。
  • UPDATE_ERROR_BLOCKED - 安装被系统阻止。
  • UPDATE_ERROR_INVALID_APK - 更新的APK无效。
  • UPDATE_ERROR_CONFLICT - 与当前应用程序版本冲突。
  • UPDATE_ERROR_STORAGE - 设备存储空间不足。
  • UPDATE_ERROR_INCOMPATIBLE - 与设备不兼容。
  • UPDATE_ERROR_APP_NOT_OWNED - 应用程序未购买。
  • UPDATE_ERROR_INTERNAL_ERROR - 内部错误。
  • UPDATE_ERROR_ABORTED - 用户拒绝安装更新。
  • UPDATE_ERROR_APK_NOT_FOUND - 用于启动安装的APK未找到。
  • UPDATE_ERROR_EXTERNAL_SOURCE_DENIED - 启动更新被禁止。例如,在第一个方法中返回更新不可用的响应,但用户调用了第二个方法。

示例代码

import 'package:flutter/material.dart';
import 'package:flutter_rustore_update/const.dart';
import 'package:flutter_rustore_update/flutter_rustore_update.dart';

void main() {
  runApp(const App());
}

class App extends StatefulWidget {
  const App({super.key});

  [@override](/user/override)
  State<App> createState() => _AppState();
}

class _AppState extends State<App> {
  int availableVersionCode = 0;
  int installStatus = 0;
  String packageName = "";
  int updateAvailability = 0;
  String infoErr = "";

  int bytesDownloaded = 0;
  int totalBytesToDownload = 0;
  int installErrorCode = 0;

  String completeErr = "";
  int installCode = 0;

  String updateError = "";
  String silentError = "";
  String immediateError = "";

  [@override](/user/override)
  void initState() {
    super.initState();
  }

  void info() {
    RustoreUpdateClient.info().then((info) {
      setState(() {
        availableVersionCode = info.availableVersionCode;
        installStatus = info.installStatus;
        packageName = info.packageName;
        updateAvailability = info.updateAvailability;
      });
    }).catchError((err) {
      print(err);
      setState(() {
        infoErr = err.message;
      });
    });
  }

  void update() {
    RustoreUpdateClient.info().then((info) {
      setState(() {
        availableVersionCode = info.availableVersionCode;
        installStatus = info.installStatus;
        packageName = info.packageName;
        updateAvailability = info.updateAvailability;
      });

      if (info.updateAvailability == UPDATE_AILABILITY_AVAILABLE) {
        RustoreUpdateClient.listener((value) {
          print("listener installStatus ${value.installStatus}");
          print("listener bytesDownloaded ${value.bytesDownloaded}");
          print("listener totalBytesToDownload ${value.totalBytesToDownload}");
          print("listener installErrorCode ${value.installErrorCode}");

          setState(() {
            installStatus = value.installStatus;
            bytesDownloaded = value.bytesDownloaded;
            totalBytesToDownload = value.totalBytesToDownload;
            installErrorCode = value.installErrorCode;
          });

          if (value.installStatus == INSTALL_STATUS_DOWNLOADED) {
            RustoreUpdateClient.completeUpdateFlexible().catchError((err) {
              print("completeUpdateFlexible err ${err}");
              setState(() {
                completeErr = err.message;
              });
            });
          }
        });

        RustoreUpdateClient.download().then((value) {
          print("download code ${value.code}");
          setState(() {
            installCode = value.code;
          });
          if (value.code == ACTIVITY_RESULT_CANCELED) {
            print("user cancel update");
          }
        }).catchError((err) {
          print("download err ${err}");
          setState(() {
            updateError = err.message;
          });
        });
      }
    }).catchError((err) {
      print(err.toString());
      setState(() {
        infoErr = err.message;
      });
    });
  }

  void immediate() {
    RustoreUpdateClient.info().then((info) {
      setState(() {
        availableVersionCode = info.availableVersionCode;
        installStatus = info.installStatus;
        packageName = info.packageName;
        updateAvailability = info.updateAvailability;
      });

      if (info.updateAvailability == UPDATE_AILABILITY_AVAILABLE) {
        RustoreUpdateClient.listener((value) {
          print("listener installStatus ${value.installStatus}");
          print("listener bytesDownloaded ${value.bytesDownloaded}");
          print("listener totalBytesToDownload ${value.totalBytesToDownload}");
          print("listener installErrorCode ${value.installErrorCode}");

          setState(() {
            installStatus = value.installStatus;
            bytesDownloaded = value.bytesDownloaded;
            totalBytesToDownload = value.totalBytesToDownload;
            installErrorCode = value.installErrorCode;
          });
        });

        RustoreUpdateClient.immediate().then((value) {
          print("immediate code ${value.code}");
          setState(() {
            installCode = value.code;
          });
        }).catchError((err) {
          print("immediate err ${err}");
          setState(() {
            immediateError = err.message;
          });
        });
      }
    }).catchError((err) {
      print(err.toString());
      setState(() {
        infoErr = err.message;
      });
    });
  }

  void silent() {
    RustoreUpdateClient.info().then((info) {
      setState(() {
        availableVersionCode = info.availableVersionCode;
        installStatus = info.installStatus;
        packageName = info.packageName;
        updateAvailability = info.updateAvailability;
      });

      if (info.updateAvailability == UPDATE_AILABILITY_AVAILABLE) {
        RustoreUpdateClient.listener((value) {
          print("listener installStatus ${value.installStatus}");
          print("listener bytesDownloaded ${value.bytesDownloaded}");
          print("listener totalBytesToDownload ${value.totalBytesToDownload}");
          print("listener installErrorCode ${value.installErrorCode}");

          setState(() {
            installStatus = value.installStatus;
            bytesDownloaded = value.bytesDownloaded;
            totalBytesToDownload = value.totalBytesToDownload;
            installErrorCode = value.installErrorCode;
          });

          if (value.installStatus == INSTALL_STATUS_DOWNLOADED) {
            RustoreUpdateClient.completeUpdateSilent().catchError((err) {
              print("completeUpdateSilent err ${err}");

              setState(() {
                completeErr = err.message;
              });
            });
          }
        });

        RustoreUpdateClient.silent().then((value) {
          print("silent code ${value.code}");
          setState(() {
            installCode = value.code;
          });
        }).catchError((err) {
          print("silent err ${err}");
          setState(() {
            silentError = err.message;
          });
        });
      }
    }).catchError((err) {
      print(err.toString());
      setState(() {
        infoErr = err.message;
      });
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('插件示例应用'),
        ),
        body: SingleChildScrollView(
          child: Padding(
            padding: EdgeInsets.all(12),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                OutlinedButton(onPressed: info, child: Text("检查更新")),
                SizedBox(height: 24),
                Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text('信息:'),
                    Text('availableVersionCode: $availableVersionCode'),
                    Text('installStatus: $installStatus'),
                    Text('packageName: $packageName'),
                    Text('updateAvailability: $updateAvailability'),
                    Text('错误: $infoErr'),
                  ],
                ),
                SizedBox(height: 48),
                Row(
                  children: [
                    OutlinedButton(onPressed: update, child: Text("更新")),
                    SizedBox(width: 12),
                    OutlinedButton(onPressed: immediate, child: Text("强制更新")),
                    SizedBox(width: 12),
                    OutlinedButton(onPressed: silent, child: Text("静默更新")),
                  ],
                ),
                SizedBox(height: 48),
                Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text('安装:'),
                    Text('bytesDownloaded: $bytesDownloaded'),
                    Text('totalBytesToDownload: $totalBytesToDownload'),
                    Text('installErrorCode: $installErrorCode'),
                    Text('completeErr: $completeErr'),
                    Text('installCode: $installCode'),
                    SizedBox(height: 12),
                    Text('错误'),
                    Text('updateError: $updateError'),
                    Text('silentError: $silentError'),
                    Text('immediateError: $immediateError'),
                  ],
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

更多关于Flutter应用内更新插件flutter_rustore_update的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter应用内更新插件flutter_rustore_update的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter应用中实现应用内更新功能,flutter_rustore_update 是一个可以选择的插件(请注意,插件名称可能因版本或社区维护而有所不同,这里假设插件名正确)。这个插件通常用于从指定的服务器下载新版本的应用 APK 文件,并提示用户进行安装。以下是如何使用 flutter_rustore_update 插件的一个基本示例。

1. 添加依赖

首先,在你的 pubspec.yaml 文件中添加对 flutter_rustore_update 的依赖。由于我无法实时检查最新的包名和版本,这里假设包名为 flutter_rustore_update,你需要根据实际情况替换。

dependencies:
  flutter:
    sdk: flutter
  flutter_rustore_update: ^x.y.z  # 替换为实际版本号

然后运行 flutter pub get 来获取依赖。

2. 导入插件

在你的 Dart 文件中导入插件:

import 'package:flutter_rustore_update/flutter_rustore_update.dart';

3. 检查并下载更新

你可以在应用启动时或某个特定的时间点检查更新。以下是一个简单的示例,演示如何检查更新并下载 APK 文件。

void checkForUpdates() async {
  try {
    // 假设你有一个API端点提供最新版本信息
    String updateUrl = "https://yourserver.com/latest_version.json";
    
    // 使用dio或其他HTTP客户端获取最新版本信息
    var response = await Dio().get(updateUrl);
    
    // 解析JSON响应,这里假设响应包含一个名为'apkUrl'的字段
    var data = response.data;
    String apkUrl = data['apkUrl'] as String;
    
    // 使用flutter_rustore_update插件检查并下载更新
    var updateManager = FlutterRustoreUpdate();
    
    // 检查是否有新版本
    bool hasUpdate = await updateManager.checkNeedUpdate(apkUrl: apkUrl);
    
    if (hasUpdate) {
      // 显示下载对话框
      var result = await updateManager.downloadApk(apkUrl: apkUrl);
      
      if (result) {
        // 下载完成后提示用户安装
        updateManager.installApk();
      } else {
        // 下载失败处理
        print("下载更新失败");
      }
    } else {
      // 没有新版本
      print("当前版本已是最新");
    }
  } catch (e) {
    // 错误处理
    print("检查更新时发生错误: $e");
  }
}

4. 调用检查更新函数

你可以在应用的主入口文件(如 main.dart)中调用 checkForUpdates 函数。

void main() {
  runApp(MyApp());
  
  // 在适当的时间点调用检查更新函数
  // 例如,在MyApp的initState中或使用定时器
  checkForUpdates();
}

注意事项

  1. 权限:确保你的应用有必要的权限,如网络访问权限和写入外部存储权限(如果APK需要保存到设备)。
  2. 安全性:验证从服务器下载的APK文件的完整性,防止被篡改。
  3. 用户体验:提供友好的用户提示,告知用户有更新可用,并引导用户进行下载和安装。

这个示例是一个基本的实现,你可能需要根据实际需求进行扩展和修改。确保在发布应用前进行充分的测试。

回到顶部