Flutter混合工程开发探究

做者:腾讯NOW直播 -koudleren(任晓帅)java

团队介绍

腾讯NOW终端技术团队在Flutter推出后就一直在关注Flutter的发展,而且在2018年4月份将Flutter引入到NOW直播APP中,在将Flutter运用在业务中的同事,也一直在研究并完善Flutter的技术,但愿将本身团队的技术和经验分享给其余团队。android

前言

Flutter做为一个新的UI开发框架,由于其创新的理念,已经吸引了愈来愈多的人参与其中,在实际的项目开发中,咱们更但愿将Flutter引入到咱们的APP中,而不是用Flutter从新开发一款全新的APP。因此做为开发者,咱们更关心的是,如何使用Flutter进行混合工程的开发?值得关注的是,将Flutter集成到现有的工程中的方法, 随着Flutter框架的升级也在不断变换中。而本文将介绍NOW直播在混合开发中采用的方法,首先带领你们从Flutter插件和编译脚本入手,研究Flutter的构建工程目录结构,最后本身实现Flutter混合工程开发的插件,给其余团队以参考。git

从建立Flutter工程提及

按照官网的提示,要进行Flutter的开发,要以下几步:github

一、下载Flutter SDKapi

二、配置环境变量bash

三、在Android Studio上安装Flutter和Dart插件app

四、File > New Flutter Project > Flutter application > Enter a project name > Finish框架

以后在Android Studio 的toolbar上就能够看到以下的图标:性能

Main IntelliJ toolbar

1.Flutter 插件

咱们点击Android Studio 上的New菜单,建立Flutter工程,而后点击Android Studio toolbar上的Run运行Flutter程序,不由思考,为何Flutter工程要这样建立,在点击Run以后发生了什么,Hot Reload是怎么实现的,为了一探究竟,咱们能够从Flutter 的Android Studio插件源码入手。gradle

在GitHub上,有Flutter Android Studio插件的源码,地址为:

https://github.com/flutter/flutter-intellij

经过阅读插件的源代码,能够发现插件实现不一样功能的代码逻辑是怎样的,咱们能够根据插件的配置信息查找具体的插件动做及其实现类,

插件配置信息所在的文件为:resources/META-INF/plugin.xml

下面咱们将分别找到New Flutter Project 和 Run Flutter Project的配置信息,并进行分析。

1.1 New Flutter Project

<!-- Define the 'New Flutter Project' menu item -->
    <action id="flutter.NewProject" class="io.flutter.actions.FlutterNewProjectAction" text="New Flutter Project..." description="Create a new Flutter project"> <add-to-group group-id="NewProjectOrModuleGroup" anchor="after" relative-to-action="NewProject"/> </action> 复制代码

在注释里能够看到,这个配置信息就是New Flutter Project的,action 的 id 是flutter.NewProject,对应的类文件是io.flutter.actions.FlutterNewProjectAction,

因而找到这个类,分析里面实现的类,能够发现以下的关系:

FlutterNewProjectAction(新建工程的入口) > FlutterProjectModel(填写工程信息的窗口) > FlutterProjectCreator (工程建立准备及验证) > FlutterSmallIDEProjectGenerator(获取Flutter SDK信息)> FlutterSdk (调用了Flutter SDK的命令行代码)

在Flutter工程建立的过程当中,最核心的代码实际上是这一行:

return new FlutterCommand(this, appDir.getParent(), FlutterCommand.Type.CREATE, vargs)
复制代码

这一句实际上是运行了Flutter SDK的一句命令行:

$ flutter create --template=app --org=now.tencent.com flutter_app
复制代码

使用Flutter的命令行建立了工程。

1.2 Run Flutter Project

<!-- main toolbar run actions -->
    <action id="Flutter.Toolbar.ReloadAction" class="io.flutter.actions.ReloadFlutterAppRetarget"
            description="Reload"
            icon="FlutterIcons.HotReload">
      <add-to-group group-id="ToolbarRunGroup" anchor="after" relative-to-action="RunnerActions"/>
      <keyboard-shortcut keymap="$default" first-keystroke="ctrl BACK_SLASH"/>
    </action>

