Flutter

Flutter引擎源码调试与Channel底层原理探索

2021-12-26  本文已影响0人  浅墨入画

配置项目代码关联引擎源码

通过下载引擎源码可以进行分析以及动态调试

下面新建工程flutter_engine_demo(注意iOS与Android平台的语言选择OC以及Java),让其加载上面编译好的自定义引擎

基于Debug与Release环境配置

Generated.xcconfig是通用环境配置,我们在这个文件中进行配置

通用配置 查看脚本 脚本目录 执行xcode_backend.dart脚本 用AS查看xcode_backend.dart脚本文件

这就是Android Studio执行了Flutter工程会调用Xcode,而Xcode又关联到Android Studio的过程;关联的过程都在脚本文件中处理好了,后面如果想配置持续集成进行打包,也需要配置相应的脚本文件。

关联自定义引擎相关配置 断点调试

通过断点调试,我们发现了touchesBegan的源码实现,这里的源码在FlutterViewController.mm文件中,即编译好的引擎中目录:/src/out/ios_debug_sim_unopt

下面验证FlutterViewController.mm就在关联的引擎工程中

Runner看到的源码中添加注释 引擎工程

我们在Runner工程中进行调试,添加了注释;然后打开引擎工程发现注释存在,就证明了Runner自定义引擎存在了关联。

编译Flutter引擎 断点调试 编译引擎

重新运行Runner工程,点击屏幕查看日志打印

点击屏幕打印日志

Runner成功与Flutter引擎进行关联,而且还可以修改引擎源码进行调试(每次修改源码都需要重新编译引擎)。

查看引擎源码目录

flutter_engine引擎源码中查看FlutterViewController.mm文件的目录,发现目录结构为/flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm

目录结构

我们发现引擎源码flutter目录下,并不在out目录,说明源码只有一份,根据真机、模拟器不同平台,编译出不同的Flutter.framework库。

查看工程Flutter.framework/Flutter的哈希值是否相同?
查看Flutter.framework 查看哈希值
// 查看哈希值命令
$ md5 Flutter
// Flutter.framework/Flutter 的哈希值
4a794a8d53a0fadebc0453fa16e56518
// Runner/Flutter.framework/Flutter 的哈希值
4a794a8d53a0fadebc0453fa16e56518

通过对比,哈希值相同,说明是一个产物。

查看发布版本的Flutter引擎哈希值

通过对比我们发现自定义引擎发布版本的引擎哈希值不相同。

哈希值对比

我们的工程在每次编译生成Flutter.framework的时候,可能会添加一些其它内容,我们不能单纯的比对Flutter.framwork的哈希值来判断Framework是否发生更新。

疑问?
Runnder项目实际上获取到的是编译完成的Flutter.framework,而Flutter.framework/Flutter是如何定位到源代码的路径呢?跨工程是如何定位到的?
Flutter二进制文件中包含一些调试信息,使其进行关联。

检查二进制文件中是否包含调试信息

查看发布版本的二进制文件调试信息

Flutter二进制文件 查看Flutter中是否包含调试信息 查看发布版本的调试信息

由打印信息可知,发布版本会把调试信息隐藏掉

查看自定义引擎的二进制文件调试信息
查看Flutter中是否包含调试信息 查看自定义版本的调试信息

通过终端成功查看到自定义引擎的调试信息;说明Runner工程成功与自定义引擎关联到了一块,就可以直接调试源码

检查⼆进制是否含有调试信息
#可以查看包含的架构
$ lipo -info xxx
#拆分架构
$ lipo xxx -thin armv7 -output armv7_xxx
#合并多架构
$ lipo -create xxx.a xxx.a -output xxx.a
$ lldb --file Flutter_arm64
(lldb) target create "Flutter_arm64" 
Current executable set to 'Flutter_arm64' (arm64)
// 查看有多少个编译单元,即.o文件
.(lldb) script lldb.target.module['Flutter_arm64'] .GetNumCompileUnits()
1
(lldb)
(lldb) target create "Flutter_arm64"
Current executable set to 'Flutter_arm64' (arm64).
(lldb) script 
Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
    // 获取到模型
