Flutter语法检测及原理剖析

79 篇文章 0 订阅
45 篇文章 0 订阅

前言

Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。

Fair是58技术开源的一个Flutter动态化的框架,能够实现UI和逻辑的动态化。 [图片上传失败…(image-faa0b7-1666677643144)]

开发者在使用Fair开发过程中存在一些痛点,比如可能会出现使用语法糖不正确或者存在不支持的语法糖问题,所以我们需要一个配套插件去提示用户使用Fair语法糖。

一、Flutter语法检测机制

在IDE中,Flutter语法检测机制是依赖Dart/Flutter插件实现的,即我们在开发Flutter前需要下载的Dart/Flutter插件。我们需要通过插件去提供Flutter的开发环境,同时插件也能提供语法检测功能。而插件其中的一个核心功能就是Analysis Server。

Analysis Server是什么?

Analysis Server是Dart SDK提供的一个Dart/Flutter语法分析服务,主要功能包括语法静态分析、代码提示、代码补全等。我们常用的Dart/Flutter IDE如intellij、Android Studio和VS Code都是通过安装Dart插件实现Dart开发环境的配置。

Dart/Flutter插件语法检测的工作流程

以Android Studio为例(因为对应插件的语言是Java,比较好阅读和理解),语法检测核心是Analysis Server,每当用户代码有改动的时候,就会通过Socket的方式同步给Analysis Server,当Analysis Server分析结束后也会将分析结果返回来,Dart插件则就是根据Analysis Server返的事件类型去做不同的处理,最终将结果刷新在用户IDE界面上。流程图如下:

Dart插件中Analysis Server的启动流程

当用户配置完Dart SDK路径后,插件会获取到配置路径,在插件启动的时候会启动一个进程去执行Dart SDK里的dart可执行文件,同时也会获取到Dart SDK目录下的"/bin/snapshots/analysis_server.dart.snapshot"文件,并将其路径作为vmArguments。我们可以看一小段代码:


//获取配置的SDK路径
mySdkHome = sdk.getHomePath();

//找到dart可执行文件的路径
final String runtimePath = FileUtil.toSystemDependentName(DartSdkUtil.getDartExePath(sdk));

//找到analysis_server.dart.snapshot文件路径
String analysisServerPath = FileUtil.toSystemDependentName(mySdkHome + "/bin/snapshots/analysis_server.dart.snapshot");

//拼凑vmArguments
String firstArgument = useDartLangServerCall ? "language-server" : analysisServerPath;

//创建Socket
myServerSocket =
        new StdioServerSocket(runtimePath, StringUtil.split(vmArgsRaw, " "), firstArgument, StringUtil.split(serverArgsRaw, " "), debugStream);

//创建AnalysisServer实现类
final RemoteAnalysisServerImpl startedServer = new RemoteAnalysisServerImpl(myServerSocket);

//这里的具体实现其实就是socket.start(),将socket启动起来
startedServer.start();

Dart插件与Analysis Server交互

Dart插件与Analysis Server交互类型主要分为两种,一种是id,一种是event。每次Dart插件给Analysis Server同步数据时都会生成一个uniqueId并缓存在HashMap,每次Analysis Server返数据的时候也会带上event或id,优先处理event类型数据。

Dart插件同步给Analysis Server数据示例:

request: {
  "id": String
  "method": "analysis.setAnalysisRoots"
  "params": {
    "included": List<FilePath>
    "excluded": List<FilePath>
    "packageRoots": optional Map<FilePath, FilePath>
  }
}

上述说到Dart插件每次给Analysis Server同步数据都会生成一个uniqueId,这里的生成规则就是通过具备原子属性的AtomicInteger的getAndIncrement()方法每同步一次数据+1。Dart插件对应Analysis Server每种事件实现一个Consumer或Processor(如果是event类型数据则是Processor,result事件类型的话就是Consumer)。

语法检测插件的挂载流程

插件是有一个挂载过程的,插件的挂载流程如下图所示:

小结一下:

  1. YamlParaser解析analysis_options.yaml文件中analyzer#plugins节点,获取到一个pluginNameList
  2. 根据pluginNameList遍历通过Plugin Locator获取到每个plugin的analyzer_plugin目录路径,即pluginPath
  3. 将pluginPath目录路径文件copy到系统的.dartServer/plugin_manager/unique Directory目录下,其中unique Directory是通过对pluginPath进行md5操作生成的串
  4. 执行pluginPath下/bin/plugin.dart的main方法

二、Fair语法检测插件的实现原理

当我们掌握了前面的前置知识,再去开发语法插件就简单许多了。

Fair语法检测插件的实现机制