<!-- run menu actions -->
    <group id="Flutter.MenuActions.Run">
      <separator/>
      <reference ref="Flutter.Toolbar.ReloadAction"/>
      <action id="Flutter.Toolbar.RestartAction" class="io.flutter.actions.RestartFlutterAppRetarget"
              description="Restart"
              icon="FlutterIcons.HotRestart">
        <keyboard-shortcut keymap="$default" first-keystroke="ctrl shift BACK_SLASH"/>
        <keyboard-shortcut keymap="$default" first-keystroke="ctrl shift S"/>
      </action>
      <action id="Flutter.Menu.RunProfileAction" class="io.flutter.actions.RunProfileFlutterApp"
              description="Flutter Run Profile Mode"
              icon="AllIcons.Actions.Execute">
      </action>
      <action id="Flutter.Menu.RunReleaseAction" class="io.flutter.actions.RunReleaseFlutterApp"
              description="Flutter Run Release Mode"
              icon="AllIcons.Actions.Execute">
      </action>
      <separator/>
      <add-to-group group-id="RunMenu" anchor="after" relative-to-action="Stop"/>
    </group>
复制代码

一样的原理,能够发现核心的代码以下:

[SdkRunConfig.java]
return fields.createFlutterSdkRunCommand(project, mode, FlutterLaunchMode.fromEnv(env), device);
复制代码
[SdkFields.java]
final FlutterCommand command = flutterSdk.flutterRun(root, main.getFile(), device, runMode, flutterLaunchMode, args);
    return command.createGeneralCommandLine(project);
复制代码

实际上是运行了Flutter SDK的一句命令行:

$ flutter run ...
复制代码

将Flutter程序Run起来,并实现了Hot Reload。

2.Flutter编译脚本

若是说Flutter的插件,是将Flutter和Android Studio(IDE)链接在一块儿,那么Flutter的gradle编译脚本就是将Flutter和Android链接在一块儿:在Android工程中添加Flutter的依赖,并采用不一样的编译策略,生成Flutter的构建产物,最后将Flutter产物打包到Android的APK中。

Flutter编辑脚本所在的位置是Flutter SDK文件夹下的packages/flutter_tools/gradle/flutter.gralde

接下来咱们将分析Flutter编译脚本的代码,研究Flutter编译脚本的做用。主要作的内容包括:

2.1 获取Flutter 环境变量

包括:

一、Flutter SDK的路径

二、获取flutter.bat 的路径

三、Flutter jar 包的路径

四、编译模式

等等,部分代码以下:

String flutterRootPath = resolveProperty(project, "flutter.sdk", System.env.FLUTTER_ROOT)

2.2 添加Flutter的依赖,包括jar包和so包

根据buildMode的不一样,依赖不一样的配置,部分代码以下:

/** * Adds suitable flutter.jar api dependencies to the specified buildType. * * Note: The BuildType DSL type is not public, and is therefore omitted from the signature. */
private void addFlutterJarApiDependency(Project project, buildType) {
    project.dependencies {
        String configuration;
        if (project.getConfigurations().findByName("api")) {
            configuration = buildType.name + "Api";
        } else {
            configuration = buildType.name + "Compile";
        }
        add(configuration, project.files {
            String buildMode = buildModeFor(buildType)
            if (buildMode == "debug") {
                [flutterX86Jar, debugFlutterJar]
            } else if (buildMode == "profile") {
                profileFlutterJar
            } else {
                releaseFlutterJar
            }
        })
    }
}
复制代码

2.3运行Flutter的SDK对Dart代码构建产物

​ 咱们知道Flutter是用Dart代码编写的,为了让Dart代码能够运行在Flutter engine上,须要对Flutter代码进行编译,这里有两种编译方式:

一、JIT编译

​ JIT(Just In Time),即时编译 ,Flutter在debug模式下使用此种编译方式,能够实现Hot Reload

二、AOT编译

​ AOT(Ahead Of Time),静态提早编,编译成本地机器码 ,Flutter在Release模式下使用此种编译方式,拥有更好的性能 。

FlutterTask flutterTask = project.tasks.create(name: "flutterBuild${variant.name.capitalize()}", type: FlutterTask) {
                flutterRoot this.flutterRoot
                flutterExecutable this.flutterExecutable
                buildMode flutterBuildMode
                localEngine this.localEngine
                localEngineSrcPath this.localEngineSrcPath
                targetPath target
                verbose verboseValue
                previewDart2 previewDart2Value
                fileSystemRoots fileSystemRootsValue
                fileSystemScheme fileSystemSchemeValue
                trackWidgetCreation trackWidgetCreationValue
                buildSnapshot buildSnapshotValue
                buildSharedLibrary buildSharedLibraryValue
                targetPlatform targetPlatformValue
                sourceDir project.file(project.flutter.source)
                intermediateDir project.file("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter/${variant.name}")
                extraFrontEndOptions extraFrontEndOptionsValue
                extraGenSnapshotOptions extraGenSnapshotOptionsValue
            }
