2023-02-24 flutter热重载和热重启

2023-02-23  本文已影响0人  我是小胡胡123

1、_commonTerminalInputHandler

热重载和热重启部分相同的。都是执行residentRunner.restart();
热重载:
final OperationResult result = await residentRunner.restart();
热重启:
final OperationResult result = await residentRunner.restart(fullRestart: true);

 case 'r':
        if (!residentRunner.canHotReload) {
          return false;
        }
        final OperationResult result = await residentRunner.restart();
        if (result.fatal) {
          throwToolExit(result.message);
        }
        if (!result.isOk) {
          _logger.printStatus('Try again after fixing the above error(s).', emphasis: true);
        }
        return true;
      case 'R':
        // If hot restart is not supported for all devices, ignore the command.
        if (!residentRunner.supportsRestart || !residentRunner.hotMode) {
          return false;
        }
        final OperationResult result = await residentRunner.restart(fullRestart: true);
        if (result.fatal) {
          throwToolExit(result.message);
        }
        if (!result.isOk) {
          _logger.printStatus('Try again after fixing the above error(s).', emphasis: true);
        }
        return true;

2、residentRunner.restart();

 Future<OperationResult> restart({
    bool fullRestart = false,
    String reason,
    bool silent = false,
    bool pause = false,
  }) async {
    if (flutterDevices.any((FlutterDevice device) => device.devFS == null)) {
      return OperationResult(1, 'Device initialization has not completed.');
    }
    await _calculateTargetPlatform();
    final Stopwatch timer = Stopwatch()..start();

    // Run source generation if needed.
    await runSourceGenerators();

    if (fullRestart) {
      final OperationResult result = await _fullRestartHelper(
        targetPlatform: _targetPlatform,
        sdkName: _sdkName,
        emulator: _emulator,
        reason: reason,
        silent: silent,
      );
      if (!silent) {
        globals.printStatus('Restarted application in ${getElapsedAsMilliseconds(timer.elapsed)}.');
      }
      unawaited(residentDevtoolsHandler.hotRestart(flutterDevices));
      return result;
    }
    final OperationResult result = await _hotReloadHelper(
      targetPlatform: _targetPlatform,
      sdkName: _sdkName,
      emulator: _emulator,
      reason: reason,
      pause: pause,
    );
    if (result.isOk) {
      final String elapsed = getElapsedAsMilliseconds(timer.elapsed);
      if (!silent) {
        globals.printStatus('${result.message} in $elapsed.');
      }
    }
    return result;
  }

这段代码实现了一个 restart 方法,接收几个可选参数:fullRestart,reason,silent 和 pause,返回一个 Future<OperationResult> 对象。

最后,返回操作结果。

热重启和热重载,都会经过重新生成源代码的过程。
_calculateTargetPlatform -》runSourceGenerators

_fullRestartHelper 执行热重启,_hotReloadHelper 执行热重载。

3、runSourceGenerators

 Future<void> runSourceGenerators() async {
    _environment ??= Environment(
      artifacts: globals.artifacts,
      logger: globals.logger,
      cacheDir: globals.cache.getRoot(),
      engineVersion: globals.flutterVersion.engineRevision,
      fileSystem: globals.fs,
      flutterRootDir: globals.fs.directory(Cache.flutterRoot),
      outputDir: globals.fs.directory(getBuildDirectory()),
      processManager: globals.processManager,
      platform: globals.platform,
      projectDir: globals.fs.currentDirectory,
      generateDartPluginRegistry: generateDartPluginRegistry,
      defines: <String, String>{
        // Needed for Dart plugin registry generation.
        kTargetFile: mainPath,
      },
    );

    final CompositeTarget compositeTarget = CompositeTarget(<Target>[
      const GenerateLocalizationsTarget(),
      const DartPluginRegistrantTarget(),
    ]);

    _lastBuild = await globals.buildSystem.buildIncremental(
      compositeTarget,
      _environment,
      _lastBuild,
    );
    if (!_lastBuild.success) {
      for (final ExceptionMeasurement exceptionMeasurement in _lastBuild.exceptions.values) {
        globals.printError(
          exceptionMeasurement.exception.toString(),
          stackTrace: globals.logger.isVerbose
            ? exceptionMeasurement.stackTrace
            : null,
        );
      }
    }
    globals.printTrace('complete');
  }

这段代码实现了一个 runSourceGenerators() 方法,它的作用是运行Flutter应用程序的源代码生成器来生成本地化和Dart插件注册表。

4、 buildIncremental

