Fluter 應用調試

Flutter 構建模式

目前,Flutter一共提供了三種運行模式,分別是Debug、Release和Profile模式。其中,Debug模式主要用在軟件編寫過程當中,Release模式主要用於應用發佈過程當中,而Profile模式則主要用於應用性能分析時,每一個模式都有本身特殊的使用場景。下面簡介介紹下這幾種模式:java

Debug模式
Debug模式又名調試模式,Debug模式能夠同時在物理設備、仿真器或者模擬器上運行應用。默認狀況下,使用flutter run命令運行應用程序時就是使用的Debug模式。在Debug模式下,全部的斷言、服務擴展是開啓的,而且在模式對快速開發和運行週期進行了編譯優化,當使用調試工具進行代碼調試時能夠直接鏈接到應用的進程裏。android

Release模式
Release模式又名發佈模式,此模式只能在物理設備上運行,不能在模擬器上運行。使用flutter run --release命令運行應用程序時就是使用的Release模式。在Release模式下,斷點、調試信息和服務擴展是不可用的,而且Release模式針對快速啓動、快速執行和安裝包大小進行了優化。json

Profile模式
Profile模式只能在物理設備上運行,不能在模擬器上運行。此模式主要用於應用性能分析,一些應用調試能力是被保留的,目的是分析應用存在的性能問題。Profile模式和Release模式大致相同,不一樣點體如今,Profile模式的某些服務擴展是啓用的,某些進程調試手段也是開啓的。android-studio

調試模式

在 Debug 模式下,app 能夠被安裝在物理設備、仿真器或者模擬器上進行調試。在Debug模式下,能夠進行以下操做:瀏覽器

  • 斷點 是開啓的。
  • 服務擴展是開啓的。
  • 針對快速開發和運行週期進行了編譯優化(但不是針對執行速度、二進制文件大小或者部署)。
  • 調試開啓,相似 開發者工具 等調試工具能夠鏈接到進程裏。

若是是在 Web 平臺下的調試模式,能夠進行以下操做:併發

  • 本次構建 沒有 最小化資源而且整個構建 沒有 優化性能。
  • 爲了簡化調試,這個 Web 應用使用了 dartdevc 編譯器。

默認狀況下,運行 flutter run 會使用 Debug 模式,同時 IDE 也支持這些模式。例如,Android Studio 提供了 Run > Debug… 菜單選項,並且在項目面板中還有一個三角形的綠色運行按鈕圖標 。app

Release 模式

當你想要最大的優化以及最小的佔用空間時,就使用 Release 模式來部署 app。 release 模式是不支持模擬器或者仿真器的,使用 Release 模式意味着。框架

  • 斷點是不可用的。
  • 調試信息是不可見的。
  • 調試是禁用的。
  • 編譯針對快速啓動、快速執行和小的 package 的大小進行了優化。
  • 服務擴展是禁用的。

對於Web開發來講,使用 Release 模式意味着。less

  • 此次構建資源已經被壓縮,而且性能得以優化。
  • 這個 Web 應用經過 dart2js 編譯器構建,以確保更優秀的性能。

Profile 模式

在 Profile 模式下,一些調試能力是被保留的,足夠分析你的 app 性能。Profile 模式在仿真器和模擬器上是不可用的,由於他們的行爲不能表明真實的性能。和 release 相比, profile 模式有如下不一樣:async

  • 一些服務擴展是啓用的。例如,支持 performance overlay。
  • Tracing 是啓用的,一些調試工具,好比 開發者工具 能夠鏈接到進程裏。

在 Web 平臺使用Profile 模式意味着:

  • 資源文件沒有被壓縮,可是總體性能已經優化。
  • 這個 Web 應用經過 dart2js 編譯器構建。

調試工具

在Flutter應用開發中,有不少工具能夠幫助調試 Flutter 應用程序,常見的以下所示。

  • 開發者工具,是一套運行在瀏覽器的性能及分析工具。
  • Android Studio/IntelliJVS Code(藉助 Flutter 和 Dart 插件)支持內置的源代碼調試器,能夠設置斷點,單步調試,檢查數值。
  • Flutter inspector,是開發者工具提供的 widget 檢查器,也可直接在 Android Studio 和 IntelliJ 中使用(藉助 Flutter 插件)。檢查器能夠可視化展示 widget 樹,查看單個 widget 及其屬性值,開啓性能圖層,等等。

開發者工具

要調試及分析應用,開發者工具多是你的首選。開發者工具運行在瀏覽器,支持如下特性:

  • 源代碼調試器
  • Widget 檢查器,展現可視化的 widget 樹; 「widget select」 模式,在應用中選擇一個 widget,會在 widget 樹直接定位到它的位置。
  • 內存分析
  • 時間線視圖,支持跟蹤,導入及導出跟蹤信息
  • 日誌視圖

若是你在Debug 模式 或Profile 模式 運行,那麼能夠在瀏覽器打開開發者工具鏈接到你的應用。開發者工具不能用在 Release 模式 編譯的應用,由於調試和分析信息都被刪除了。若是你要用開發者工具分析應用,需確保使用 Profile 模式運行應用。