>>> m = lldb.target.module['Flutter_arm64']
>>> for i in range(m.GetNumCompileUnits()):
...     cu = m.GetCompileUnitAtIndex(i).file.fullpath
...     print(cu)
...
None
>>>

调试引擎源码Channel底层实现

下面我们就通过Runner工程来调试Flutter引擎源码

<!-- main.dart文件 -->
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const EnginePage(),
    );
  }
}

class EnginePage extends StatefulWidget {
  const EnginePage({Key? key}) : super(key: key);

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

class _EnginePageState extends State<EnginePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('EnginePage'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            // 给原生发送消息
            const MethodChannel('engine_page')
                .invokeMapMethod('method_channel');
          },
          child: const Icon(Icons.add),
        ),
      ),
    );
  }
}
#import "AppDelegate.h"
#import "GeneratedPluginRegistrant.h"

@interface AppDelegate ()
@property(nonatomic, strong) FlutterEngine* flutterEngine;
@property(nonatomic, strong) FlutterViewController* flutterVc;
@property(nonatomic, strong) FlutterMethodChannel* methodChannel;
@end

@implementation AppDelegate
// 懒加载引擎,通过引擎获取VC
- (FlutterEngine *)flutterEngine {
    if (!_flutterEngine) {
        FlutterEngine * engine = [[FlutterEngine alloc] initWithName:@"hk"];
        if (engine.run) {
            _flutterEngine = engine;
        }
    }
    return _flutterEngine;
}

- (BOOL)application:(UIApplication *)application
        didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [GeneratedPluginRegistrant registerWithRegistry:self];

    self.flutterVc = [[FlutterViewController alloc] initWithEngine:self.flutterEngine nibName:nil bundle:nil];

    // 通过VC获取channel
    // MethodChannel:传递方法的调用
    self.methodChannel = [FlutterMethodChannel methodChannelWithName:@"engine_page" binaryMessenger:self.flutterVc.binaryMessenger];

    [self.methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) {
             NSLog(@"收到了:%@",call.method);
     }];

    // BasicMessageChannel:传递字符串和半结构化信息
    [FlutterBasicMessageChannel messageChannelWithName:@"123" binaryMessenger:self.flutterVc.binaryMessenger];

    // EventChannel:传递数据流
    [FlutterEventChannel eventChannelWithName:@"123" binaryMessenger:self.flutterVc.binaryMessenger];

    // Override point for customization after application launch.
    return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end
image.png image.png image.png

通过methodChannelWithName源码我们发现,如果不传解码器会有默认的解码器,而且是一个单例eventChannelWithName方法也是一样的,会默认传一个单例解码器

image.png

保存namemessenger解码器

image.png image.png

如果有连接connection先清空,否则设置消息回调

image.png image.png image.png image.png

用字典把namehandler进行保存

image.png

self.flutterVc.binaryMessengersetMethodCallHandler方法中创建的messageHandler是同一个对象。

疑问?其实通讯的是数据,那么传递的数据底层是怎么交互的?推荐跟踪invokeMapMethod底层源码......

codec编解码器

数据是怎么解析最终变成二进制的?下面探索编解码器的底层实现...

前面我们学习的任何一种channel,内部都有一个编解码器编解码器其实是一种通讯协议

image.png image.png

FlutterEventChannel就是通过MethodCodec编解码的

image.png
Flutter中,MessageCodec有多种实现:

查看FlutterStandardMessageCodec源码

image.png image.png

查看FlutterStandardMethodCodec源码

image.png image.png

编解码器底层都使用的FlutterStandardReaderWriter实现的,下面我们来分析FlutterStandardReaderWriter的源码

image.png image.png 写数据 读数据
上一篇下一篇

猜你喜欢

热点阅读