Future<BuildResult> buildIncremental(
    Target target,
    Environment environment,
    BuildResult? previousBuild,
  ) async {
    environment.buildDir.createSync(recursive: true);
    environment.outputDir.createSync(recursive: true);

    FileStore? fileCache;
    if (previousBuild == null || _incrementalFileStore[previousBuild] == null) {
      final File cacheFile = environment.buildDir.childFile(FileStore.kFileCache);
      fileCache = FileStore(
        cacheFile: cacheFile,
        logger: _logger,
        strategy: FileStoreStrategy.timestamp,
      )..initialize();
    } else {
      fileCache = _incrementalFileStore[previousBuild];
    }
    final Node node = target._toNode(environment);
    final _BuildInstance buildInstance = _BuildInstance(
      environment: environment,
      fileCache: fileCache!,
      buildSystemConfig: const BuildSystemConfig(),
      logger: _logger,
      fileSystem: _fileSystem,
      platform: _platform,
    );
    bool passed = true;
    try {
      passed = await buildInstance.invokeTarget(node);
    } finally {
      fileCache.persistIncremental();
    }
    final BuildResult result = BuildResult(
      success: passed,
      exceptions: buildInstance.exceptionMeasurements,
      performance: buildInstance.stepTimings,
    );
    _incrementalFileStore[result] = fileCache;
    return result;
  }

这段代码实现了一个异步函数 buildIncremental,用于增量构建指定目标(Target)的代码,并在给定环境(Environment)下执行构建过程。它接受三个参数:目标、环境和先前的构建结果(可选)。它返回一个 Future<BuildResult>,其中包含构建结果。

函数的执行流程如下:

因此,这段代码实现了一个增量构建过程,其中缓存机制可以提高构建性能,只有在必要时才会重新构建文件。这可以节省时间和资源,特别是在大型项目中。

5、 _fullRestartHelper

Future<OperationResult> _fullRestartHelper({
    String targetPlatform,
    String sdkName,
    bool emulator,
    String reason,
    bool silent,
  }) async {
    if (!supportsRestart) {
      return OperationResult(1, 'hotRestart not supported');
    }
    Status status;
    if (!silent) {
      status = globals.logger.startProgress(
        'Performing hot restart...',
        progressId: 'hot.restart',
      );
    }
    OperationResult result;
    String restartEvent;
    try {
      final Stopwatch restartTimer = _stopwatchFactory.createStopwatch('fullRestartHelper')..start();
      if (!(await hotRunnerConfig.setupHotRestart())) {
        return OperationResult(1, 'setupHotRestart failed');
      }
      result = await _restartFromSources(reason: reason);
      restartTimer.stop();
      if (!result.isOk) {
        restartEvent = 'restart-failed';
      } else {
        HotEvent('restart',
          targetPlatform: targetPlatform,
          sdkName: sdkName,
          emulator: emulator,
          fullRestart: true,
          reason: reason,
          fastReassemble: false,
          overallTimeInMs: restartTimer.elapsed.inMilliseconds,
          syncedBytes: result.updateFSReport?.syncedBytes,
          invalidatedSourcesCount: result.updateFSReport?.invalidatedSourcesCount,
          transferTimeInMs: result.updateFSReport?.transferDuration?.inMilliseconds,
          compileTimeInMs: result.updateFSReport?.compileDuration?.inMilliseconds,
          findInvalidatedTimeInMs: result.updateFSReport?.findInvalidatedDuration?.inMilliseconds,
          scannedSourcesCount: result.updateFSReport?.scannedSourcesCount,
        ).send();
      }
    } on vm_service.SentinelException catch (err, st) {
      restartEvent = 'exception';
      return OperationResult(1, 'hot restart failed to complete: $err\n$st', fatal: true);
    } on vm_service.RPCError  catch (err, st) {
      restartEvent = 'exception';
      return OperationResult(1, 'hot restart failed to complete: $err\n$st', fatal: true);
    } finally {
      // The `restartEvent` variable will be null if restart succeeded. We will
      // only handle the case when it failed here.
      if (restartEvent != null) {
        HotEvent(restartEvent,
          targetPlatform: targetPlatform,
          sdkName: sdkName,
          emulator: emulator,
          fullRestart: true,
          reason: reason,
          fastReassemble: false,
        ).send();
      }
      status?.cancel();
    }
    return result;
  }

  

