Flutter学习记录——28.Flutter 调试及 Android 和 iOS 打包


在使用不同的开发语言进行开发时,一般都会用到调试、断点调试功能。例如 Web、Android 等等都是支持调试和断点调试的,Flutter 也不例外,支持调试和单元测试功能,还有性能、布局分析器等等。那么这节课就给大家讲解下 Flutter 的调试和测试等功能的使用,配合一些实例进行讲解。

1.Flutter的调试

在开发中经常会用到调试功能,来验证某些输入输出数据、查找分析问题等等。Flutter 也是支持调试功能的,而且也非常的强大。我们先从最简单的说起。

我们可以通过控制台输出 Log 日志来查看调试程序:

print(object)
// 例如
int a = 6;
double b = 3.18;
print('$a ,$b');
// print取值通过$符号来取值
// debugPrint参数只能是String
debugPrint(String);

这种调试方式也成为日志调试。

debugPrint 用于当我们一次输出太多日志时,那么 Android 有时会丢弃一些日志行。为了避免这种情况,我们可以使用 Flutter 的 foundation 库中的 debugPrint()。 这是一个封装 print,可以避免被 Android 内核丢弃某些日志行。

我们可以在窗口通过 flutter logs 来过滤查看输出入日志。

接下来看下断点调试,这个也是比较重要和常用的调试手段。

我们先以 Android Studio 为例。

我们只需要在要添加断点的地方左侧点击一下就添加了一个红色的断点按钮。

断点调试

接下来以 Debug 模式运行应用程序即可:

断点调试

我们以官方默认的例子为例,我们在点击 + 号按钮的地方设置了 2 个断点。当我们点击加号时会自动执行到断点地方暂停:

断点调试

我们可以在线面的调试控制台里看到断点地方的各个信息和属性值,包括 _counter。

当前断点所在行地方会高亮,我们可以点击调试窗口的 Run to Cusor(Alt+F9) 跳到下一个断点。

怎么样,是不是很简单,和其他平台的断点调试很像。

1.1 VS Code 的断点调试

点击要断点调试的所在行的左侧边栏,这时就会添加一个红色的断点按钮。

断点调试

接着以 Debug 模式运行应用:

断点调试

编辑器页面顶部会悬浮调试操作栏:

断点调试

左侧的 Debug 窗口可以查看相关属性值:

断点调试

除了这几种调试外,还有一些命令的调试的支持。

如:Dart 分析器,用来检查分析代码并帮助发现代码中的一些问题和优化。我们在项目所在根目录输入命令行:flutter analyze 即可运行分析检测。

Dart分析器

我们还可以在代码里植入一些调试专用 API 语句来实现调试功能:

1.2 debugger()

构造方法如下:

external bool debugger({bool when: true, String message});

使用示例:

import 'dart:developer';
... ...
  void _incrementCounter() {
    setState(() {
      _counter++;
    });
    print(_counter);
    // 满足某个条件时执行进入调试
    debugger(when: _counter > 5.0);
  }

我们还可以设置 assert 断言来判断是否等于某个值。

使用示例:

assert(_counter==2);

1.3 debugDumpApp()

我们还可以通过 debugDumpApp() 来获取整个页面的 Widget 树状结构层级图:

debugDumpApp

我们可以详细的看到整个页面的Widget的内容。

debugDumpApp要在调用 runApp() 之后。

1.4 debugDumpRenderTree()

如果觉得 debugDumpApp() 输入的 Widget 层还不够详细,比较乱的话,也提供了 debugDumpRenderTree() 的使用。

使用 debugDumpRenderTree() 需要导入包:

import'package:flutter/rendering.dart';

debugDumpRenderTree

1.5 debugDumpLayerTree()

层级调试信息:

debugDumpRenderTree

1.6 debugDumpSemanticsTree()

我们还可以调用 debugDumpSemanticsTree() 获取语义树。

使用示例:

debugDumpSemanticsTree(DebugSemanticsDumpOrder.traversalOrder);

需要开启:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      // 开启showSemanticsDebugger
      showSemanticsDebugger: true,
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

在这里插入图片描述
在这里插入图片描述

1.7 可视化调试

我们可以通过设置 debugPaintSizeEnabled 为 true 以可视化方式调试布局问题。

