关于android:Google-2020开发者大会Flutter专题

1次阅读

共计 9482 个字符,预计需要花费 24 分钟才能阅读完成。

因为疫情的起因,往年的 Google 开发者大会 (Google Developer Summit) 在线上举办,本次大会以“代码不止”为主题,全面介绍了产品更新以及一系列面向本地开发者的技术支持内容。我比拟关注的是挪动开发,在本次大会上,对于 Flutter 主题的演讲次要从 Flutter 性能方面优化和新性能进行开展。

作为寰球增长速度第二的开源我的项目,越来越多国内开发者应用 Flutter 实现跨平台开发,包含腾讯英语君团队、阿里闲鱼团队等等。其在 开放性上的提高,得益于开源社区、生态建设、对 Web 的反对。

有趣味的读者能够通过 Google Developer 官网进行学习:Google Developer 官网

上面咱们就来看一下这些新性能和性能上的优化。

Flutter 性能优化

首先为咱们带来演讲的是 Google 软件工程师李宇骞,他是 Flutter 团队的一位软件工程师,次要专一于晋升其性能。上面是具体的演讲内容:

2019 下半年,Flutter 团队共收到 23 个量化的性能晋升;2020 上半年,Flutter 团队共收到 27 个量化的性能晋升。2020 上半年 Flutter 团队共收到来自 78 位开发者的 49 个性能改良。

工具的性能非常重要,性能测试也同样至关重要,领有良好的性能测试能够:

  • 疾速重现问题;
  • 迭代和验证解决方案;
  • 提供数据,激励进一步的工作并避免倒退。

通常,能耗与渲染速度相干,每一帧渲染工夫越长则能耗就越高,但能耗并不能掂量渲染速度,因为在某些状况下渲染速度快也可能会导致能耗升高,渲染速度慢也可能不耗能。

CPU 上运行工夫尽管短,但因为新的算法利用了更多的 GPU 外围,所以 GPU 能耗反而减少;有些 CPU 上的工作被别的 I/O 或 GPU 工作阻塞,进行了长时间的期待,而期待的工夫内并无过多能耗。

因而,在速度之外减少能耗测试是十分必要的。因为 Flutter 团队在 GitHub 上收到的大部分能耗问题都和 iOS 相干,所以此次 Flutter 首先退出了 iOS 的能耗测试,Android 的能耗测试工具会于后续退出。

开发者能够应用 Flutter Gallery App 在 Timeline 中查看 CPU/GPU 的使用率,也能够用集成测试自动检测 CPU/GPU 的使用率。

Flutter 还新退出了 SkSL 着色器编译预热性能,来帮忙开发者打消着色器编译卡顿。如果一个 Flutter 程序第一次渲染某类动画时呈现显著的卡顿,然而之后渲染这些动画时,卡顿齐全隐没,那么这就很可能是着色器编译卡顿。开发者能够应用 –trace-skia,而后查看 Timeline 来确认是否为着色器卡顿。

值得一提的是,SkSL 能够实现自动化生成与测试,这对于须要继续更新的 Flutter App 来说,能够节俭很多的人力。

内存和包体积的测试工具

接下来,是由 Flutter 用户体验研究员侯悠扬带来的测试工具专题。侯悠扬于 2017 年退出 Google,并于 2019 年退出 Flutter 团队。她是 Flutter 团队一名用户体验研究员,关注晋升 Flutter 产品和开发工具的程序员体验。

此次,Flutter 团队更新了 Dart 开发工具。Dart 开发工具是面向 Flutter 和 Dart 开发人员的工具套件,包含如下一些小工具:

  • 布局查看(Inspector)
  • 性能调试(Performance)
  • 内存调试(Memory)
  • 网络调试(Network)
  • 包体积调试(App Size)
  • 调试器(Debugger)
  • 日志(Logging)

连贯上设施而后运行 Flutter 利用,点击 Android Studio 底部工具栏中的【Open DevTools】按钮即可开启调试性能。

内存调试器性能

Flutter 的内存调试器提供如下性能:

  • 事件窗格(Dart 和 Android 内存)
  • 手动和主动快照(snapshot)和垃圾回收(GC)
  • 内存剖析
  • 内存堆调配累加器(Heap Allocation Accumulators)
  • 通过命令行界面将内存统计信息到处到 JSON 文件

内存测试

内存测试提供如下性能:

  • 通过 ADB 交互间接进行内存测试
  • Dart 开发工具内存测试
  • iOS 内存测试