6、 await _restartFromSources(reason: reason);

  Future<OperationResult> _restartFromSources({
    String reason,
  }) async {
    final Stopwatch restartTimer = Stopwatch()..start();
    UpdateFSReport updatedDevFS;
    try {
      updatedDevFS = await _updateDevFS(fullRestart: true);
    } finally {
      hotRunnerConfig.updateDevFSComplete();
    }
    if (!updatedDevFS.success) {
      for (final FlutterDevice device in flutterDevices) {
        if (device.generator != null) {
          await device.generator.reject();
        }
      }
      return OperationResult(1, 'DevFS synchronization failed');
    }
    _resetDirtyAssets();
    for (final FlutterDevice device in flutterDevices) {
      // VM must have accepted the kernel binary, there will be no reload
      // report, so we let incremental compiler know that source code was accepted.
      if (device.generator != null) {
        device.generator.accept();
      }
    }
    // Check if the isolate is paused and resume it.
    final List<Future<void>> operations = <Future<void>>[];
    for (final FlutterDevice device in flutterDevices) {
      final Set<String> uiIsolatesIds = <String>{};
      final List<FlutterView> views = await device.vmService.getFlutterViews();
      for (final FlutterView view in views) {
        if (view.uiIsolate == null) {
          continue;
        }
        uiIsolatesIds.add(view.uiIsolate.id);
        // Reload the isolate.
        final Future<vm_service.Isolate> reloadIsolate = device.vmService
          .getIsolateOrNull(view.uiIsolate.id);
        operations.add(reloadIsolate.then((vm_service.Isolate isolate) async {
          if ((isolate != null) && isPauseEvent(isolate.pauseEvent.kind)) {
            // The embedder requires that the isolate is unpaused, because the
            // runInView method requires interaction with dart engine APIs that
            // are not thread-safe, and thus must be run on the same thread that
            // would be blocked by the pause. Simply un-pausing is not sufficient,
            // because this does not prevent the isolate from immediately hitting
            // a breakpoint (for example if the breakpoint was placed in a loop
            // or in a frequently called method) or an exception. Instead, all
            // breakpoints are first disabled and exception pause mode set to
            // None, and then the isolate resumed.
            // These settings to not need restoring as Hot Restart results in
            // new isolates, which will be configured by the editor as they are
            // started.
            final List<Future<void>> breakpointAndExceptionRemoval = <Future<void>>[
              device.vmService.service.setIsolatePauseMode(isolate.id,
                exceptionPauseMode: vm_service.ExceptionPauseMode.kNone),
              for (final vm_service.Breakpoint breakpoint in isolate.breakpoints)
                device.vmService.service.removeBreakpoint(isolate.id, breakpoint.id)
            ];
            await Future.wait(breakpointAndExceptionRemoval);
            await device.vmService.service.resume(view.uiIsolate.id);
          }
        }));
      }

      // The engine handles killing and recreating isolates that it has spawned
      // ("uiIsolates"). The isolates that were spawned from these uiIsolates
      // will not be restarted, and so they must be manually killed.
      final vm_service.VM vm = await device.vmService.service.getVM();
      for (final vm_service.IsolateRef isolateRef in vm.isolates) {
        if (uiIsolatesIds.contains(isolateRef.id)) {
          continue;
        }
        operations.add(device.vmService.service.kill(isolateRef.id)
          .catchError((dynamic error, StackTrace stackTrace) {
            // Do nothing on a SentinelException since it means the isolate
            // has already been killed.
            // Error code 105 indicates the isolate is not yet runnable, and might
            // be triggered if the tool is attempting to kill the asset parsing
            // isolate before it has finished starting up.
          }, test: (dynamic error) => error is vm_service.SentinelException
            || (error is vm_service.RPCError && error.code == 105)));
      }
    }
    await Future.wait(operations);

    await _launchFromDevFS();
    restartTimer.stop();
    globals.printTrace('Hot restart performed in ${getElapsedAsMilliseconds(restartTimer.elapsed)}.');
    _addBenchmarkData('hotRestartMillisecondsToFrame',
        restartTimer.elapsed.inMilliseconds);

    // Send timing analytics.
    globals.flutterUsage.sendTiming('hot', 'restart', restartTimer.elapsed);

    // Toggle the main dill name after successfully uploading.
    _swap =! _swap;

    return OperationResult(
      OperationResult.ok.code,
      OperationResult.ok.message,
      updateFSReport: updatedDevFS,
    );
  }

在这里发现一个目录:
/private/var/folders/4j/z1n6phgx4d11klg60p7d3b240000gn/T/flutter_tools.COlwZv/flutter_tool.Kr5rSU/app.dill

编译过程:


image.png

编译同步过程:


image.png

VmService call方法:

  Future<T> _call<T>(String method, [Map args = const {}]) async {
    print('--->method:$method');
    final request = _OutstandingRequest(method);
    _outstandingRequests[request.id] = request;
    Map m = {
      'jsonrpc': '2.0',
      'id': request.id,
      'method': method,
      'params': args,
    };
    String message = jsonEncode(m);
    _onSend.add(message);
    _writeMessage(message);
    return await request.future as T;
  }

在这个方法添加上日志,比较hot reload 和hot restart 区别:

从上面调试过程中的,可以看出下面转载的内容没有什么出入。

下面内容从https://juejin.cn/post/7041728779038752799转载:

热重载的步骤:

不支持热重载的场景

Flutter 的热重载是基于 JIT 编译模式的代码增量同步。由于 JIT 属于动态编译,能够将 Dart 代码编译成生成中间代码,让 Dart VM 在运行时解释执行,因此可以通过动态更新中间代码实现增量同步。

上一篇 下一篇

猜你喜欢

热点阅读