Flutter 混合開發實戰問題記錄(四)編譯運行時問題的一些總結(隨時補充)

項目中的一些模塊用flutter從新開發後在兩次雲測和灰度少許渠道後發現了些問題,分爲兩類:java

一個是非編譯/運行時問題,即dart語法使用錯誤或widget佈局錯誤使用出現的問題android

由於這類問題對於全部人幾乎都會遇到,再也不詳細說解決方案,只簡單羅列下我遇到的,提醒別人注意便可:git

1、非編譯/運行問題

1 黑黃相間提示:內容溢出

須要顯示的內容超出了widget容器或屏幕的範圍會出現,通常放入滾動容器中可解github

2 image組件的封裝

由於官方image組件提供了相似scaleType的屬性:fit,當出現圖片寬大於高,或高大於寬時,fitWidth或fitHeight就直接替換爲BoxFit.cover吧,能夠作下兼容,另外由於原生組件沒有提供磁盤緩存功能,能夠本身實現或直接加載個三方庫,比較推薦 flutter_cached_network_image ,而後能夠封裝本身的imageloader了, 我寫了個簡易的例子web

3 劉海屏適配:用 SafeArea 防止帶劉海的屏幕,或者iPhonex 底部bottom 的頁面顯示異常

class FlutterAlign extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Align(
        alignment: Alignment(-1, 1),
        child: Container(
          child: Text(
            "Hello",
          ),
        ),
      ),
    );
  }
}
複製代碼

4 關於toast

flutter做爲一款UI框架,使用原則貌似是隻要是實現不復雜,原生非不可替代的控件都應該用flutter來作,不過toast這個東西爲了保持跟原生界面一致性,仍是調用原生吧,不過我遇到的一個小問題是:有個界面有可能須要快速頻繁提示toast,並且原生代碼中使用的toast是自定義的不排隊toast(繼承Toast),因爲內部機制,快速插入toast隊列信息會形成系統等待,短期再也不響應toast彈出,解決方案能夠是按鈕控制節流(throttle)或防抖(debounce),也能夠優化下toast排隊機制。 不過最後仍是決定用flutter作一個備用吧,網上的多數方案是使用Overlaysshell

Overlays經過把子widget插入到overlay的stack裏面, 讓依賴它的子widget能夠浮在其它的可見元素上面。OverlayEntry能夠管理漂浮的widgets。(一個OverlayEntry就是一個層)
複製代碼

我貼一個簡單實現api

import 'package:flutter/material.dart';

class FlutterToast {
  static OverlayEntry _overlayEntry; //toast靠它加到屏幕上
  static bool _showing = false; //toast是否正在showing
  static DateTime _startedTime; //開啓一個新toast的當前時間,用於對比是否已經展現了足夠時間
  static String _msg;
  static void toast(
      BuildContext context,
      String msg,
      ) async {
    assert(msg != null);
    _msg = msg;
    _startedTime = DateTime.now();
    //獲取OverlayState
    OverlayState overlayState = Overlay.of(context);
    _showing = true;
    if (_overlayEntry == null) {
      _overlayEntry = OverlayEntry(
          builder: (BuildContext context) => Positioned(
            //top值,能夠改變這個值來改變toast在屏幕中的位置
            top: MediaQuery.of(context).size.height * 2 / 3,
            child: Container(
                alignment: Alignment.center,
                width: MediaQuery.of(context).size.width,
                child: Padding(
                  padding: EdgeInsets.symmetric(horizontal: 80.0),
                  child: AnimatedOpacity(
                    opacity: _showing ? 1.0 : 0.0, //目標透明度
                    duration: _showing
                        ? Duration(milliseconds: 100)
                        : Duration(milliseconds: 400),
                    child: _buildToastWidget(),
                  ),
                )),
          ));
      overlayState.insert(_overlayEntry);
    } else {
      _overlayEntry.markNeedsBuild();
    }
    await Future.delayed(Duration(milliseconds: 2000)); //等待兩秒

    if (DateTime.now().difference(_startedTime).inMilliseconds >= 2000) {
      _showing = false;
      _overlayEntry.markNeedsBuild();
    }
  }

  static _buildToastWidget() {
    return Center(
      child: Card(
        color: Colors.black,
        child: Padding(
          padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 8.0),
          child: Text(
            _msg,
            style: TextStyle(
              fontSize: 14.0,
              color: Colors.white,
            ),
          ),
        ),
      ),
    );
  }
}
複製代碼

能夠根據須要調節UI,讓它和你的原生toast保持一致,可是有個問題,它彈出後會被鍵盤覆蓋,因爲機制問題,因此暫時貌似還不能解決,也因如此,flutter這種toast默認在中間彈出防止被蓋住。緩存

5 錯誤:Another exception was thrown: Invalid argument(s)