更多信息能够通过这篇由 Flutter 工程师撰写的文章进行理解:怎么进行 Flutter 内存测试

包体积调试器性能

包体积调试器提供如下性能:

  • 可视化了应用程序的总大小,包含性能级别的 Dart AOT 快照;
  • 剖析快照和利用包(APK,IPA 等);
  • 剖析快照或利用程序包(APK,IPA 等)的差别;
  • 查看软件包级别的利用大小归因数据。

Pigeon 与 Flutter 混合开发

什么是 Pigeon

在晚期的 hybird 开发模式中,前端和 Native 交互时须要 native 双端为 JS 提供接口。这种状况下如何标准命名,参数等就成了一个问题,如果独自保护一份协定文件,三端按照协定文件进行开发,很容易呈现协定更改后,没有及时同步,又或者在理论开发过程没有依照标准,可能导致各种意外状况。

同样,在 Flutter 插件包的开发中,因为波及到 Native 双端代码开发能力,Dart 侧裸露对立的接口给使用者,也会呈现同样的问题,此时 Pigeon 应运而生,Pigeon 是 Flutter 官网举荐插件管理工具,能够应用来解决和优化 Native 插件开发上 platform channel 相干的问题。

Flutter 官网提供的 Pigeon 插件,通过 dart 入口,生成双端通用的模板代码,Native 局部只需通过重写模板内的接口,无需关怀 methodChannel 局部的具体实现,入参,出参也均通过生成的模板代码进行束缚。接口新增,或者参数批改,只须要在 dart 侧更新协定文件,生成双端模板,即可达到同步更新,无效的防止了参数批改,参数新增带来的双端代码不同步的问题,上面是 Pigeon 工作原理示意图。

上面是 Pigeon 给出的示例:

能够看到接入 Pigeon 后整体代码简洁了不少,而且标准了类型定义。

Pigeon 接入

接下来咱们看一下如何从零接入 Pigeon。截止目前,Pigeon 曾经公布了 0.1.15 版本,如下图所示。

首先,新建一个名为 testpigeon 的 Flutter 我的项目,关上我的项目的 pubspec.yaml 文件,并增加如下依赖代码。

dependencies:
  pigeon: ^0.1.15

而后,依照官网的要求在我的项目目录下新建一个 pigeons 目录,作为寄存 dart 侧的入口文件,内容为接口、参数、返回值的定义等,以及前面通过 pigeon 的命令,生产 native 端代码。接下来,新建一个 message.dart 文件,并增加如下。

import 'package:pigeon/pigeon.dart';

class SearchRequest {String query;}

class SearchReply {String result;}

@HostApi()
abstract class Api {SearchReply search(SearchRequest request);
}

在下面的 message.dart 文件中,通过 @HostApi() 注解标示了通信对象和接口,之后咱们只须要执行如下命令,就能够生成对应代码到工程中。

flutter pub run pigeon  --input pigeons/message.dart

其实下面的命令是上面命令的简写形式:

flutter pub run pigeon  --input pigeons/message.dart  --dart_out lib/pigeon.dart  --objc_header_out ios/Runner/pigeon.h --objc_source_out ios/Runner/pigeon.m --java_out android/app/src/main/java/Pigeon.java --java_package "com.xzh.testpigeon"

命令的参数的含意如下:

  • –input:引入了咱们创立的 message.dart 文件;
  • –dart_out:输入了 dart 模板文件;
  • –objc_header_out 和 –objc_source_out 输入了 object-c 文件;
  • –java_out 输入了 java 文件;

命令执行后 dart 文件输入到 lib 目录下,object-c 文件输入到了 ios/Runner 目录下,java 文件输入到指定的 com.xzh.testpigeon” 包名门路下,之后就能够开始正式接入。而后咱们别离应用 Android Studio 和 Xcode 关上原生工程代码。

Android 工程代码

应用 Android Studio 关上 Flutter 我的项目的原生 Android 工程,生成的代码如下:

// Autogenerated from Pigeon (v0.1.15), do not edit directly.
// See also: https://pub.dev/packages/pigeon

package com.xzh.testpigeon;

import io.flutter.plugin.common.BasicMessageChannel;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.StandardMessageCodec;
import java.util.ArrayList;
import java.util.HashMap;

/** Generated class from Pigeon. */
@SuppressWarnings("unused")
public class Pigeon {

  /** Generated class from Pigeon that represents data sent in messages. */
  public static class SearchReply {
    private String result;
    public String getResult() { return result;}
    public void setResult(String setterArg) {this.result = setterArg;}