... ...
 @override
  Widget build(BuildContext context) {
    debugPaintSizeEnabled = true;
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
... ...

可视化调试

会绘制一些基线、辅助线、箭头、颜色等信息帮助我们分析和绘制布局。

类似这样的配置命令有: debugPaintBaselinesEnabled、debugPaintPointersEnabled、debugPaintLayerBordersEnabled、debugRepaintRainbowEnabled 等。这些都只能在 debug 模式下使用和看到。

大家可以分别尝试下,看下它们的特点和不同点。

其他命令:debugPrintBeginFrameBanner、debugPrintEndFrameBanner、debugPrintScheduleFrameStacks。用法类似 debugPaintSizeEnabled。

... ...
 @override
  Widget build(BuildContext context) {
    debugPrintBeginFrameBanner = true;
    debugPrintEndFrameBanner = true;
    debugPrintScheduleFrameStacks = true;
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
... ...

1.8 动画的调试

当我们制作了一个动画,由于动画运行太快,我们无法观察动画的细节,调试动画最简单的方法是减慢它们的速度。所以 Flutter 也提供了动画调试功能:可以将 timeDilation 变量(在 scheduler 库中)设置为大于 1.0 的数字,例如 60.0。我们只需在应用程序启动时设置赋值一次即可。

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    timeDilation = 60.0;
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      showSemanticsDebugger: false,
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

1.9 性能的调试

要了解我们的应用程序导致重新布局或重新绘制的原因,我们可以分别设置 debugPrintMarkNeedsLayoutStacksdebugPrintMarkNeedsPaintStacks 标志。每当渲染盒被要求重新布局和重新绘制时,这些都会将堆栈跟踪记录到控制台。可以使用 services 库中的 debugPrintStack() 方法按需打印堆栈痕迹。

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

    debugPrintMarkNeedsLayoutStacks = true;
    debugPrintMarkNeedsPaintStacks = true;
    debugPrintStack();

    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      showSemanticsDebugger: false,
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

1.10 应用启动时间的查看

如果我们想查看应用启动时间相关信息,可以通过这种命令方式运行 Flutter 程序:

$ flutter run --trace-startup --profile

在 Flutter 的项目根目录的 build 目录下的 start_up_info.json 文件里。

各个关键阶段的启动时间信息都可以看到:

{ // 进入Flutter引擎
  "engineEnterTimestampMicros": 96025565262,
  // 展示应用第一帧
  "timeToFirstFrameMicros": 2171978,
  // 初始化Flutter框架
  "timeToFrameworkInitMicros": 514585,
  // 完成Flutter框架初始化
  "timeAfterFrameworkInitMicros": 1657393
}

1.11 代码、方法执行时间查看

如果想跟踪代码的性能、执行时间的话,可以使用 dart:developerTimeline API来测试我们的的代码块:

Timeline.startSync('interesting function');

// 方法

Timeline.finishSync();

1.12 应用程序性能图

如果想直观的查看应用程序时时性能图,可以开启 showPerformanceOverlay

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      showPerformanceOverlay: true,
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

会显示两个图表。第一个是 GPU 线程花费的时间,第二个是 CPU 线程花费的时间。

图中的白线以 16ms 增量沿纵轴显示;如果图中超过这三条线,那么我们的应用运行频率低于 60Hz,横轴代表帧。该图仅在应用程序绘制时更新,因此如果它处于空闲状态,该图将停止移动。

应用程序性能图

1.13 网格线

Material grid 网格线功能可以辅助我们进行对其验证。用法很简单,开启 debugShowMaterialGrid

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      debugShowMaterialGrid: true,
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

效果如下图:

网格线

关于调试还有异常捕获、日志收集等,后续会给大家讲解。

2.Flutter的单元测试

接下来我们看下 Flutter 的单元测试。 单元测试在项目根目录的 test 目录下,默认有一个 widget_test.dart 示例文件。

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

import 'package:flutter_app/main.dart';

// main方法为入口函数
void main() {

  // 单元测试的一个用例的方法名
  testWidgets('Counter increments smoke test', (WidgetTester tester) async {
    // 运行应用
    await tester.pumpWidget(MyApp());

    // 验证我们的界面widget显示的是0,而不是1
    // 期望找到一个含有0这个字符的Widget
    expect(find.text('0'), findsOneWidget);
    // 期望没有含有1这个字符的Widget
    expect(find.text('1'), findsNothing);

    // 模拟触发点击+号图标
    await tester.tap(find.byIcon(Icons.add));
    await tester.pump();

    // 验证我们的字符已经增加改变了
    // 期望找到0个含有0这个字符的Widget
    expect(find.text('0'), findsNothing);
    // 期望找到一个含有1这个字符的Widget
    expect(find.text('1'), findsOneWidget);
  });
}

怎么样是不是很简单。

如果我们用的是 VS Code 的话,会看到 Run|Debug 这个点击按钮开始运行单元测试。
单元测试

3.Flutter辅助工具的使用

接下来我们看下 Android Studio 下的一些 Flutter 辅助工具:

Flutter 辅助工具

我们可以在 View->Tool Windows 下查看到一些辅助工具,如:Dart Analysis(代码分析审查)、Flutter Inspector(用于可视化和浏览 Flutter Widget 树)、Flutter Outline(代码层级结构查看)、Flutter Performance(性能分析的)。

Dart Analysis 用于代码的检查和优化分析,如代码中无效的常量、变量声明等等。

Flutter Inspector 用于可视化和浏览当前页面布局的 Flutter Widget 树。

Flutter Inspector

Flutter Inspector

点击 Flutter Inspector 工具栏上的 Select widget,然后点击设备选择一个 widget。所选 Widget将在设备和 Widget 树结构中高亮显示。

Flutter Inspector

点击真机某个 Widget 或点击 Flutter Inspector 某个 Widget,都会在设备上和 Flutter Inspector 上有相应显示。

Flutter Inspector

Flutter Outline 可以查看代码的结构层级。

Flutter Outline

Flutter Performance 用作时时性能分析和显示的。

如帧率、内存使用等统计显示。

Flutter Performance

Flutter Performance

当然,除了以上这些,还有下图这几个强大的工具:

Flutter 辅助工具

Dart DevTools 我们可以在 Web 浏览器上打开。功能比较强大,集合了很多工具功能。

Dart DevTools

集合了:Flutter Inspector、Timeline、Memory、Performance、Debugger、Logging 功能,这些数据显示变成了在 Web 端显示和操作。大家可以详细体验下,前面都大致给大家说了下这些功能,这里就详细说了。

例如我们可以在这里打断点、查看日志等等:

Dart DevTools

Dart DevTools

Timeline

访问地址类似于这种格式: http://127.0.0.1:57553/cAHbB_v162I=/#/timeline 我们可以看到一些方法、程序、页面的执行时间信息:

Timeline

Dart VM Observatory

访问地址类似于这种格式:http://127.0.0.1:57553/cAHbB_v162I=/#/vm

这个通过 flutter run 命令启动后,会在控制台自动输出这个访问地址。

我们在这里同样可以看到很多不同功能的工具集合:

Dart VM Observatory

大家可以一一详细体验使用下,这里就不再重复详细讲解了。

4.Flutter Android 应用打包发布

终于到了应用打包发布的时候了。我们先看下 Android 的应用打包发布,非常的简单。

主要关注点如下。

4.1 应用包名

应用包名也是应用的唯一标识,格式类似于 com.google.googlemap 这种形式。类似域名倒着写+产品英文名。

包名的配置和修改在 android 项目 app 目录下的 build.gradle 里,它位于项目目录 /android/app/下:

applicationId "com.flutter.flutter_app"

applicationId 便是包名配置项。

versionCode 和 versionName 指定应用程序版本号和版本号字符串。

minSdkVersion 和 targetSdkVersion 指定最低的 API 级别以及应用程序设计运行的 API 级别。

AndroidManifest.xml 主要看是否缺少某些权限。如:

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WR/>

4.2 应用图标的替换

在项目目录 /android/app/src/main/res/ 目录中,将图标文件放入 mipmap-xxx 文件夹下。

应用图标的替换

具体的尺寸按照不同的目录下的 ic_launcher.png 这个默认图标进行设计替换即可。

4.3 应用签名

应用必须要签名后才可以进行发布和使用。

我们接下来看下如何制作 Android 的应用签名文件:

我们先用 Android Studio 单独打开 Android 项目 Module:

应用签名

我们需要使用 Android Studio 制作签名文件,Build-> Generate Signed Bundle/APK

应用签名

选择 APK:

应用签名

接下来就开始制作签名 keystore了:

点击 “Create new…“:

在 Key store path 里选择 keystore 签名文件存储的位置,然后输入签名密钥的密码。Alias 为这个签名文件的别名,也可以理解为备注。

应用签名

输入相关的签名文件信息,前几项不说了,后面的是 Validity——有效年限;First and Last Name——名字;Organizational Unit——单位名称;Organization——组织;City or Locality——城市;State or Province——省份;Country Code——国家代码。

创建好后,回到这个界面。

应用签名

选择一个打包模式就可以打包了:

应用签名

当然,我们不这样打包 Flutter 的 APK,这一步只是为了创建我们的签名 keystore。

我们在 Android 项目的 app 目录下的 build.gradle 里配置签名信息:

signingConfigs {
        release {
            keyAlias 'flutterNote'
            keyPassword 'key123'
            storeFile file('D:/flutterapp/flutter.jks')
            storePassword 'key123'
            v1SigningEnabled true
            v2SigningEnabled true
        }
    }

    buildTypes {
        debug {
            buildConfigField "boolean", "LOG_DEBUG", "true"
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.debug
        }
        release {
            //关闭日志输出
            buildConfigField "boolean", "LOG_DEBUG", "false"
            //关闭混淆
            minifyEnabled false
            //Zipalign优化:4字节对齐,减少运行内存消耗 
            zipAlignEnabled true
            //混淆规则文件
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            //签名文件
            signingConfig signingConfigs.release
        }
    }

这样我们在打包 Android Flutter 项目的 APK 时便会自动根据 debug 版本或 release 版本自动进行签名了。

接下来我们在控制台窗口运行打包命令:

$ flutter build apk
// 或
$ flutter build apk --release

打包好的发布 APK 位于 项目目录 /build/app/outputs/apk/app-release.apk

打包好的 APK 我们也可以对它进行加固处理,可以使用 360 加固保或腾讯乐固进行加固处理。

这个 APK 我们便可以发布到各个应用商店。

5.Flutter iOS 应用打包发布

接下来我们看下 Flutter iOS 应用的打包发布步骤。

我们需要在 Mac 下安装好 Android Studio 和 Xcode。

Android Studio 可以在官方下载 Mac 版本: https://developer.android.google.cn/studio

Xcode 在 AppStore 下载即可。

Android Studio 里安装好 Flutter 和 Dart 插件。接着下载 Flutter SDK,配置好 Mac 下环境变量。

给大家讲解下 Mac 下配置 Flutter 环境变量:

//在终端进入用户目录,一般打开终端默认就是
cd ~           

//打开配置文件
open .bash_profile    

//如果 .bash_profile文件不存在需先创建再打开,具体如下:
touch .bash_profile
open .bash_profile

//添加环境变量
export PATH=${PATH}:/Users/xxx/flutter/bin:$PATH
export PUB_HOSTED_URL=https://pub.flutter-io.cn
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn

// 执行文件 使命令生效
source .bash_profile      

假如我的 Flutter SDK 目录在 Users/td 下的 Documents 目录下,那么 PATH 就是:

export PATH=${PATH}:/Users/td/Documents/flutter/bin:$PATH

接下来输入 flutter 查看是否配置成功。

然后输入 flutter doctor 来检查 Flutter 运行环境是否完整:

检查环境

根据提示安装好各种所缺的库、文件即可。

这里讲解下安装 brew。

brew 官方地址:https://brew.sh/

安装命令为:

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

但是一般安装速度非常慢,我们需要更换源:

首先获取 brew_install 文件。

curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install >> brew_install

接下来找到并编辑 brew_install 文件。

#BREW_REPO = "https://github.com/Homebrew/brew".freeze
BREW_REPO = "git://mirrors.ustc.edu.cn/brew.git".freeze

这样我们就可以用新的源安装 brew。

/usr/bin/ruby ./brew_install

安装好 brew 后如果想下载其他库非常慢的话,可以使用清华镜像: https://mirror.tuna.tsinghua.edu.cn/help/homebrew/

替换现有上游:

git -C "$(brew --repo)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git

git -C "$(brew --repo homebrew/core)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-core.git

brew update

接下来进入正题,我们用 Xcode 打开我们通过 Android Studio 或 VS Code 创建的 Flutter 项目的 iOS 项目目录:

IOS项目

打开后可以看到这样的项目信息界面:

IOS项目

我们依然只需要像 Android 一样关注几个点即可。

Display Name 要在主屏幕和其他地方显示的应用程序的名称。

Bundle Identifier 在 iTunes Connect 上注册的 App ID。(类似于 Android 的应用唯一包名:com.xxx.xxx)

Version 版本名称,类似于 Android 的 VersionName。

Build 版本号,类似于 Android 的 VersionCode。

Team 选择与我们注册的 Apple Developer 帐户关联的团队。如果需要,请选择 Add Account…,然后更新此设置。新版 XCode 要求必须要有 Team 账户。

App Icons and Launch Images 应用图标设置。当创建新的 Flutter 应用程序时,会创建一个默认的 Flutter 图标占位图标集。在这一步中,我们可以用自己的图标替换这些占位图标。

IOS项目

有不同尺寸的图标:

IOS项目

在 Runner 文件夹中选择 Assets.xcassets,里面有相应的图标,替换即可。

替换后,我们可以运行 flutter run,验证应用图标是否已被替换。

这样主要的关注点都已经大致了解了。

但是如果要开发、测试、发布 iOS 应用,还应先在 App Store Connect:https://appstoreconnect.apple.com/ 上注册相应的开发者账号。

App Store Connect 是我们创建和管理应用程序的地方。我们可以创建应用程序名称和说明,添加屏幕截图,设置价格并管理版本到 App Store 和 TestFlight。

注册创建我们的应用程序涉及两个步骤:注册唯一的 Bundle ID,并在 App Store Connect 上选择运行的设备创建应用程序。

注册 Bundle ID

每个 iOS 应用程序都与一个 Bundle ID 关联,这是一个在 Apple 注册的唯一标识符。操作步骤:

  1. 打开开发者帐户的 App IDs 页:https://developer.apple.com/account/ios/identifier/bundle ;
  2. 点击 + 创建一个 Bundle ID;
  3. 输入应用程序名称, 选择 Explicit App ID, 然后输入一个 ID;
  4. 选择我们的应用将使用的服务,然后点击 ”Continue”;
  5. 在下一页中,确认详细信息,然后点击 Register 注册Bundle ID。

在 App Store Connect 上创建应用程序记录

  1. 在浏览器中打开 App Store Connect:https://appstoreconnect.apple.com/;
  2. 在 App Store Connect 登陆页上,点击 “My Apps“;
  3. 点击 My App 页面左上角的 “+“ ,然后选择 New App;
  4. 填写我们的应用详细信息。在 Platforms 部分中,确保已选中 iOS。由于 Flutter 目前不支持 tvOS,请不要选中该复选框。点击 “Create“;
  5. 跳转到我们的 APP 的应用程序详细信息页,App Information;
  6. 在 General Information 部分, 选择并填写我们在上一步中注册的 Bundle ID。

在开发过程中,我们一直在开发、调试、测试 debug 版本。如果想正式发布应用到 App Store 或 TestFlight 上时,需要准备 release 版本:

运行 flutter build ios 来创建 iOS 的 release 版本(flutter build 默认为 --release)。

归档并上传到 App Store Connect

接下来我们如果要上传到 App Store Connect 上需要进行归档:

  1. 选择 Product > Archive 来归档;
  2. 在 Xcode Organizer 窗口的边栏中,选择我们的 iOS 应用程序,然后选择刚刚生成的 build 档案。 点击 “Validate…“ 按钮;
  3. 档案已成功验证后,单击 “Upload to App Store…“,您可以在 App Store Connect 的应用详情页的 “Activities” 选项卡中查看构建状态;

我们大概在 30 分钟内收到一封电子邮件,通知构建归档已经过验证,并可以在 TestFlight 上发布给测试人员。此时,我们可以选择是否在 TestFlight 上发布,或继续并直接将我们的 release 版发布到 App Store。

在 TestFlight 发布应用到 App Store

TestFlight 允许开发人员将应用程序推送给内部和外部测试人员。在这个可选步骤中,您将在 TestFlight 上发布 build:

  1. 在 App Store Connect 上跳转到应用程序详细信息页面的 TestFlight 选项卡;
  2. 在侧边栏选择 “Internal Testing“;
  3. 选择要发布到测试人员的 build,然后单击 “Save“;
  4. 加任何内部测试人员的电子邮件地址。我们可以在 App Store Connect 的用户和角色页面添加更多的内部用户,可从页面顶部的下拉菜单中看到。

发布到 App Store

最后一个步骤:准备将应用发布到 App Store,我们可以按照以下步骤将应用提交给 App Store 进行审查和发布:

  1. 从 iTunes 应用程序的应用程序详情页的边栏中选择 “Pricing and Availability“,然后填写所需的信息;
  2. 从边栏选择状态。如果这是该应用的第一个版本,则其状态将为 1.0 Prepare for Submission,并完成所有必填字段;
  3. 最后点击 “Submit for Review“。

Apple 会在应用程序审查完成时通知我们。我们的应用将根据我们在 Version Release 部分指定的说明描述进行发布到 App Store。

发布了253 篇原创文章 · 获赞 52 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_41151659/article/details/103467020