發生場景:bash

String name;
 Container(
 padding: EdgeInsets.only(top: 5.0),
 child: Text(
   "名字:" + name ?? "",
   style: TextStyle(fontSize: 14.0, color: const Color(0xFF878E9F)),
 ),
),
複製代碼

對非空判斷符號使用的錯誤網絡

name ?? "" , 獨立使用沒有問題,不過拼接到字符串中須要改爲這樣:

"名字:" + (name ?? "")

6 dio庫的異常捕獲

強大的網絡請求Dio庫在請求過程當中發生錯誤,入無網絡或者非200的錯誤碼響應等異常只會在控制檯中打印出如下相似信息

Unhandled Exception: DioError [DioErrorType.DEFAULT]: SocketException: Failed host lookup:
複製代碼

假如你用

try{
   dio.post()... 
}on DioError catch (e) {
   print(e.message); 
}
複製代碼

沒法捕獲到請求內部異常,查看源碼可知 dio.post的返回值是Future, 而且它也使用的是catchError((err) => throw_assureDioError(err)); 來捕獲異常。

因此咱們使用的時候也要使用Future的api來捕獲

dio.post()
    .then((resp) => {
        ...  
    }).catchError((e){
           print(e.message);
        });

複製代碼





--------------------------------------分割線---------------------------------------





另一類問題就是直接debug或release報錯,或者機型適配出現的問題了,多出如今dart VM或SDK中

2、編譯運行問題

1 [FATAL:flutter/runtime/dart_vm.cc(416)] Error while initializing the Dart VM: Wrong full snapshot version, expected '0c73eb70aa4d30f450273cb424be8c62' found 'eed485c757fba5d731e4054412c99f2e'

問題發生於升級flutter sdk以後,看上邊的日誌Wrong full snapshot version能夠猜想:升級了sdk,可是以前已生成的編譯產物仍是舊的,不匹配,須要從新build一下,搜索了issue後找到了個方案:

1) removing the content of flutter/bin/cache

2) then running flutter upgrade again

3) then running flutter clean prior to flutter run

複製代碼

2 部分成米手機的崩潰

E/flutter: [ERROR:flutter/runtime/dart_vm.cc(259)] VM snapshot must be valid. A/flutter: [FATAL:flutter/shell/common/shell.cc(212)] Check failed: vm. Must be able to initialize the VM. --------- beginning of crash

升級sdk到1.4以後解決

3 cpu架構中so包支持問題

由於是混合開發,flutter做爲插件支持原生項目,因此須要支持什麼樣的cpu架構視原生項目狀況而定,咱們的平臺底層庫只支持

ndk {
    abiFilters 'armeabi'
    }
複製代碼

對於flutter SDK的修改:咱們只須要修改android-arm、android-arm-profile和android-arm-release下的flutter.jar,將其中的lib/armeabi-v7a/libflutter.so移動到lib/armeabi/libflutter.so便可:

1 先進入目錄
cd $FLUTTER_ROOT/bin/cache/artifacts/engine

2 而後執行 
for arch in android-arm android-arm-profile android-arm-release; do
  pushd $arch
  cp flutter.jar flutter-armeabi-v7a.jar # 備份
  unzip flutter.jar lib/armeabi-v7a/libflutter.so
  mv lib/armeabi-v7a lib/armeabi
  zip -d flutter.jar lib/armeabi-v7a/libflutter.so
  zip flutter.jar lib/armeabi/libflutter.so
  popd
done
複製代碼

注: 全部的x86/x86_64/armeabi-v7a/arm64-v8a設備都支持armeabi架構的.so文件,所以彷佛移除其餘ABIs的.so文件是一個減小APK大小的好技巧。但事實上並非:這不僅影響到函數庫的性能和兼容性,x86設備可以很好的運行ARM類型函數庫,但並不保證100%不發生crash,特別是對舊設備。64位設備(arm64-v8a, x86_64, mips64)可以運行32位的函數庫,可是以32位模式運行,在64位平臺上運行32位版本的ART和Android組件,將丟失專爲64位優化過的性能(ART,webview,media等等)。

4 錯誤:Reply already submitted

java.lang.IllegalStateException: Reply already submitted at io.flutter.view.FlutterNativeView$PlatformMessageHandlerImpl1.reply(FlutterNativeView.java:197)at io.flutter.plugin.common.MethodChannelIncomingMethodCallHandler$1.success(MethodChannel.java:204)

發生場景: Flutter 經過 eventMethod 調用原生端方法,該方法是在switch case 語句段中,由於沒有寫break,大概致使了 MethodChannel.Result 返回給Flutter參數時調用了屢次,直接把switch case 中的break補充上就沒有再復現,不過GitHub中的issue場景貌似要複雜一些,能夠參考github

相關文章
相關標籤/搜索