当前位置:   article > 正文

flutter 配置安卓的签名_flutter 防止二次签名

flutter 防止二次签名

背景

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

如下

安装过程

版本升级的代码如下

  1. ///版本更新检查
  2. static Future<VersionEntity> checkVersionUpdate() async {
  3. if (isWeb) {
  4. return VersionEntity(need: false);
  5. }
  6. PackageInfo packageInfo = await PackageInfo.fromPlatform();
  7. String version = packageInfo.version;
  8. // String jsonStr = await rootBundle.loadString('assets/json/version.json');
  9. // Map<String, dynamic> jsonData = json.decode(jsonStr);
  10. Map<String, dynamic> jsonData = await getVersion();
  11. VersionEntity versionEntity = VersionEntity.fromJson(jsonData);
  12. Version latestVersion = Version.parse(versionEntity.version ?? version);
  13. Version currentVersion = Version.parse(version);
  14. print(1);
  15. if (latestVersion > currentVersion) {
  16. _toUpdate(versionEntity);
  17. } else {
  18. return VersionEntity(need: false);
  19. }
  20. /// 非强制更新
  21. // if (versionEntity.data!.need! && !(versionEntity.data!.necessary!)) {
  22. // _toUpdate(versionEntity);
  23. // } // 强制更新
  24. // else if (versionEntity.data!.need! && versionEntity.data!.necessary!) {
  25. // _toUpdate(versionEntity);
  26. // } else {
  27. // // KLogUtil.d('无需更新');
  28. // }
  29. return versionEntity;
  30. }
  31. ///版本更新弹窗
  32. static _toUpdate(VersionEntity versionEntity) {
  33. // 进度
  34. String progress = '';
  35. double downloadProgress = 0.0;
  36. bool downloadStart = false;
  37. // 禁止返回
  38. bool disBack = versionEntity.necessary!;
  39. Get.bottomSheet(
  40. isDismissible: !(versionEntity.necessary!),
  41. enableDrag: !(versionEntity.necessary!),
  42. StatefulBuilder(
  43. builder: (context, state) {
  44. String fileAddress = '';
  45. return WillPopScope(
  46. onWillPop: () => _onBackPressed(disBack),
  47. child: AppToast.bottomSheetContainer(
  48. padding: EdgeInsets.fromLTRB(20.w, 5.h, 20.w, 20.h),
  49. height: 429.h,
  50. bgColor: const Color(0xFF25272B),
  51. child: Column(
  52. children: [
  53. Container(
  54. width: 36.w,
  55. height: 4.h,
  56. decoration: BoxDecoration(
  57. color: versionEntity.necessary!
  58. ? Colors.transparent
  59. : const Color(0xFF3B3D40),
  60. borderRadius: BorderRadius.circular(3.r),
  61. ),
  62. ),
  63. 10.verticalSpace,
  64. Assets.images.versionUpdate
  65. .image(width: 100.r, height: 100.r),
  66. Padding(
  67. padding: EdgeInsets.symmetric(vertical: 20.h),
  68. child: Text(
  69. "Update to ${versionEntity.version}",
  70. style: TextStyle(
  71. color: Colors.white,
  72. fontSize: 16.sp,
  73. ),
  74. ),
  75. ),
  76. Padding(
  77. padding: EdgeInsets.symmetric(vertical: 5.h),
  78. child: Row(
  79. children: [
  80. Text(
  81. "更新内容:",
  82. style: TextStyle(
  83. color: Colors.white,
  84. fontSize: 14.sp,
  85. ),
  86. ),
  87. ],
  88. ),
  89. ),
  90. Expanded(
  91. child: ListView.builder(
  92. itemCount: versionEntity.contents?.length ?? 0,
  93. itemBuilder: (context, index) {
  94. return Text(
  95. versionEntity!.contents![index],
  96. style: TextStyle(
  97. color: Colors.grey,
  98. fontSize: 13.sp,
  99. ),
  100. );
  101. },
  102. ),
  103. ),
  104. Row(
  105. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  106. children: [
  107. versionEntity.necessary == false && disBack == false
  108. ? Expanded(
  109. child: SizedBox(
  110. height: 56.h,
  111. child: ElevatedButton(
  112. onPressed: () {
  113. /// 每天弹一次更新
  114. Get.back();
  115. },
  116. style: ButtonStyle(
  117. backgroundColor: MaterialStateProperty.all(
  118. const Color(0xFF25272B)),
  119. shape: MaterialStateProperty.all(
  120. RoundedRectangleBorder(
  121. borderRadius:
  122. BorderRadius.circular(16.r),
  123. ),
  124. ),
  125. ),
  126. child: Text(
  127. "Not Now ",
  128. style: TextStyle(
  129. color: const Color(0xFF25272B),
  130. fontSize: 14.sp,
  131. fontWeight: FontWeight.bold,
  132. ),
  133. ),
  134. ),
  135. ),
  136. )
  137. : Container(),
  138. versionEntity.necessary == false && disBack == false
  139. ? SizedBox(width: 15.w)
  140. : Container(),
  141. Visibility(
  142. visible: progress == '',
  143. child: Expanded(
  144. child: SizedBox(
  145. height: 56.h,
  146. child: ElevatedButton(
  147. onPressed: () async {
  148. if (Platform.isIOS) {
  149. InstallPlugin.install(iosAppStoreUrl);
  150. // AppInstaller.goStore(
  151. // iosAppStoreUrl, "id1375433239",
  152. // review: true);
  153. return;
  154. }
  155. if (progress == '') {
  156. state(() {
  157. downloadStart = true;
  158. });
  159. disBack = true; //开始下载后禁止退出弹窗
  160. final filePath =
  161. await getExternalStorageDirectory();
  162. fileAddress = '${filePath!.path}/app-LH.apk';
  163. try {
  164. Dio dio = Dio(
  165. BaseOptions(
  166. connectTimeout:
  167. const Duration(milliseconds: 10000),
  168. receiveTimeout: const Duration(
  169. milliseconds: 100000),
  170. sendTimeout:
  171. const Duration(milliseconds: 10000),
  172. ),
  173. );
  174. await dio.download(
  175. versionEntity.url!,
  176. fileAddress,
  177. onReceiveProgress: (received, total) {
  178. if (total != -1) {
  179. state(() {
  180. progress =
  181. "${(received / total * 100).toStringAsFixed(2)}%";
  182. downloadProgress = received / total;
  183. });
  184. }
  185. },
  186. ).then(
  187. (response) async {
  188. if (response.statusMessage == 'OK') {
  189. // AppInstaller.installApk(fileAddress);
  190. InstallPlugin.install(fileAddress);
  191. // await AppInstaller.installApk(
  192. // fileAddress);
  193. } else {
  194. AppToast.toast(
  195. stateType: StateType.error,
  196. tips: "Failed to download");
  197. disBack = false;
  198. }
  199. },
  200. );
  201. } catch (e) {
  202. print(e);
  203. }
  204. }
  205. },
  206. style: ButtonStyle(
  207. backgroundColor: MaterialStateProperty.all(
  208. const Color(0xFF4677FF)),
  209. shape: MaterialStateProperty.all(
  210. RoundedRectangleBorder(
  211. borderRadius:
  212. BorderRadius.circular(16.r)),
  213. ),
  214. ),
  215. child: downloadStart
  216. ? CircularProgressIndicator(
  217. valueColor: const AlwaysStoppedAnimation(
  218. Colors.white),
  219. backgroundColor:
  220. Colors.white.withOpacity(.1),
  221. )
  222. : Text("Update",
  223. style: TextStyle(
  224. fontSize: 14.sp,
  225. fontWeight: FontWeight.bold,
  226. color: Colors.white,
  227. )),
  228. ),
  229. ),
  230. ),
  231. ),
  232. Visibility(
  233. visible: progress != '',
  234. child: Expanded(
  235. child: Container(
  236. decoration: BoxDecoration(
  237. color: Colors.white.withOpacity(.1),
  238. borderRadius: BorderRadius.circular(16.r),
  239. ),
  240. clipBehavior: Clip.hardEdge,
  241. child: Stack(
  242. alignment: AlignmentDirectional.center,
  243. children: [
  244. InkWell(
  245. child: SizedBox(
  246. height: 56.h,
  247. width: double.infinity,
  248. child: LinearProgressIndicator(
  249. value: downloadProgress,
  250. backgroundColor: Colors.transparent,
  251. valueColor: const AlwaysStoppedAnimation(
  252. Color(0xFF4677FF)),
  253. ),
  254. ),
  255. onTap: () async {
  256. if (progress == "100.00%") {
  257. await InstallPlugin.install(fileAddress);
  258. }
  259. },
  260. ),
  261. Text(
  262. progress == ''
  263. ? "Update"
  264. : (progress == "100.00%"
  265. ? "安装app"
  266. : progress),
  267. style: TextStyle(
  268. fontSize: 14.sp,
  269. fontWeight: FontWeight.bold,
  270. ))
  271. ],
  272. ),
  273. ),
  274. ),
  275. ),
  276. ],
  277. ),
  278. ],
  279. ),
  280. ),
  281. );
  282. },
  283. ),
  284. );
  285. }
  286. static Future<bool> _onBackPressed(bool necessary) async {
  287. // 强更 禁止退出弹窗
  288. if (necessary) {
  289. return false;
  290. } else {
  291. return true;
  292. }
  293. }
  294. class VersionEntity {
  295. bool? necessary;
  296. bool? need;
  297. String? version;
  298. String? url;
  299. List<String>? contents;
  300. VersionEntity({this.necessary, this.need, this.version, this.url});
  301. VersionEntity.fromJson(Map<String, dynamic> json) {
  302. necessary = json['necessary'];
  303. need = json['need'];
  304. version = json['version'];
  305. url = json['url'];
  306. contents = json['contents'].cast<String>();
  307. }
  308. Map<String, dynamic> toJson() {
  309. final Map<String, dynamic> data = <String, dynamic>{};
  310. data['necessary'] = necessary;
  311. data['need'] = need;
  312. data['version'] = version;
  313. data['url'] = url;
  314. data['contents'] = contents;
  315. return data;
  316. }
  317. }

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

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

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