Dart插件是基于Analysis Server实现了Dart语法的检测,Fair语法检测插件则是对Dart插件语法检测功能的补充和扩展,实现对Fair语法糖的检测,Fair语法插件本质页是一个Dart语法检测插件,可以通过配置pubspec.yaml和analysis_options.yaml来使用。

语法插件的开发流程

主要可以分为以下几步:

  1. 首先创建一个类去继承ServerPlugin,并实现抽象属性/方法。
  2. 然后跟lib同级创建tools/analyzer_plugin目录,内容如图所示:

  1. 最后在plugin.dart文件中注册步骤1中实现的ServerPlugin。
//示例
void main(List<String> args, SendPort sendPort) {
	ServerPluginStarter(FairPlugin(PhysicalResourceProvider.INSTANCE)).start(sendPort);
}

核心实现

Fair语法检测插件的核心就是抽象语法树遍历,通过对抽象语法树的遍历,获取到每个子节点,然后插入自定义语法检测逻辑。

1.创建AnalysisDriver

createAnalysisDriver()是ServerPlugin的一个抽象方法,开发者实现这个方法创建一个AnalysisDriver,然后我们可以通过AnalysisDriver的results数据流去监听AnalysisResult的返回,其中这个AnalysisResult就是Analysis Server对文件的分析结果。示例代码如下所示:

@override
AnalysisDriverGeneric createAnalysisDriver(plugin.ContextRoot contextRoot) {

	//获取contextRoot
	final contextRoots =
        ContextLocator(resourceProvider: resourceProvider).locateRoots(...);

	//获取ContextBuilder
	final contextBuilder =
        ContextBuilderImpl(resourceProvider: resourceProvider);

	//获取Analysis Context
	final context = contextBuilder.createContext(
        contextRoot: contextRoots.first, byteStore: byteStore);

	//获取Analysis Driver
	final dartDriver = context.driver;

	//监听分析结果
	dartDriver.results.listen((analysisResult) {_processResult(...);});
	return dartDriver;
}

2.感知代码变化

ServerPlugin提供contentChanged方法,我们只需要收到回调的时候将内容有改变的文件路径添加到AnalysisDriver即可

@override
void contentChanged(String path) {
  super.driverForPath(path)?.addFile(path);
}

3.利用AOP思想对AnalysisResult处理

Dart的抽象语法树遍历是通过访问者模式实现的,我们可以在我们想要处理的节点插入一个自定义的Visitor对其及其子节点实现访问。其中我们可以从AnalysisResult拿到编译单元(CompilationUnit),每个编译单元就是一棵抽象语法树,我们可以通过其accept()方法插入一个Visitor。

4.@FairPatch注解识别实现逻辑

我们了解到Dart抽象语法树是通过访问者模式实现的,就能够很简单地去实现自定义的逻辑了。我们要实现@FairPatch注解的识别,我们只需要在遍历Annotation节点时,判断一下annotation name是我们想要的FairPatch即可。示例代码如下:

class _FairVisitor extends RecursiveAstVisitor<void> {

  @override
  void visitAnnotation(Annotation node) {
    node.visitChildren(this);
    if (node.name.name == \'FairPatch\') {
      //...
    }
  }
}
5.if语法检测

因为if语法检测是需要前置条件的,首先得是用@FairPatch标注的类,其次还需要是在build()方法下的if才进行检测。

  • 首先是build()方法检测,只需要在method deceleration的时候进行一下方法过滤即可。
@override
void visitMethodDeclaration(MethodDeclaration node) {
	if (node.name.name == \'build\') {
		//...
	}
}

  • 其次是if语法检测,稍微复杂一点,if分为IfStatement和IfElement,IfStatement指代的是外层的if-else语句,IfElement指代的是嵌套在其他语法里的if-else语句,如果想要全覆盖if语法,则需要两者都实现。

上述两张图分别对应IfStatement和IfElement具体含义。

6.Fair的Sugar.if语法糖分类实现

在Fair中常用的Sugar.if相关语法糖有Sugar.ifEqual、Sugar.ifEqualBool和Sugar.ifRange

  • Sugar.ifRange
static K ifRange<T, K>(
    T actual,
    List<T> expect, {
    required K trueValue,
    required K falseValue,
  }) =>
      expect.contains(actual) ? trueValue : falseValue;
  • Sugar.ifEqual
static K ifEqual<T, K>(
    T actual,
    T expect, {
    required K trueValue,
    required K falseValue,
  }) =>
      expect == actual ? trueValue : falseValue;
  • Sugar.ifEqualBool
static K ifEqualBool<T, K>(
    bool state, {
    required K trueValue,
    required K falseValue,
  }) =>
      state ? trueValue : falseValue;

