当前位置:   article > 正文

Android Flutter编译_flutter 编译apk

flutter 编译apk

环境准备

flutter_windows_3.3.10-stable(flutter sdk)
windows 10

flutter build apk流程梳理

注意:
以下{flutter sdk path}表示flutter sdk安装目录

1.这里的flutter是{flutter sdk path}\bin\flutter.bat 批处理程序

而批处理程序本质是运行了dart程序

"%dart%" --disable-dart-dev --packages="%flutter_tools_dir%\.dart_tool\package_config.json" %FLUTTER_TOOL_ARGS% "%snapshot_path%" %* & exit /B !ERRORLEVEL!
  • 1

{flutter sdk path}\bin\cache\dart-sdk\bin\dart.exe {flutter sdk path}\bin\cache\flutter_tools.snapshot,而这里的\flutter_tools.snapshot本质上是dart编译的中间产物????入口函数为{flutter sdk path}\packages\flutter_tools\bin\flutter_tools.dart中的main函数

2.一步一步跟踪dart文件到

{flutter sdk path}\packages\flutter_tools\lib\src\android\gradle.dart

Future<void> buildGradleApp({
    required FlutterProject project,
    required AndroidBuildInfo androidBuildInfo,
    required String target,
    required bool isBuildingBundle,
    required List<GradleHandledError> localGradleErrors,
    bool validateDeferredComponents = true,
    bool deferredComponentsEnabled = false,
    int retry = 0,
    @visibleForTesting int? maxRetries,
  }) async {
    。。。
  final String assembleTask = isBuildingBundle
        ? getBundleTaskFor(buildInfo)
        : getAssembleTaskFor(buildInfo);
  。。。
    。。。
  final List<String> command = <String>[
      _gradleUtils.getExecutable(project),//这里windows环境下就是gradlew.bat批处理程序
    ];
    if (_logger.isVerbose) {
      command.add('--full-stacktrace');
      command.add('--info');
      command.add('-Pverbose=true');
    } else {
      command.add('-q');
    }
    。。。
      。。。
  command.add(assembleTask);
    。。。
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

{flutter sdk path}\packages\flutter_tools\lib\src\android\gradle_utils.dart

/// Gets the Gradle executable path and prepares the Gradle project.
  /// This is the `gradlew` or `gradlew.bat` script in the `android/` directory.
  String getExecutable(FlutterProject project) {
    final Directory androidDir = project.android.hostAppGradleRoot;
    injectGradleWrapperIfNeeded(androidDir);

    final File gradle = androidDir.childFile(
      _platform.isWindows ? 'gradlew.bat' : 'gradlew',
    );
    if (gradle.existsSync()) {
      _logger.printTrace('Using gradle from ${gradle.absolute.path}.');
      // If the Gradle executable doesn't have execute permission,
      // then attempt to set it.
      _operatingSystemUtils.makeExecutable(gradle);
      return gradle.absolute.path;
    }
    throwToolExit(
      'Unable to locate gradlew script. Please check that ${gradle.path} '
      'exists or that ${gradle.dirname} can be read.'
    );
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

可以看到其实是执行gradlew.bat … assemblexxx
这里的assemblexxx就是android gradle定义的编译task

3.来到flutter自定义gradle插件里寻找端倪

在{flutter sdk path}\packages\flutter_tools\gradle\flutter.gradle
定义函数addFlutterTasks,创建了FlutterTask自定义gradle task,具体这个task怎么跟android gradle编译task关联起来,需要重点研究下???

private void addFlutterTasks(Project project) {
。。。
FlutterTask compileTask = project.tasks.create(name: taskName, type: FlutterTask) {
                flutterRoot this.flutterRoot
                flutterExecutable this.flutterExecutable
                buildMode variantBuildMode
                localEngine this.localEngine
                localEngineSrcPath this.localEngineSrcPath
                targetPath getFlutterTarget()
                verbose isVerbose()
                fastStart isFastStart()
                fileSystemRoots fileSystemRootsValue
                fileSystemScheme fileSystemSchemeValue
                trackWidgetCreation trackWidgetCreationValue
                targetPlatformValues = targetPlatforms
                sourceDir getFlutterSourceDirectory()
                intermediateDir project.file("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter/${variant.name}/")
                extraFrontEndOptions extraFrontEndOptionsValue
                extraGenSnapshotOptions extraGenSnapshotOptionsValue
                splitDebugInfo splitDebugInfoValue
                treeShakeIcons treeShakeIconsOptionsValue
                dartObfuscation dartObfuscationValue
                dartDefines dartDefinesValue
                bundleSkSLPath bundleSkSLPathValue
                performanceMeasurementFile performanceMeasurementFileValue
                codeSizeDirectory codeSizeDirectoryValue
                deferredComponents deferredComponentsValue
                validateDeferredComponents validateDeferredComponentsValue
                doLast {
                    project.exec {
                        if (Os.isFamily(Os.FAMILY_WINDOWS)) {
                            commandLine('cmd', '/c', "attrib -r ${assetsDirectory}/* /s")
                        } else {
                            commandLine('chmod', '-R', 'u+w', assetsDirectory)
                        }
                    }
                }
            }
            。。。
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

FlutterTask继承自BaseFlutterTask,BaseFlutterTask有函数buildBundle()

void buildBundle() {
        if (!sourceDir.isDirectory()) {
            throw new GradleException("Invalid Flutter source directory: ${sourceDir}")
        }

        intermediateDir.mkdirs()

        // Compute the rule name for flutter assemble. To speed up builds that contain
        // multiple ABIs, the target name is used to communicate which ones are required
        // rather than the TargetPlatform. This allows multiple builds to share the same
        // cache.
        String[] ruleNames;
        if (buildMode == "debug") {
            ruleNames = ["debug_android_application"]
        } else if (deferredComponents) {
            ruleNames = targetPlatformValues.collect { "android_aot_deferred_components_bundle_${buildMode}_$it" }
        } else {
            ruleNames = targetPlatformValues.collect { "android_aot_bundle_${buildMode}_$it" }
        }
        project.exec {
            logging.captureStandardError LogLevel.ERROR
            executable flutterExecutable.absolutePath
            workingDir sourceDir
            if (localEngine != null) {
                args "--local-engine", localEngine
                args "--local-engine-src-path", localEngineSrcPath
            }
            if (verbose) {
                args "--verbose"
            } else {
                args "--quiet"
            }
            args "assemble"
            args "--no-version-check"
            args "--depfile", "${intermediateDir}/flutter_build.d"
            args "--output", "${intermediateDir}"
            if (performanceMeasurementFile != null) {
                args "--performance-measurement-file=${performanceMeasurementFile}"
            }
            if (!fastStart || buildMode != "debug") {
                args "-dTargetFile=${targetPath}"
            } else {
                args "-dTargetFile=${Paths.get(flutterRoot.absolutePath, "examples", "splash", "lib", "main.dart")}"
            }
            args "-dTargetPlatform=android"
            args "-dBuildMode=${buildMode}"
            if (trackWidgetCreation != null) {
                args "-dTrackWidgetCreation=${trackWidgetCreation}"
            }
            if (splitDebugInfo != null) {
                args "-dSplitDebugInfo=${splitDebugInfo}"
            }
            if (treeShakeIcons == true) {
                args "-dTreeShakeIcons=true"
            }
            if (dartObfuscation == true) {
                args "-dDartObfuscation=true"
            }
            if (dartDefines != null) {
                args "--DartDefines=${dartDefines}"
            }
            if (bundleSkSLPath != null) {
                args "-dBundleSkSLPath=${bundleSkSLPath}"
            }
            if (codeSizeDirectory != null) {
                args "-dCodeSizeDirectory=${codeSizeDirectory}"
            }
            if (extraGenSnapshotOptions != null) {
                args "--ExtraGenSnapshotOptions=${extraGenSnapshotOptions}"
            }
            if (extraFrontEndOptions != null) {
                args "--ExtraFrontEndOptions=${extraFrontEndOptions}"
            }
            args ruleNames
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76

又回到flutter sdk中的flutter.bat脚本执行flutter assemble

4.又回到flutter sdk中的flutter.bat脚本执行flutter assemble

此时原本以为循环执行gradle assemblexxx,其实不是
因为flutter assemble执行后走到的是dart程序

{flutter sdk path}\packages\flutter_tools\lib\src\commands\assemble.dart

需要具体分析下这里怎么执行flutter中dart程序编译的?怎么生成flutter_assets的?以及debug模式下三个文件kernel_blob.bin、vm_snapshot_data、isolate_snapshot_data有什么作用以及如何生成到flutter_assets目录下的?

这里还有两个很重要的文件
{flutter sdk path}\bin\cache\artifacts\engine\windows-x64\frontend_server.dart.snapshot
{flutter sdk path}\bin\cache\artifacts\engine\android-arm-release\windows-x64\gen_snapshot.exe

一个是dart编译的中间产物,需要配合dart vm使用,负责生成kernel文件?
一个纯二级制可执行文件,负责生成aot可执行文件?

而这个frontend_server.dart.snapshot不同于之前的flutter_tool.snapshot,他是存在于engine的Dart代码中的,而不是Flutter SDK中。

engine的代码需要单独下载:https://github.com/flutter/engine
  • 1
  • 2
  • 3

总结

在这里插入图片描述

flutter tools(dart)源码调试

{flutter sdk}\packages\flutter_tools整个目录作为dart工程根目录打开(如果出现as不识别工程,查工程有没有指定dart sdk路径,file->settings->dart,指定dart sdk路径为{flutter sdk}\bin\cache\dart-sdk并apply)
在这里插入图片描述
有两种方式:
方式1:Dart Command Line App
方式2:Dart Remote Debug

方式1:Dart Command Line App

在这里插入图片描述
在这里插入图片描述
指定dart入口文件,命令行参数为build apk,工作目录选择任意flutter工程根目录(会在指定的目录下执行dart命令,此处是执行flutter编译android apk)

方式2:Dart Remote Debug

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
这里可以看到要求对应的dart vm启动时必须指定 --enable-vm-service:12345 --pause_isolates_on_start 两个选项,其中12345随意指定,后续要用到

使用刚刚提供的vm选项启动dart vm执行flutter编译任务

1.首先,修改flutter.bat文件

“%dart%” --packages=“%flutter_tools_dir%.dart_tool\package_config.json” %FLUTTER_TOOL_ARGS% “%snapshot_path%” %* & “%exit_with_errorlevel%”

之前赋值变量FLUTTER_TOOL_ARGS

SET FLUTTER_TOOL_ARGS=–enable-vm-service:12345 --pause_isolates_on_start --disable-service-auth-codes %FLUTTER_TOOL_ARGS%

2.然后直接去任意flutter项目根目录下执行flutter build apk

比如

G:\MyWork\Flutter\Tools\flutter_windows_3.7.3-stable\flutter\bin\flutter.bat --no-color build apk

3.遇到问题:Dart Remote Debug无法断言跟踪到flutter_tools.dart源码文件

但是如果是Dart Command Line App

G:/MyWork/Flutter/Tools/flutter_windows_3.7.3-stable/flutter/bin/cache/dart-sdk/bin/dart.exe --enable-asserts --pause_isolates_on_start --enable-vm-service:63036 G:\MyWork\Flutter\Tools\flutter_windows_3.7.3-stable\flutter\packages\flutter_tools\bin\flutter_tools.dart build apk

则正常断言到flutter_tools.dart源码main函数
在这里插入图片描述

对比flutter.bat批处理执行的命令

“G:\MyWork\Flutter\Tools\flutter_windows_3.7.3-stable\flutter\bin\cache\dart-sdk\bin\dart.exe” --packages=“G:\MyWork\Flutter\Tools\flutter_windows_3.7.3-stable\flutter\packages\flutter_tools.dart_tool\package_config.json” --enable-vm-service:12345 --pause_isolates_on_start --disable-service-auth-codes “G:\MyWork\Flutter\Tools\flutter_windows_3.7.3-stable\flutter\bin\cache\flutter_tools.snapshot” --no-color build apk

对比发现可能是启动dart vm时候没有指定选项–enable-asserts导致

修改flutter.bat中的dart vm启动选项,新增–enable-asserts

报错snapshot文件不支持assert

修改flutter.bat中的dart vm启动入口,把snapshot文件换成flutter_tools.dart文件(有效!!!)

SET snapshot_path=%cache_dir%\flutter_tools.snapshot

改为

SET snapshot_path=%flutter_tools_dir%\bin\flutter_tools.dart

重新执行步骤2 flutter build apk
此时可以正常断言到flutter_tools.dart文件的main函数
在这里插入图片描述

参考

https://developer.aliyun.com/article/761239
https://juejin.cn/post/7093388612078665764

注意:以上两个博文都是使用的其他版本flutter sdk,所以跟本次分享的内容有点差异,如assemble.dart在参考博文中的flutter sdk版本是不存在的

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/412338
推荐阅读
相关标签
  

闽ICP备14008679号