在這裏插入圖片描述

斷點調試

和其餘語言同樣,Flutter的斷點調試支持在 IDE 或編輯器(好比 Android Studio/IntelliJ 和 VS Code)、或者經過編碼兩種方式。
其中,開發者工具調試器以下圖所示。
在這裏插入圖片描述
若是須要,在源代碼中設置斷點,而後點擊工具欄中的 【Debug】 按鈕,或選擇 【Run】 > 【Debug】便可開啓調試功能。
在這裏插入圖片描述

開啓調試後,能夠在控制檯看到以下一些信息。

  • 底部的 Debugger 窗口會顯示出堆棧和變量信息。
  • 底部的 Console 窗口會顯示詳細的日誌輸出。
  • 調試基於默認的啓動配置,若是須要自定義,點擊選擇目標下拉按鈕,選擇 Edit configuration 進行配置。

在進行斷點調試時,使用得最多的就是單步調試,三個單步調試按鈕在暫停後會變爲可用狀態。

  • 使用 Step in 來進入被調用的方法,在遇到方法內的第一行可執行代碼時結束。
  • 使用 Step over 直接執行某個方法調用而不進入內部;該按鈕在當前方法內按行執行。
  • 使用 Step out 來跳出當前方法,這種方式會直接執行完全部當前方法內的語句。

除此以外,咱們還可使用代碼的方式進行斷點調試,咱們能夠在源代碼中使用 debugger()函數來開啓斷點,當代碼運行到此處時就會颳起,以下所示。

import 'dart:developer';

void someFunction(double offset) {
  debugger(when: offset > 30.0);
  // ...
}

Dart 分析器

若是你使用的是 Android Studio或者VSCode,那麼工具會自帶的 Dart 分析器默認會檢查代碼,並發現可能的錯誤。若是你使用命令行,則可使用 flutter analyze命令來檢查代碼。Dart 分析器很是依賴你在代碼中添加的類型註解,以幫助跟蹤問題。

另外,咱們可使用flutter analyze --flutter-repo命令將分析結果打印到控制檯上,每次運行這個命名以前,請先運行flutter update-packages 升級最新的包,這樣就能夠獲取最新的依賴包。若是你不這樣作,你可能會從dart:ui獲得一些錯誤消息,好比偏移量等。由於執行flutter analysis 命令時並不會主動去拉取依賴。

對於一次性的Dart分析,直接使用flutter analyze --flutter-repo便可,對於連續分析,則可使用flutter analyze --flutter-repo --watch命令。若是你想知道多少個成員變量丟失了dartdocs,能夠添加一個dartdocs參數。

Flutter inspector 工具

Flutter inspector 是分析Flutter組件狀態樹的利器,Flutter使用小部件來控制頁面組件到佈局的精準控制,Flutter inspector 能夠幫助咱們進行以下一些分析。

  • 進行佈局分析,理解佈局層次
  • 診斷佈局問題

在這裏插入圖片描述
在調試模式下,咱們點擊Android Studio右邊Flutter inspector按鈕便可開啓Flutter inspector分析,Flutter inspector提供了以下的可視化調試工具。
在這裏插入圖片描述

  • Select widget mode:啓用此按鈕後,選擇組件樹的代碼會自動跳轉到對應的源代碼裏面。
  • Refresh tree : 從新加載最新的組件信息。
  • Slow Animations:放慢動畫速度,以便進行視覺上的查驗。
  • Debug Paint: 邊框、方向的可視化。
  • Paint Baselines: 每一個渲染框在它的每一個文本基線上畫一條線。
  • Repaint Rainbow:查看重繪的嚴重程度,嚴重的會被爆紅。

除了上面的功能外,咱們還能夠點擊【Open DevTools】打開Flutter的調試頁面,能夠藉助它進行不少性能分析,後面會具體介紹。
在這裏插入圖片描述

測量應用啓動時間

要收集有關 Flutter 應用程序啓動所需時間的詳細信息,能夠在運行 flutter run 命令時使用 trace-startup 和 profile 選項,以下所示。

flutter run --trace-startup --profile

跟蹤輸出被保存到 Flutter 工程目錄在 build 目錄下,一個名爲 start_up_info.json 的 JSON 文件中,輸出列出了從應用程序啓動到這些跟蹤事件(以微秒捕獲)所用的時間,以下所示。

{
  "engineEnterTimestampMicros": 2346054348633,
  "timeToFrameworkInitMicros": 812748,
  "timeToFirstFrameRasterizedMicros": 1573154,
  "timeToFirstFrameMicros": 1221472,
  "timeAfterFrameworkInitMicros": 408724
}

對應的具體含義以下:

  • 進入 Flutter 引擎時
  • 展現應用第一幀時
  • 初始化Flutter框架時
  • 完成Flutter框架初始化時

使用Android Studio進行調試