使用Fair语法糖能够加快Fair的编译,所以我们都更推荐能使用语法糖就使用语法糖,我们要实现对语法糖分类,其实就是对if的condition进行区分,其中ifRange稍微复杂一点,则我们再讲下如何实现,其他也同理:

//ifRange有个前置条件就是如参expect是一个List,所以再检测的时候只需要判断用户在调用list.contains(xx)即可

//先判断用户是在调用contains()方法
  @override
  void visitMethodDeclaration(MethodDeclaration node) {
    if (node.name.name == \'contains\') {
      //...
    }
  }

//其次再判断调用contains方法的对象是List
  @override
  void visitMethodInvocation(MethodInvocation node) {
    super.visitMethodInvocation(node);

    //判断staticType即可
    _result = node.target?.staticType?.isDartCoreList ?? false;
    if (_result) {
      _target = node.target;
      _actual = node.argumentList.arguments.first;
    }
  }

三、Fair Plugin实现效果展示

  • Android Studio实现对build()方法下if语法块的检测效果
  1. build方法下if的代码检测,及提示引导信息

  1. 点击more action 或者 AS代码提示快捷键

  1. 根据提示点击替换

相关推荐

支持我们

欢迎大家使用 Fair,也欢迎大家为我们点亮star

Github地址:fair.58.com
Fair官网:fair.58.com

作者:王猛猛
链接:https://juejin.cn/post/7156160803719249933

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

一、架构师筑基必备技能

1、深入理解Java泛型
2、注解深入浅出
3、并发编程
4、数据传输与序列化
5、Java虚拟机原理
6、高效IO
……

在这里插入图片描述

二、Android百大框架源码解析

1.Retrofit 2.0源码解析
2.Okhttp3源码解析
3.ButterKnife源码解析
4.MPAndroidChart 源码解析
5.Glide源码解析
6.Leakcanary 源码解析
7.Universal-lmage-Loader源码解析
8.EventBus 3.0源码解析
9.zxing源码分析
10.Picasso源码解析
11.LottieAndroid使用详解及源码解析
12.Fresco 源码分析——图片加载流程

在这里插入图片描述

三、Android性能优化实战解析

  • 腾讯Bugly:对字符串匹配算法的一点理解
  • 爱奇艺:安卓APP崩溃捕获方案——xCrash
  • 字节跳动:深入理解Gradle框架之一:Plugin, Extension, buildSrc
  • 百度APP技术:Android H5首屏优化实践
  • 支付宝客户端架构解析:Android 客户端启动速度优化之「垃圾回收」
  • 携程:从智行 Android 项目看组件化架构实践
  • 网易新闻构建优化:如何让你的构建速度“势如闪电”?

在这里插入图片描述

四、高级kotlin强化实战

1、Kotlin入门教程
2、Kotlin 实战避坑指南
3、项目实战《Kotlin Jetpack 实战》

  • 从一个膜拜大神的 Demo 开始

  • Kotlin 写 Gradle 脚本是一种什么体验?

  • Kotlin 编程的三重境界

  • Kotlin 高阶函数

  • Kotlin 泛型

  • Kotlin 扩展

  • Kotlin 委托

  • 协程“不为人知”的调试技巧

  • 图解协程:suspend

在这里插入图片描述

五、Android高级UI开源框架进阶解密

1.SmartRefreshLayout的使用
2.Android之PullToRefresh控件源码解析
3.Android-PullToRefresh下拉刷新库基本用法
4.LoadSir-高效易用的加载反馈页管理框架
5.Android通用LoadingView加载框架详解
6.MPAndroidChart实现LineChart(折线图)
7.hellocharts-android使用指南
8.SmartTable使用指南
9.开源项目android-uitableview介绍
10.ExcelPanel 使用指南
11.Android开源项目SlidingMenu深切解析
12.MaterialDrawer使用指南
在这里插入图片描述

六、NDK模块开发

1、NDK 模块开发
2、JNI 模块
3、Native 开发工具
4、Linux 编程
5、底层图片处理
6、音视频开发
7、机器学习

在这里插入图片描述

七、Flutter技术进阶

1、Flutter跨平台开发概述
2、Windows中Flutter开发环境搭建
3、编写你的第一个Flutter APP
4、Flutter开发环境搭建和调试
5、Dart语法篇之基础语法(一)
6、Dart语法篇之集合的使用与源码解析(二)
7、Dart语法篇之集合操作符函数与源码分析(三)

在这里插入图片描述

八、微信小程序开发

1、小程序概述及入门
2、小程序UI开发
3、API操作
4、购物商场项目实战……

在这里插入图片描述

全套视频资料:

一、面试合集
在这里插入图片描述
二、源码解析合集

在这里插入图片描述
三、开源框架合集

在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取↓↓↓

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值