Flutter專案之app升級方案

lunlunshiwo發表於2019-02-11

題接上篇的文章的專案,還是那個空貨管理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不動。只能盡力補救之後,眼睜睜地看著這個專案走向深淵。當時我還是個萌新,想進去學技術的,結果發現一群自稱三年經驗以上的人還需要我和另一個剛進公司的同事帶。當時還是很絕望的,只能一邊絕望一邊帶著他們加班。現在覺得專案組的水平和氛圍真的重要!!!

image

相關文章