flutter 配置安卓的签名

背景

最近遇到一个需求,需要实现app的热更新,了解了一下热更新方案时间的时间有点久,就做了个app升级的过渡版本,然后遇到问题 真机安装遇到签名不一致的问题

如下

安装过程

版本升级的代码如下

Dart 复制代码
 ///版本更新检查
  static Future<VersionEntity> checkVersionUpdate() async {
    if (isWeb) {
      return VersionEntity(need: false);
    }

    PackageInfo packageInfo = await PackageInfo.fromPlatform();
    String version = packageInfo.version;

    // String jsonStr = await rootBundle.loadString('assets/json/version.json');
    // Map<String, dynamic> jsonData = json.decode(jsonStr);
    Map<String, dynamic> jsonData = await getVersion();
    VersionEntity versionEntity = VersionEntity.fromJson(jsonData);

    Version latestVersion = Version.parse(versionEntity.version ?? version);
    Version currentVersion = Version.parse(version);
    print(1);
    if (latestVersion > currentVersion) {
      _toUpdate(versionEntity);
    } else {
      return VersionEntity(need: false);
    }

    /// 非强制更新
    // if (versionEntity.data!.need! && !(versionEntity.data!.necessary!)) {
    //   _toUpdate(versionEntity);
    // } // 强制更新
    // else if (versionEntity.data!.need! && versionEntity.data!.necessary!) {
    //   _toUpdate(versionEntity);
    // } else {
    //   // KLogUtil.d('无需更新');
    // }

    return versionEntity;
  }

  ///版本更新弹窗
  static _toUpdate(VersionEntity versionEntity) {
    // 进度
    String progress = '';
    double downloadProgress = 0.0;
    bool downloadStart = false;
    // 禁止返回
    bool disBack = versionEntity.necessary!;
    Get.bottomSheet(
      isDismissible: !(versionEntity.necessary!),
      enableDrag: !(versionEntity.necessary!),
      StatefulBuilder(
        builder: (context, state) {
          String fileAddress = '';
          return WillPopScope(
            onWillPop: () => _onBackPressed(disBack),
            child: AppToast.bottomSheetContainer(
              padding: EdgeInsets.fromLTRB(20.w, 5.h, 20.w, 20.h),
              height: 429.h,
              bgColor: const Color(0xFF25272B),
              child: Column(
                children: [
                  Container(
                    width: 36.w,
                    height: 4.h,
                    decoration: BoxDecoration(
                      color: versionEntity.necessary!
                          ? Colors.transparent
                          : const Color(0xFF3B3D40),
                      borderRadius: BorderRadius.circular(3.r),
                    ),
                  ),
                  10.verticalSpace,
                  Assets.images.versionUpdate
                      .image(width: 100.r, height: 100.r),
                  Padding(
                    padding: EdgeInsets.symmetric(vertical: 20.h),
                    child: Text(
                      "Update to ${versionEntity.version}",
                      style: TextStyle(
                        color: Colors.white,
                        fontSize: 16.sp,
                      ),
                    ),
                  ),
                  Padding(
                    padding: EdgeInsets.symmetric(vertical: 5.h),
                    child: Row(
                      children: [
                        Text(
                          "更新内容:",
                          style: TextStyle(
                            color: Colors.white,
                            fontSize: 14.sp,
                          ),
                        ),
                      ],
                    ),
                  ),
                  Expanded(
                    child: ListView.builder(
                      itemCount: versionEntity.contents?.length ?? 0,
                      itemBuilder: (context, index) {
                        return Text(
                          versionEntity!.contents![index],
                          style: TextStyle(
                            color: Colors.grey,
                            fontSize: 13.sp,
                          ),
                        );
                      },
                    ),
                  ),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: [
                      versionEntity.necessary == false && disBack == false
                          ? Expanded(
                              child: SizedBox(
                                height: 56.h,
                                child: ElevatedButton(
                                  onPressed: () {
                                    /// 每天弹一次更新
                                    Get.back();
                                  },
                                  style: ButtonStyle(
                                    backgroundColor: MaterialStateProperty.all(
                                        const Color(0xFF25272B)),
                                    shape: MaterialStateProperty.all(
                                      RoundedRectangleBorder(
                                        borderRadius:
                                            BorderRadius.circular(16.r),
                                      ),
                                    ),
                                  ),
                                  child: Text(
                                    "Not Now ",
                                    style: TextStyle(
                                      color: const Color(0xFF25272B),
                                      fontSize: 14.sp,
                                      fontWeight: FontWeight.bold,
                                    ),
                                  ),
                                ),
                              ),
                            )
                          : Container(),
                      versionEntity.necessary == false && disBack == false
                          ? SizedBox(width: 15.w)
                          : Container(),
                      Visibility(
                        visible: progress == '',
                        child: Expanded(
                          child: SizedBox(
                            height: 56.h,
                            child: ElevatedButton(
                              onPressed: () async {
                                if (Platform.isIOS) {
                                  InstallPlugin.install(iosAppStoreUrl);
                                  // AppInstaller.goStore(
                                  //     iosAppStoreUrl, "id1375433239",
                                  //     review: true);
                                  return;
                                }
                                if (progress == '') {
                                  state(() {
                                    downloadStart = true;
                                  });
                                  disBack = true; //开始下载后禁止退出弹窗
                                  final filePath =
                                      await getExternalStorageDirectory();
                                  fileAddress = '${filePath!.path}/app-LH.apk';
                                  try {
                                    Dio dio = Dio(
                                      BaseOptions(
                                        connectTimeout:
                                            const Duration(milliseconds: 10000),
                                        receiveTimeout: const Duration(
                                            milliseconds: 100000),
                                        sendTimeout:
                                            const Duration(milliseconds: 10000),
                                      ),
                                    );
                                    await dio.download(
                                      versionEntity.url!,
                                      fileAddress,
                                      onReceiveProgress: (received, total) {
                                        if (total != -1) {
                                          state(() {
                                            progress =
                                                "${(received / total * 100).toStringAsFixed(2)}%";
                                            downloadProgress = received / total;
                                          });
                                        }
                                      },
                                    ).then(
                                      (response) async {
                                        if (response.statusMessage == 'OK') {
                                          // AppInstaller.installApk(fileAddress);
                                          InstallPlugin.install(fileAddress);
                                          // await AppInstaller.installApk(
                                          //     fileAddress);
                                        } else {
                                          AppToast.toast(
                                              stateType: StateType.error,
                                              tips: "Failed to download");
                                          disBack = false;
                                        }
                                      },
                                    );
                                  } catch (e) {
                                    print(e);
                                  }
                                }
                              },
                              style: ButtonStyle(
                                backgroundColor: MaterialStateProperty.all(
                                    const Color(0xFF4677FF)),
                                shape: MaterialStateProperty.all(
                                  RoundedRectangleBorder(
                                      borderRadius:
                                          BorderRadius.circular(16.r)),
                                ),
                              ),
                              child: downloadStart
                                  ? CircularProgressIndicator(
                                      valueColor: const AlwaysStoppedAnimation(
                                          Colors.white),
                                      backgroundColor:
                                          Colors.white.withOpacity(.1),
                                    )
                                  : Text("Update",
                                      style: TextStyle(
                                        fontSize: 14.sp,
                                        fontWeight: FontWeight.bold,
                                        color: Colors.white,
                                      )),
                            ),
                          ),
                        ),
                      ),
                      Visibility(
                        visible: progress != '',
                        child: Expanded(
                          child: Container(
                            decoration: BoxDecoration(
                              color: Colors.white.withOpacity(.1),
                              borderRadius: BorderRadius.circular(16.r),
                            ),
                            clipBehavior: Clip.hardEdge,
                            child: Stack(
                              alignment: AlignmentDirectional.center,
                              children: [
                                InkWell(
                                  child: SizedBox(
                                    height: 56.h,
                                    width: double.infinity,
                                    child: LinearProgressIndicator(
                                      value: downloadProgress,
                                      backgroundColor: Colors.transparent,
                                      valueColor: const AlwaysStoppedAnimation(
                                          Color(0xFF4677FF)),
                                    ),
                                  ),
                                  onTap: () async {
                                    if (progress == "100.00%") {
                                      await InstallPlugin.install(fileAddress);
                                    }
                                  },
                                ),
                                Text(
                                    progress == ''
                                        ? "Update"
                                        : (progress == "100.00%"
                                            ? "安装app"
                                            : progress),
                                    style: TextStyle(
                                      fontSize: 14.sp,
                                      fontWeight: FontWeight.bold,
                                    ))
                              ],
                            ),
                          ),
                        ),
                      ),
                    ],
                  ),
                ],
              ),
            ),
          );
        },
      ),
    );
  }

  static Future<bool> _onBackPressed(bool necessary) async {
    // 强更 禁止退出弹窗
    if (necessary) {
      return false;
    } else {
      return true;
    }
  }



