本文的更新思路简述如下: 通过远端获取最新的app版本号,与本地版本号进行对比,若存在新版本,询问用户是否进行更新,若同意更新,则iOS端跳转app store,Android端下载apk文件并启动apk文件以完成更新。
该功能我使用到的dependencies如下:
dependencies:
dio: ^3.0.7
permission_handler: ^4.0.0
path_provider: ^1.4.5
open_file: ^2.1.1
package_info: ^0.4.0
flutter_downloader: ^1.3.3
各package的功能:
- dio:请求库,用于获取版本号;
- permission_handler:查询和获取安卓端的权限,该功能点主要用到的权限有READ_EXTERNAL_STORAGE,WRITE_EXTERNAL_STORAGE, REQUEST_INSTALL_PACKAGES,分别是为了读取apk下载链接,下载apk和安装apk文件;
- path_provider: 获取本地存储路径;
- open_file: 用于打开apk文件;
- package_info: 用于获取当前app的版本号;
- flutter_downloader用于下载apk文件,该库在安卓端的具体配置可以参考我的这篇文章;
步骤详解
- 从远端获取版本号(可以是接口或者文件),与package_info库所获取的当前app版本号进行对比,若版本号不一致,则进行下一步逻辑;
// 检查版本
Future<bool> _checkVersion() async {
// 使用请求库dio读取文件服务器存有版本号的json文件
var res = await Dio().get('YOUR_HOST/version.json').catchError((e) {
print('获取版本号失败----------' + e);
});
if(res.statusCode == 200) {
// 解析json字符串
AppInfo appInfo = AppInfo.fromJson(res.data);
// 获取 PackageInfo class
PackageInfo packageInfo = await PackageInfo.fromPlatform();
// 比较版本号
if(packageInfo.version.hashCode != appInfo.version.hashCode) {
return true;
}
}
return false;
}
bool hasNewVersion = await _checkVersion();
// 若没有新版本,则退出逻辑
if(!hasNewVersion) {
return;
}
// 若不同意更新,则退出逻辑
bool confirm = await showDialog();
if(!confirm) {
return;
}
// 弹窗确认是否安装更新
Future<bool> showDialog() {
return showDialog<bool>(
context: context,
builder: (context) {
return AlertDialog(
title: Text("检测到新版本"),
content: Text("已准备好更新,确认安装新版本?"),
actions: <Widget>[
FlatButton(
child: Text(
"取消",
style: TextStyle(color: Color(0xff999999)),
),
onPressed: () {
Navigator.of(context).pop();
} // 关闭对话框
),
FlatButton(
child: Text("确认"),
onPressed: () {
//关闭对话框并返回true
Navigator.of(context).pop(true);
},
),
],
);
},
);
}
- 通过dart:io中的Platform判断系统类型,iOS系统
import ‘dart:io’; // Platform依赖dart:io
// 判断系统,ios跳转app store,安卓下载新的apk
if(Platform.isIOS) {
// 跳转app store
} else if( Platform.isAndroid) {
// 执行下载安装的准备工作
await _prepareDownload();
if(_downloadPath.isNotEmpty) {
// 执行下载逻辑
await download();
}
}
- 下载apk
// 下载完成之后的回调
static downloadCallback(id, status, progress) {
final SendPort send = IsolateNameServer.lookupPortByName('downloader_send_port');
send.send([id, status, progress]);
}
// 下载apk
static Future<void> download() async {
final bool _permissionReady = await _checkPermission();
if(_permissionReady) {
// final taskId = await downloadApk();
IsolateNameServer.registerPortWithName(_port.sendPort, 'downloader_send_port');
_port.listen((dynamic data) async {
String id = data[0];
DownloadTaskStatus status = data[1];
int progress = data[2];
if(status == DownloadTaskStatus.complete) {
// 更新弹窗提示,确认后进行安装
OpenFile.open('$_downloadPath/$_filename');
print('==============_installApkz: $_taskId $_downloadPath /$_filename');
}
});
FlutterDownloader.registerCallback(downloadCallback);
_taskId = await FlutterDownloader.enqueue(
url: '${Constants.asset_path}/dare_devil/app_release.apk',
savedDir: _downloadPath,
fileName: _filename,
showNotification: true,
openFileFromNotification: true
);
} else {
print('-----------------未授权');
}
}
// 检查并请求动态权限
static Future<bool> _checkPermission() async {
if (Platform.isAndroid) {
PermissionStatus permission = await PermissionHandler()
.checkPermissionStatus(PermissionGroup.storage);
if (permission != PermissionStatus.granted) {
Map<PermissionGroup, PermissionStatus> permissions =
await PermissionHandler()
.requestPermissions([PermissionGroup.storage]);
if (permissions[PermissionGroup.storage] == PermissionStatus.granted) {
return true;
}
} else {
return true;
}
} else {
return true;
}
return false;
}
- 安装apk
// 安装apk
static Future<void> installApk() async {
await OpenFile.open('$_downloadPath/$_filename');
}
本文涉及到的源码可以点击该链接
进行查看,如有疑问或不足欢迎在评论区或着issue中指出。