題接上篇的文章的項目,還是那個空貨管理app。本篇文章用於講解基於Flutter的app項目的升級方案。 在我接觸Flutter之前,做過一個比較失敗的基於DCloud的HTML5+技術的app,做過幾個RN項目。在這兩種不同機制的app升級方案中,RN採用的是微軟的CodePush技術。而那個比較 ...
題接上篇的文章的項目,還是那個空貨管理app。本篇文章用於講解基於Flutter的app項目的升級方案。
在我接觸Flutter之前,做過一個比較失敗的基於DCloud的HTML5+技術的app,做過幾個RN項目。在這兩種不同機制的app升級方案中,RN採用的是微軟的CodePush技術。而那個比較失敗的項目採用的是檢查版本號,下載安裝包的方法。而在這個Flutter項目中,我在寫app更新方法時,查資料的時候查到一篇文章,文章大概意思講解了一下Flutter實行CodePush的可能性。但是,我並未找到可能實現的方法。因此,採用了簡單粗暴的進行app升級。
伺服器的操作
為了檢驗版本號和下載app安裝包,我們在伺服器某文件夾下放置兩個文件,第一個為version.json文件,內容為:
{
"android": "1.0.1"
}
這個文件用於保存版本號,我們可以寫一個讀取方法來讀取這個版本號:
Future<bool> checkNewVersion() async {
try {
final res = await http.get(downLoadUrl + '/version.json');
if (res.statusCode == 200) {
final Map<String, dynamic> body = json.decode(res.body);
if (defaultTargetPlatform == TargetPlatform.android) {
final packageInfo = await PackageInfo.fromPlatform();
final newVersion = body['android'];
return (newVersion.compareTo(packageInfo.version) == 1);
}
}
} catch (e) {
return false;
}
return false;
}
第二個文件為app安裝包,用來下載之後安裝。
app端的操作
在app端需要增加的方法比較多,有需要處理app的許可權和處理版本號讀取以及app安裝包下載和安裝等方法。
許可權的獲取
在targetSdkVersion < 24之前,我們可以通過在AndroidManifest.xml這個文件中寫入獲取讀寫許可權:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
但是當Flutter更新到1.0.0版本之後,現階段的targetSdkVersion為27。API 23之前的版本都是自動獲取許可權,而從 Android 6.0 開始添加了許可權申請的需求,更加安全。因此,我們需要做一下額外的才做來獲取許可權。
我在stackoverflow上找到了一篇文章瞭解了一下這個問題的解決方案。這篇文章中贊最高的方法比較負責,因為時間比較短,暫時沒有研究,不過項目組大佬說如果要完美地解決這個問題還是要會過來研究一下。
我在本次項目中採用了第二種方法,在MainActivity.java的onCreate方法中添加
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
然後引入simple_permissions這個依賴查詢app的許可權和詢問是否開啟許可權。具體方法為:
//是否有許可權
Future<bool> checkPermission() async {
bool res = await SimplePermissions.checkPermission(
Permission.WriteExternalStorage);
return res;
}
//打開許可權
Future<PermissionStatus> requestPermission() async {
return SimplePermissions.requestPermission(Permission.WriteExternalStorage);
}
版本號的獲取
我們在伺服器上放置了一個名為version.json的文件,我們可以獲取一下這個文件的內容訪問寫在裡面的版本號:
Future<bool> checkNewVersion() async {
try {
final res = await http.get(downLoadUrl + '/version.json');
if (res.statusCode == 200) {
final Map<String, dynamic> body = json.decode(res.body);
if (defaultTargetPlatform == TargetPlatform.android) {
// 獲取此時版本
final packageInfo = await PackageInfo.fromPlatform();
final newVersion = body['android'];
// 此處比較版本
return (newVersion.compareTo(packageInfo.version) == 1);
}
}
} catch (e) {
return false;
}
return false;
}
因為這個項目是基於安卓7.0的手持終端的項目,此處做了是否為安卓的查詢處理。
安裝包下載
在下載安裝包這個功能中,我們安裝了flutter_downloader這個依賴。先獲取一下下載地址,在下載安裝包:
// 獲取安裝地址
Future<String> get _apkLocalPath async {
final directory = await getExternalStorageDirectory();
return directory.path;
}
// 下載
Future<void> executeDownload() async {
final path = await _apkLocalPath;
//下載
final taskId = await FlutterDownloader.enqueue(
url: downLoadUrl + '/app-release.apk',
savedDir: path,
showNotification: true,
openFileFromNotification: true);
FlutterDownloader.registerCallback((id, status, progress) {
// 當下載完成時,調用安裝
if (taskId == id && status == DownloadTaskStatus.complete) {
_installApk();
}
});
}
// 安裝
Future<Null> _installApk() async {
// XXXXX為項目名
const platform = const MethodChannel(XXXXX);
try {
final path = await _apkLocalPath;
// 調用app地址
await platform.invokeMethod('install', {'path': path + '/app-release.apk'});
} on PlatformException catch (_) {}
}
安裝完成。
總結
以上為Flutter項目的更新步驟。在以上步驟中比較坑人的部分時許可權獲取至一塊中,如果不設置,則會無法成功下載安裝包。相信在不久的將來,Flutter可能也會用上CodePush。
順便說一下那個被我稱為失敗的項目,我去那個項目組的時候這個項目已經做了一半了。而讓我十分震驚的是作為一個基於vue的項目,項目進行了一多半還沒人使用狀態管理,vuex引入了,但是沒人用。嗯,強行carray,發現carry不動。只能儘力補救之後,眼睜睜地看著這個項目走向深淵。當時我還是個萌新,想進去學技術的,結果發現一群自稱三年經驗以上的人還需要我和另一個剛進公司的同事帶。當時還是很絕望的,只能一邊絕望一邊帶著他們加班。現在覺得項目組的水平和氛圍真的重要!!!