class VersionEntity {
  bool? necessary;
  bool? need;
  String? version;
  String? url;
  List<String>? contents;

  VersionEntity({this.necessary, this.need, this.version, this.url});

  VersionEntity.fromJson(Map<String, dynamic> json) {
    necessary = json['necessary'];
    need = json['need'];
    version = json['version'];
    url = json['url'];
    contents = json['contents'].cast<String>();
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = <String, dynamic>{};
    data['necessary'] = necessary;
    data['need'] = need;
    data['version'] = version;
    data['url'] = url;
    data['contents'] = contents;
    return data;
  }
}

下面是解决签名不一致的问题 解决方案

keytool -genkey -v -keystore ./key.jks -keyalg RSA -keysize 2048 -validity 20000 -alias HL

很多会遇到 原因是keytool 是java的库 需要配置java环境 或者 在java目录下进行操作

bash 复制代码
bash: keytool: command not found

在java环境目录 打开cmd 执行后 复制key.jks 到你的安卓目录下 (android/)

在安卓目录下(android/) 新增key.properties 文件

写入 密码是你自己设置的密码

bash 复制代码
storePassword=789asd
keyPassword=789asd
keyAlias=LH
storeFile=../key.jks

最后在你 (android/app) 下的build.gradle 配置 buildTypes