bash: keytool: command not found

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

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

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

  1. storePassword=789asd
  2. keyPassword=789asd
  3. keyAlias=LH
  4. storeFile=../key.jks

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

  1. // 最上面
  2. def keystorePropertiesFile = rootProject.file("key.properties")
  3. def keystoreProperties = new Properties()
  4. keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
  5. //签名信息
  6. signingConfigs {
  7. release {
  8. keyAlias keystoreProperties['keyAlias']
  9. keyPassword keystoreProperties['keyPassword']
  10. storeFile file(keystoreProperties['storeFile'])
  11. storePassword keystoreProperties['storePassword']
  12. }
  13. }
  14. buildTypes {
  15. release {
  16. // TODO: Add your own signing config for the release build.
  17. // Signing with the debug keys for now, so `flutter run --release` works.
  18. debuggable false
  19. minifyEnabled true
  20. // signingConfig signingConfigs.debug
  21. signingConfig signingConfigs.release
  22. ndk{ // 必须加入这部分,否则可能导致编译成功的release包在真机中会闪退
  23. abiFilters "armeabi-v7a"
  24. }
  25. }
  26. debug {
  27. ndk {
  28. //这里要加上,否则debug包会出问题,后面三个为可选,x86建议加上不然部分模拟器回报错
  29. abiFilters "armeabi", "armeabi-v7a", "arm64-v8a", "x86"
  30. }
  31. }
  32. }
flutter build apk --release

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

生成签名

keytool -genkeypair -alias key -keyalg RSA -keypass 123456 -storepass 123456  -validity 3650 -keystore key.jks

  • -genkey:表示要生成一个密钥

  • -alias:后跟密钥的别名  可以设置给项目名字  对应 keyAlias

  • -keyalg:使用的密钥算法,这里使用的是 RSA

  • -keysize:密钥的长度,这里是 2048 位

  • -keystore:指定密钥库的文件名和类型,这里是 key.jks

  • -validity:密钥的有效期,这里是 3650 天

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/爱喝兽奶帝天荒/article/detail/820115
推荐阅读
相关标签
  

闽ICP备14008679号