    HashMap toMap() {HashMap<String, Object> toMapResult = new HashMap<>();
      toMapResult.put("result", result);
      return toMapResult;
    }
    static SearchReply fromMap(HashMap map) {SearchReply fromMapResult = new SearchReply();
      Object result = map.get("result");
      fromMapResult.result = (String)result;
      return fromMapResult;
    }
  }

  /** Generated class from Pigeon that represents data sent in messages. */
  public static class SearchRequest {
    private String query;
    public String getQuery() { return query;}
    public void setQuery(String setterArg) {this.query = setterArg;}

    HashMap toMap() {HashMap<String, Object> toMapResult = new HashMap<>();
      toMapResult.put("query", query);
      return toMapResult;
    }
    static SearchRequest fromMap(HashMap map) {SearchRequest fromMapResult = new SearchRequest();
      Object query = map.get("query");
      fromMapResult.query = (String)query;
      return fromMapResult;
    }
  }

  /** Generated interface from Pigeon that represents a handler of messages from Flutter.*/
  public interface Api {SearchReply search(SearchRequest arg);

    /** Sets up an instance of `Api` to handle messages through the `binaryMessenger` */
    static void setup(BinaryMessenger binaryMessenger, Api api) {
      {
        BasicMessageChannel<Object> channel =
            new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.Api.search", new StandardMessageCodec());
        if (api != null) {channel.setMessageHandler((message, reply) -> {HashMap<String, HashMap> wrapped = new HashMap<>();
            try {@SuppressWarnings("ConstantConditions")
              SearchRequest input = SearchRequest.fromMap((HashMap)message);
              SearchReply output = api.search(input);
              wrapped.put("result", output.toMap());
            }
            catch (Exception exception) {wrapped.put("error", wrapError(exception));
            }
            reply.reply(wrapped);
          });
        } else {channel.setMessageHandler(null);
        }
      }
    }
  }
  private static HashMap wrapError(Exception exception) {HashMap<String, Object> errorMap = new HashMap<>();
    errorMap.put("message", exception.toString());
    errorMap.put("code", exception.getClass().getSimpleName());
    errorMap.put("details", null);
    return errorMap;
  }
}

下面生成的 Pigeon.java 代码中蕴含了 Api 接口用于开发者实现交互逻辑,同时开发者能够通过 SearchRequest 获取 dart 发送过去的申请,通过 SearchReply 返回数据给 dart。而后,还须要在 Android 的入口文件 MainActivity 中实现 Api 接口来实现数据交互,代码如下。

public class MainActivity extends FlutterActivity {

  private class MyApi implements Pigeon.Api {
    @Override
    public Pigeon.SearchReply search(Pigeon.SearchRequest request) {Pigeon.SearchReply reply = new Pigeon.SearchReply();
      reply.setResult(String.format("Hi %s!", request.getQuery()));
      return reply;
    }
  }

  @Override
  protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
    GeneratedPluginRegistrant.registerWith(this);
    Pigeon.Api.setup(getFlutterView(), new MyApi());
  }
}

首先,咱们继承 Pigeon.Api 实现了 MyApi 对象,而后在 search() 办法中通过 request.getQuery() 获取 dart 的申请数据,并且通过 Pigeon.SearchReply 的 setResult 返回 数据给 dart 端,最初通过 Pigeon.Api.setup(getFlutterView(), new MyApi()) 启动。

iOS

应用 Xcode 关上 Flutter 我的项目的 iOS 工程,把生成的 pigeon.h 和 pigeon.m 文件 link 到 Xcode 工程里,之后如下代码所示在 AppDelegate.h 引入 Api 协定。

#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
#import "pigeon.h"

@interface AppDelegate : FlutterAppDelegate<Api>

@end

接下来,在 AppDelegate.m 中实现 search 接口,并在收到的 dart 音讯后基于回复,最初调用 ApiSetup()办法将实现注册。

#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {[GeneratedPluginRegistrant registerWithRegistry:self];
  // Override point for customization after application launch.
  FlutterViewController* controller =
      (FlutterViewController*)self.window.rootViewController;
  ApiSetup(controller.binaryMessenger, self);
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}


-(SearchReply *)search:(SearchRequest*)input error:(FlutterError **)error {SearchReply* result = [[SearchReply alloc] init];
    result.result  = [NSString stringWithFormat:@"%s%@","Hi",input.query];
    return result;
}


@end

Dart 测试

最初咱们在 Dart 代码中新建一个测试的代码,如下所示。

import 'pigeon.dart';