复制代码

2.4将构建的产物copy到Android assets目录下

​ 在使用不一样编译器编译的时候生成的产物也不同,具体查看下面2.5的图。

Task copyFlutterAssetsTask = project.tasks.create(name: "copyFlutterAssets${variant.name.capitalize()}", type: Copy) {
                dependsOn flutterTask
                dependsOn variant.mergeAssets
                dependsOn "clean${variant.mergeAssets.name.capitalize()}"
                into variant.mergeAssets.outputDir
                with flutterTask.assets
            }
复制代码

2.5Flutter APK目录结构

​ 讲过上面的介绍,再来看Flutter APK的目录结构,就很容易理解,下图分别是Flutter Debug APK和Release APK目录结构的对比:

建立Flutter混合工程

NOW直播从Flutter的0.3.1版本开始接入,到如今Flutter发布到0.5.7,见证了Flutter建立混合工程的方法的变化,方法虽然在变,可是原理不变,通过上面介绍的Flutter插件和编译脚本,咱们很容易理解Flutter建立混合工程的原理:就是经dart代码通过Flutter SDK编译,而后把生成的产物打包到制定的APK路径下。如今将告诉你如何建立Flutter的混合工程。

Flutter 最新版本0.5.7建立混合工程的方法以下:

一、在已有工程的同级目录里,运行:

$ flutter create -t module my_flutter
复制代码

二、在已有工程的settings.gradle里添加Flutter 的model,以下:

// MyApp/settings.gradle
include ':app'          // assumed existing content
setBinding(new Binding([gradle: this]))    // new
evaluate(new File(                        // new
  settingsDir.parentFile,                 // new
  'my_flutter/.android/include_flutter.groovy'  // new
))         // new
复制代码

三、在已有工程的build.gradle里添加Flutter model的依赖,以下:

// MyApp/app/build.gradle
:dependencies {  
	implementation project(':flutter')  
	:
}
复制代码

通过上面的步骤咱们就建立了Flutter的混合工程,将Flutter工程Run起来并使用Hot Reload 的方法有两种:

一、使用flutter run

$ cd MyApp
$ ./gradlew app:assembleDebug
$ cd ../xyz
$ flutter run --use-application-binary \
    ../MyApp/app/build/outputs/apk/debug/app-debug.apk
复制代码

二、使用flutter attach

cd <path to your Flutter module>
flutter attach
复制代码

在混合工程里使用Flutter,发现纯Flutter 工程里在Android Studio 的toobar上显示的icon不见了,想使用Flutter,只能使用命令行来运行,因此其实咱们能够在Flutter插件上扩展混合工程建立的命令及Run 和 HotReload的命令。

本身实现Flutter混合工程的插件

在NOW直播里为了方便的进行Flutter混合工程的开发,本身实现了一个插件,示例以下:

<action id="New-Flutter-Model" class="now.tencent.FlutterNewModel"
            text="New Flutter Model"
            description="Create a new Flutter model">
      <add-to-group group-id="NewProjectOrModuleGroup" anchor="after" relative-to-action="NewProject"/>
    </action>
    
   <action id="Hot-reload" class="now.tencent.now.HotReload" text="HotReload" description="HotReload when has started">
      <add-to-group group-id="ToolbarRunGroup" anchor="after" relative-to-action="RunnerActions"/>
      <keyboard-shortcut keymap="$default" first-keystroke="shift ctrl alt 0"/>
    </action>

    <action id="Start-Hot" class="now.tencent.now.StartHotReload" text="StartHotReload" description="StartHotReload">
      <add-to-group group-id="ToolbarRunGroup" anchor="after" relative-to-action="RunnerActions"/>
      <keyboard-shortcut keymap="$default" first-keystroke="shift ctrl alt 9"/>
    </action>
复制代码

这个插件,NOW直播将在以后开源,欢迎关注。

总结

通过上面的步骤,NOW直播团队已经解决了Flutter混合工程开发的问题,经过实现自定义的插件,解决了实际开发过程当中的问题,也在不断完善Flutter的生态,让咱们期待Flutter变的更好!

相关文章
相关标签/搜索