Flutter官方推薦使用Android Studio或VSCode進行應用開發, 和其餘語言的調試同樣,Dart代碼的調試流程也差很少。若是尚未Flutter項目,能夠新建一個示例項目。經過單擊首先,點擊調試圖標(Debug-run icon)同時打開調試面板並在控制檯中運行應用,首次運行應用是最慢的,應用啓動後,界面應該是下面這樣的。
在這裏插入圖片描述
而後,咱們在在 counter++ 這一行上添加斷點。在應用裏,點擊 + 按鈕(FloatingActionButton,或者簡稱 FAB)來增長數字,應用會暫停。
在這裏插入圖片描述
你能夠 step in/out/over Dart 語句、熱重載和恢復執行應用、以及像使用其餘調試器同樣來使用 Dart 調試器。

Flutter inspector

Flutter inspector 是一個用來可視化以及查看 Flutter widget 樹的工具,提供以下功能:

  • 瞭解現有佈局
  • 診斷佈局問題

可使用 Android Studio 窗口右側的垂直按鈕來打開Flutter inspector,以下圖所示。

在這裏插入圖片描述

Flutter outline

Flutter Outline 是一個可視的顯示頁面構建方法的功能,注意在構建方法上可能與 widget 樹不一樣,可使用 Android Studio 窗口右側的垂直按鈕切換 outline 的顯示。
在這裏插入圖片描述
Tip: 咱們能夠安裝一個 Presentation Assistant 插件來輔助咱們進行開發,Presentation Assistant 提供了不少的快捷功能。例如,當焦點在編輯面板中時,輸入 command-Shift-A(Mac)或者 shift-control-A(Windows 和 Linux),該插件會同時顯示「查找」面板並顯示在全部三個平臺上執行此操做的提示。
在這裏插入圖片描述
而後在輸入框中輸入attach關鍵字,顯示以下圖。

在這裏插入圖片描述

使用 Android Gradle 調試

爲了調試原生代碼,你須要一個包含 Android 原生代碼的應用。在本節中,你將學會如何鏈接兩個調試器到你的應用:
1)Dart 調試器。
2)Android Gradle 調試器。

建立一個基本的 Flutter 應用,而後替換 lib/main.dart 的代碼爲如下示例代碼。

// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'URL Launcher',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'URL Launcher'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Future<void> _launched;

  Future<void> _launchInBrowser(String url) async {
    if (await canLaunch(url)) {
      await launch(url, forceSafariVC: false, forceWebView: false);
    } else {
      throw 'Could not launch $url';
    }
  }

  Future<void> _launchInWebViewOrVC(String url) async {
    if (await canLaunch(url)) {
      await launch(url, forceSafariVC: true, forceWebView: true);
    } else {
      throw 'Could not launch $url';
    }
  }

  Widget _launchStatus(BuildContext context, AsyncSnapshot<void> snapshot) {
    if (snapshot.hasError) {
      return Text('Error: ${snapshot.error}');
    } else {
      return Text('');
    }
  }

  @override
  Widget build(BuildContext context) {
    String toLaunch = 'https://flutter.dev';
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Padding(
              padding: EdgeInsets.all(16.0),
              child: Text(toLaunch),
            ),
            RaisedButton(
              onPressed: () => setState(() {
                    _launched = _launchInBrowser(toLaunch);
                  }),
              child: Text('Launch in browser'),
            ),
            Padding(padding: EdgeInsets.all(16.0)),
            RaisedButton(
              onPressed: () => setState(() {
                    _launched = _launchInWebViewOrVC(toLaunch);
                  }),
              child: Text('Launch in app'),
            ),
            Padding(padding: EdgeInsets.all(16.0)),
            FutureBuilder<void>(future: _launched, builder: _launchStatus),
          ],
        ),
      ),
    );
  }
}

而後,添加 url_launcher 依賴到 pubspec 文件,並執行 flutter pub get命令拉取依賴包。

name: flutter_app
description: A new Flutter application.
version: 1.0.0+1

dependencies:
  flutter:
    sdk: flutter

  url_launcher: ^3.0.3
  cupertino_icons: ^0.1.2

dev_dependencies:
  flutter_test:
    sdk: flutter

點擊調試按鈕(Debug-run icon)來同時打開調試面板並啓動應用,以下圖所示。
在這裏插入圖片描述
點擊 【Attach debugger to Android process】 按鈕,從進程對話框中,你應該能夠看到每個設備的入口。選擇 show all processes 來顯示每一個設備可用的進程。
在這裏插入圖片描述
在調試面板中,你如今應該能夠看到一個 Android Debugger 標籤頁,而後依次選擇【app_name】 > 【android】 > 【app】 > 【src】 >【 main】 > 【java】 > 【io.flutter plugins】在項目面板,而後雙擊 GeneratedProjectRegistrant 在編輯面板中打開 Java 代碼,此時Dart 和原生調試器都在與同一個進程交互。
在這裏插入圖片描述

在這裏插入圖片描述

相關文章
相關標籤/搜索