flutter 专题 六十三 Flutter入门与实战作者:xiangzhihong8Fluter 应用调试

Fluter 应用调试

Flutter 构建模式

目前,Flutter一共提供了三种运行模式,分别是Debug、Release和Profile模式。其中,Debug模式主要用在软件编写过程中,Release模式主要用于应用发布过程中,而Profile模式则主要用于应用性能分析时,每个模式都有自己特殊的使用场景。下面简介介绍下这几种模式:

Debug模式

Debug模式又名调试模式,Debug模式可以同时在物理设备、仿真器或者模拟器上运行应用。默认情况下,使用flutter run命令运行应用程序时就是使用的Debug模式。在Debug模式下,所有的断言、服务扩展是开启的,并且在模式对快速开发和运行周期进行了编译优化,当使用调试工具进行代码调试时可以直接连接到应用的进程里。

Release模式

Release模式又名发布模式,此模式只能在物理设备上运行,不能在模拟器上运行。使用flutter run --release命令运行应用程序时就是使用的Release模式。在Release模式下,断点、调试信息和服务扩展是不可用的,并且Release模式针对快速启动、快速执行和安装包大小进行了优化。

Profile模式

Profile模式只能在物理设备上运行,不能在模拟器上运行。此模式主要用于应用性能分析,一些应用调试能力是被保留的,目的是分析应用存在的性能问题。Profile模式和Release模式大体相同,不同点体现在,Profile模式的某些服务扩展是启用的,某些进程调试手段也是开启的。

调试模式

在 Debug 模式下,app 可以被安装在物理设备、仿真器或者模拟器上进行调试。在Debug模式下,可以进行如下操作:

  • 断点 是开启的。
  • 服务扩展是开启的。
  • 针对快速开发和运行周期进行了编译优化(但不是针对执行速度、二进制文件大小或者部署)。
  • 调试开启,类似 开发者工具 等调试工具可以连接到进程里。

如果是在 Web 平台下的调试模式,可以进行如下操作:

  • 本次构建 没有 最小化资源并且整个构建 没有 优化性能。
  • 为了简化调试,这个 Web 应用使用了 dartdevc 编译器。

默认情况下,运行 flutter run 会使用 Debug 模式,同时 IDE 也支持这些模式。例如,Android Studio 提供了 Run > Debug... 菜单选项,而且在项目面板中还有一个三角形的绿色运行按钮图标 。

Release 模式

当你想要最大的优化以及最小的占用空间时,就使用 Release 模式来部署 app。 release 模式是不支持模拟器或者仿真器的,使用 Release 模式意味着。

  • 断点是不可用的。
  • 调试信息是不可见的。
  • 调试是禁用的。
  • 编译针对快速启动、快速执行和小的 package 的大小进行了优化。
  • 服务扩展是禁用的。

对于Web开发来说,使用 Release 模式意味着。

  • 这次构建资源已经被压缩,并且性能得以优化。
  • 这个 Web 应用通过 dart2js 编译器构建,以确保更优秀的性能。

Profile 模式

在 Profile 模式下,一些调试能力是被保留的,足够分析你的 app 性能。Profile 模式在仿真器和模拟器上是不可用的,因为他们的行为不能代表真实的性能。和 release 相比, profile 模式有以下不同:

  • 一些服务扩展是启用的。例如,支持 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 和原生调试器都在与同一个进程交互。

相关推荐
恋猫de小郭32 分钟前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
一只大侠的侠5 小时前
Flutter开源鸿蒙跨平台训练营 Day 10特惠推荐数据的获取与渲染
flutter·开源·harmonyos
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
renke33649 小时前
Flutter for OpenHarmony:色彩捕手——基于HSL色轮与感知色差的交互式色觉训练系统
flutter
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端