bash 复制代码
// 最上面
def keystorePropertiesFile = rootProject.file("key.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))


 
//签名信息
    signingConfigs {
        release {
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storeFile file(keystoreProperties['storeFile'])
            storePassword keystoreProperties['storePassword']
        }
    }


    buildTypes {
        release {
            // TODO: Add your own signing config for the release build.
            // Signing with the debug keys for now, so `flutter run --release` works.
            debuggable false
            minifyEnabled true
            // signingConfig signingConfigs.debug
            signingConfig signingConfigs.release
            ndk{ // 必须加入这部分,否则可能导致编译成功的release包在真机中会闪退
                abiFilters "armeabi-v7a"
            }
        }
        debug {
            ndk {
                //这里要加上,否则debug包会出问题,后面三个为可选,x86建议加上不然部分模拟器回报错
                abiFilters "armeabi", "armeabi-v7a", "arm64-v8a",  "x86"
            }
        }

    }
Dart 复制代码
flutter build apk --release

这样就解决了升级遇到的签名版本不一致的问题

相关推荐
华仔啊1 小时前
Stream 代码越写越难看?JDFrame 让 Java 逻辑回归优雅
java·后端
ray_liang1 小时前
用六边形架构与整洁架构对比是伪命题?
java·架构
Ray Liang2 小时前
用六边形架构与整洁架构对比是伪命题?
java·python·c#·架构设计
Java水解3 小时前
Java 中间件:Dubbo 服务降级(Mock 机制)
java·后端
砖厂小工4 小时前
用 GLM + OpenClaw 打造你的 AI PR Review Agent — 让龙虾帮你审代码
android·github
张拭心5 小时前
春节后,有些公司明确要求 AI 经验了
android·前端·人工智能
张拭心5 小时前
Android 17 来了!新特性介绍与适配建议
android·前端
SimonKing7 小时前
OpenCode AI辅助编程,不一样的编程思路,不写一行代码
java·后端·程序员
FastBean7 小时前
Jackson View Extension Spring Boot Starter
java·后端
Kapaseker8 小时前
Compose 进阶—巧用 GraphicsLayer
android·kotlin