<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                今天,我們來聊聊 Flutter 的編譯模式吧。 在開發移動應用程序時,一個 App 的完整生命周期包括開發、測試和上線 3 個階段。在每個階段,開發者的關注點都不一樣。 比如,在開發階段,我們希望調試盡可能方便、快速,盡可能多地提供錯誤上下文信息;在測試階段,我們希望覆蓋范圍盡可能全面,能夠具備不同配置切換能力,可以測試和驗證還沒有對外發布的新功能;而在發布階段,我們則希望能夠去除一切測試代碼,精簡調試信息,使運行速度盡可能快,代碼足夠安全。 這就要求開發者在構建移動應用時,不僅要在工程內提前準備多份配置環境,還要利用編譯器提供的編譯選項,打包出符合不同階段優化需求的 App。 對于 Flutter 來說,它既支持常見的 Debug、Release 等工程物理層面的編譯模式,也支持在工程內提供多種配置環境入口。今天,我們就來學習一下 Flutter 提供的編譯模式,以及如何在 App 中引用開發環境和生產環境,使得我們在不破壞任何生產環境代碼的情況下,能夠測試處于開發期的新功能。 ## Flutter 的編譯模式 Flutter 支持 3 種運行模式,包括 Debug、Release 和 Profile。在編譯時,這三種模式是完全獨立的。首先,我們先來看看這 3 種模式的具體含義吧。 * Debug 模式對應 Dart 的 JIT 模式,可以在真機和模擬器上同時運行。該模式會打開所有的斷言(assert),以及所有的調試信息、服務擴展和調試輔助(比如 Observatory)。此外,該模式為快速開發和運行做了優化,支持亞秒級有狀態的 Hot reload(熱重載),但并沒有優化代碼執行速度、二進制包大小和部署。flutter run --debug 命令,就是以這種模式運行的。 * Release 模式對應 Dart 的 AOT 模式,只能在真機上運行,不能在模擬器上運行,其編譯目標為最終的線上發布,給最終的用戶使用。該模式會關閉所有的斷言,以及盡可能多的調試信息、服務擴展和調試輔助。此外,該模式優化了應用快速啟動、代碼快速執行,以及二級制包大小,因此編譯時間較長。flutter run --release 命令,就是以這種模式運行的。 * Profile 模式,基本與 Release 模式一致,只是多了對 Profile 模式的服務擴展的支持,包括支持跟蹤,以及一些為了最低限度支持所需要的依賴(比如,可以連接 Observatory 到進程)。該模式用于分析真實設備實際運行性能。flutter run --profile 命令,就是以這種模式運行的。 由于 Profile 與 Release 在編譯過程上幾乎無差異,因此我們今天只討論 Debug 和 Release 模式。 在開發應用時,為了便于快速發現問題,我們通常會在運行時識別當前的編譯模式,去改變代碼的部分執行行為:在 Debug 模式下,我們會打印詳細的日志,調用開發環境接口;而在 Release 模式下,我們會只記錄極少的日志,調用生產環境接口。 在運行時識別應用的編譯模式,有兩種解決辦法: * 通過斷言識別; * 通過 Dart VM 所提供的編譯常數識別。 我們先來看看**如何通過斷言識別應用的編譯模式**。 通過 Debug 與 Release 模式的介紹,我們可以得出,Release 與 Debug 模式的一個重要區別就是,Release 模式關閉了所有的斷言。因此,我們可以借助于斷言,寫出只在 Debug 模式下生效的代碼。 如下所示,我們在斷言里傳入了一個始終返回 true 的匿名函數執行結果,這個匿名函數的函數體只會在 Debug 模式下生效: ~~~ assert(() { //Do sth for debug return true; }()); ~~~ 需要注意的是,匿名函數聲明調用結束時追加了小括號()。 這是因為斷言只能檢查布爾值,所以我們必須使用括號強制執行這個始終返回 true 的匿名函數,以確保匿名函數體的代碼可以執行。 接下來,我們再看看**如何通過編譯常數識別應用的編譯模式**。 如果說通過斷言只能寫出在 Debug 模式下運行的代碼,而通過 Dart 提供的編譯常數,我們還可以寫出只在 Release 模式下生效的代碼。Dart 提供了一個布爾型的常量 kReleaseMode,用于反向指示當前 App 的編譯模式。 如下所示,我們通過判斷這個常量,可以準確地識別出當前的編譯模式: ~~~ if(kReleaseMode){ //Do sth for release } else { //Do sth for debug } ~~~ ## 分離配置環境 通過斷言和 kReleaseMode 常量,我們能夠識別出當前 App 的編譯環境,從而可以在運行時對某個代碼功能進行局部微調。而如果我們想在整個應用層面,為不同的運行環境提供更為統一的配置(比如,對于同一個接口調用行為,開發環境會使用 dev.example.com 域名,而生產環境會使用 api.example.com 域名),則需要在應用啟動入口提供可配置的初始化方式,根據特定需求為應用注入配置環境。 在 Flutter 構建 App 時,為應用程序提供不同的配置環境,總體可以分為抽象配置、配置多入口、讀配置和編譯打包 4 個步驟: 1. 抽象出應用程序的可配置部分,并使用 InheritedWidget 對其進行封裝; 2. 將不同的配置環境拆解為多個應用程序入口(比如,開發環境為 main-dev.dart、生產環境為 main.dart),把應用程序的可配置部分固化在各個入口處; 3. 在運行期,通過 InheritedWidget 提供的數據共享機制,將配置部分應用到其子 Widget 對應的功能中; 4. 使用 Flutter 提供的編譯打包選項,構建出不同配置環境的安裝包。 **接下來,我將依次為你介紹具體的實現步驟。** 在下面的示例中,我會把應用程序調用的接口和標題進行區分實現,即開發環境使用 dev.example.com 域名,應用主頁標題為 dev;而生產環境使用 api.example.com 域名,主頁標題為 example。 首先是**配置抽象**。根據需求可以看出,應用程序中有兩個需要配置的部分,即接口 apiBaseUrl 和標題 appName,因此我定義了一個繼承自 InheritedWidget 的類 AppConfig,對這兩個配置進行封裝: ~~~ class AppConfig extends InheritedWidget { AppConfig({ @required this.appName, @required this.apiBaseUrl, @required Widget child, }) : super(child: child); final String appName;// 主頁標題 final String apiBaseUrl;// 接口域名 // 方便其子 Widget 在 Widget 樹中找到它 static AppConfig of(BuildContext context) { return context.inheritFromWidgetOfExactType(AppConfig); } // 判斷是否需要子 Widget 更新。由于是應用入口,無需更新 @override bool updateShouldNotify(InheritedWidget oldWidget) => false; } ~~~ 接下來,我們需要**為不同的環境創建不同的應用入口**。 在這個例子中,由于只有兩個環境,即開發環境與生產環境,因此我們將文件分別命名為 main\_dev.dart 和 main.dart。在這兩個文件中,我們會使用不同的配置數據來對 AppConfig 進行初始化,同時把應用程序實例 MyApp 作為其子 Widget,這樣整個應用內都可以獲取到配置數據: ~~~ //main_dev.dart void main() { var configuredApp = AppConfig( appName: 'dev',// 主頁標題 apiBaseUrl: 'http://dev.example.com/',// 接口域名 child: MyApp(), ); runApp(configuredApp);// 啟動應用入口 } //main.dart void main() { var configuredApp = AppConfig( appName: 'example',// 主頁標題 apiBaseUrl: 'http://api.example.com/',// 接口域名 child: MyApp(), ); runApp(configuredApp);// 啟動應用入口 } ~~~ 完成配置環境的注入之后,接下來就可以**在應用內獲取配置數據**,來實現定制化的功能了。由于 AppConfig 是整個應用程序的根節點,因此我可以通過調用 AppConfig.of 方法,來獲取到相關的數據配置。 在下面的代碼中,我分別獲取到了應用主頁的標題,以及接口域名,并顯示了出來: ~~~ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { var config = AppConfig.of(context);// 獲取應用配置 return MaterialApp( title: config.appName,// 應用主頁標題 home: MyHomePage(), ); } } class MyHomePage extends StatelessWidget { @override Widget build(BuildContext context) { var config = AppConfig.of(context);// 獲取應用配置 return Scaffold( appBar: AppBar( title: Text(config.appName),// 應用主頁標題 ), body: Center( child: Text('API host: ${config.apiBaseUrl}'),// 接口域名 ), ); } } ~~~ 現在,我們已經完成了分離配置環境的代碼部分。最后,我們可以使用 Flutter 提供的編譯選項,來**構建出不同配置的安裝包**了。 如果想要在模擬器或真機上運行這段代碼,我們可以在 flutter run 命令后面,追加–target 或 -t 參數,來指定應用程序初始化入口: ~~~ // 運行開發環境應用程序 flutter run -t lib/main_dev.dart // 運行生產環境應用程序 flutter run -t lib/main.dart ~~~ 如果我們想在 Android Studio 上為應用程序創建不同的啟動配置,則可以**通過 Flutter 插件為 main\_dev.dart 增加啟動入口**。 首先,點擊工具欄上的 Config Selector,選擇 Edit Configurations 進入編輯應用程序啟動選項: :-: ![](https://img.kancloud.cn/ab/96/ab9617b62b6fd66ac2a0cc949aeb874e_360x164.png) 圖 1 Config Selector 新增入口 然后,點擊位于工具欄面板左側頂部的“+”按鈕,在彈出的菜單中選擇 Flutter 選項,為應用程序新增一項啟動入口: :-: ![](https://img.kancloud.cn/bf/7c/bf7cd1ffc0fc58557672b4420d1f7364_646x1260.png) 圖 2 選擇新增類型 最后,在入口的編輯面板中,為 main\_dev 選擇程序的 Dart 入口,點擊 OK 后,就完成了入口的新增工作: :-: ![](https://img.kancloud.cn/7a/de/7adea7407c99fbb1dfbbdfbb7b247278_2154x1356.png) 圖 3 編輯啟動入口 接下來,我們就可以**在 Config Selector 中切換不同的啟動入口,從而直接在 Android Studio 中注入不同的配置環境了**: :-: ![](https://img.kancloud.cn/8f/38/8f38c9d92eda9ab8d6038a7e7611323e_372x206.png) 圖 4 Config Selector 切換啟動入口 我們試著在不同的入口中進行切換和運行,可以看到,App 已經可以識別出不同的配置環境了: ![](https://img.kancloud.cn/6a/43/6a43d7189d9d8a8cb0184cb424c3ef26_750x1334.png) 圖 5 開發環境運行示例 :-: ![](https://img.kancloud.cn/6a/fb/6afbe85bea6acfe86173085d34192145_750x1334.png) 圖 6 生產環境運行示例 而如果我們想要打包構建出適用于 Android 的 APK,或是 iOS 的 IPA 安裝包,則可以在 flutter build 命令后面,同樣追加–target 或 -t 參數,指定應用程序初始化入口: ~~~ // 打包開發環境應用程序 flutter build apk -t lib/main_dev.dart flutter build ios -t lib/main_dev.dart // 打包生產環境應用程序 flutter build apk -t lib/main.dart flutter build ios -t lib/main.dart ~~~ ## 總結 好了,今天的分享就到這里。我們來總結一下今天的主要內容吧。 Flutter 支持 Debug 與 Release 的編譯模式,并且這兩種模式在構建時是完全獨立的。Debug 模式下會打開所有的斷言和調試信息,而 Release 模式下則會關閉這些信息,因此我們可以通過斷言,寫出只在 Debug 模式下生效的代碼。而如果我們想更精準地識別出當前的編譯模式,則可以利用 Dart 所提供的編譯常數 kReleaseMode,寫出只在 Release 模式下生效的代碼。 除此之外,Flutter 對于常見的分環境配置能力也提供了支持,我們可以使用 InheritedWidget 為應用中可配置部分進行封裝抽象,通過配置多入口的方式為應用的啟動注入配置環境。 需要注意的是,雖然斷言和 kReleaseMode 都能夠識別出 Debug 編譯模式,但它們對二進制包的打包構建影響是不同的。 采用斷言的方式,其相關代碼會在 Release 構建中被完全剔除;而如果使用 kReleaseMode 常量來識別 Debug 環境,雖然這段代碼永遠不會在 Release 環境中執行,但卻會被打入到二進制包中,增大包體積。因此,如果沒有特殊需求的話,一定要使用斷言來實現 Debug 特有的邏輯,或是在發布期前將使用 kReleaseMode 判斷的 Debug 邏輯完全刪除。 我把今天分享所涉及到的知識點打包到了[GitHub](https://github.com/cyndibaby905/34_multi_env)中,你可以下載下來,反復運行幾次,加深理解與記憶。 ## 思考題 最后,我給你留一道思考題吧。 在保持生產環境代碼不變的情況下,如果想在開發環境中支持不同配置的切換,我們應該如何實現?
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看