void main() {testWidgets("test pigeon", (WidgetTester tester) async {SearchRequest request = SearchRequest()..query = "Aaron";
    Api api = Api();
    SearchReply reply = await api.search(request);
    expect(reply.result, equals("Hi Aaron!"));
  });
}

Flutter 在阿里巴巴的利用

首先,主持人为咱们介绍了 Flutter 的历史,介绍围绕好看、高效、流程和凋谢等几个方面来介绍 Flutter。

接下来,阿里巴巴的无线技术专家门柳介绍 Flutter 在阿里巴巴的利用,闲鱼是阿里巴巴 Flutter 技术实际的先驱,也是国内最早尝试 Flutter 技术的大型互联网公司,而阿里巴巴旗下的淘宝也不甘示弱,也在某些模块结成 Flutter,不过大多是业务级别的模块,而没有像闲鱼那样大规模应用。咱们能够从下图看到 Flutter 在阿里巴巴的应用状况。


那为什么,这么多的挪动利用开始应用 Flutter 来进行开发呢?首先,让咱们来理解下跨平台技术的倒退历程。

能够发现,挪动跨平台开发经验了大概四个阶段:

  • 晚期的 WebView 加载计划
  • 原生 API 桥接的 Hybrid 计划
  • 原生渲染计划(Web 语法 + 原生 UI)
  • 自绘渲染(独立布局 / 渲染)

而 Flutter 就是采纳的自绘渲染计划,有趣味的童鞋能够钻研以下 Flutter 的架构。为什么抉择 Flutter 进行跨平台利用开发呢,上面是 Flutter 所具备的一些劣势:

不过,Flutter 也不是万能的,Flutter 目前处于疾速迭代的阶段,所以保险起见,咱们只在一些惯例的业务开发和模块化的 UI 界面开发和局部游戏中应用 Flutter。

总结起来,就是在一些富交互类利用和新型的利用中应用 Flutter,对于视频、直播等渲染要求高的则持续应用原生进行开发。

那应用 Flutter 进行利用开发时,有哪些教训和问题须要留神呢?下图显示了阿里巴巴在应用 Flutter 进行利用开发时遇到的一些问题,大家应用时须要躲避。

首先遇到的问题是,因为 Flutter 应用的是 Dart 进行开发,无疑减少了开发者的学习老本。其次,对于大型利用来说,如何保障代码品质,如何在多个平台运行自动化测试脚本也是一个问题;并且因为 Flutter 作为一门新的技术,如何疾速的将老得业务迁徙过去也是大家须要思考的问题。总结一下,就是调试、测试、状态治理、弛缓导航栈治理、跨平台兼容以及如何寻找解决方案的问题。

只管 Flutter 曾经提供了很多的工具,然而如何将它融入到阿里巴巴的客户端开发工作流中,是大家须要思考的问题。

首先,为了晋升开发效率,升高初期的接入老本,咱们将 Flutter Toolkit 融入到 Alibab DevOps 工作流中,并自研了一些工具、打包和公布平台以及搭建调试环境。接下来,咱们基于现存的技术积攒,研发了一些中间件。

上面来看一个实例,即如何解决多图列表页面的内存占用问题。这类问题的特色如下:

  • 页面很长,图片很多,首次加载工夫很长
  • 大量图片同时加载并生成纹理,内存飙升
  • Sliver 中每项 Cell 拆分粒度很大,单个 Cell 占用多屏,难以回收


对于列表 Flutter 列表内存回收的问题,大家能够浏览 细化 Flutter List 内存回收,解决大 Cell 问题这篇文章。

对于下面的多图长列表的内存问题,咱们能够从以下几个方面着手进行优化:

  • 拆分 Cell,使每一项变得更小
  • 依据坐标判断图片是否在屏幕内,进而进行图片的懒加载和回收
  • 提前获取图片的宽高大小,缩小布局和重绘
  • 以图片为单位进行纹理回收,而不是 Sliver 中的每项 Cell 为单位
  • 外接原生图片库,实现共享本地缓存


最初,咱们来看一下 Flutter 在阿里巴巴的体系化建设。首先,Flutter 的体系化建设次要从根底能力建设、研发平台和可继续迭代等几个方面着手。

上面是 Flutter 在阿里巴巴平台建设的具体的一些计划。

目前,Flutter 在阿里巴巴曾经通过了大规模的利用,并且咱们本人的技术体系建设也在稳步举荐中,前面会将建设的一些成绩通过社区分享进去。

附:Google 开发者大会

正文完
 0