面试题答案
一键面试框架层和引擎层的数据交互方式
在Flutter架构中,框架层和引擎层通过消息通道(Message Channel)进行数据交互。框架层使用Dart代码,而引擎层主要由C++实现。消息通道允许Dart代码和C++代码相互调用,实现数据的传递和功能的交互。
常用数据传递场景及实现方式
- 平台调用(Platform Call):框架层调用引擎层提供的平台功能。
- 场景:例如获取设备电量信息,这是依赖于原生系统的功能,需要通过引擎层来实现。
- 实现方式:
- Dart端:使用
MethodChannel
类。首先创建一个MethodChannel
实例,指定通道名称。然后调用invokeMethod
方法来调用引擎层暴露的方法,并传递参数。例如:
- Dart端:使用
import 'package:flutter/services.dart';
class Battery {
static const MethodChannel _channel =
const MethodChannel('samples.flutter.dev/battery');
static Future<int> getBatteryLevel() async {
try {
final int result = await _channel.invokeMethod('getBatteryLevel');
return result;
} on PlatformException catch (e) {
print("Failed to get battery level: '${e.message}'");
return -1;
}
}
}
- **C++端(引擎层)**:在C++中注册与Dart端通道名称一致的方法,并实现具体逻辑。在Flutter引擎中,通过`flutter::MethodChannel`来处理这些调用。例如:
#include "flutter/method_channel.h"
#include "flutter/plugin_registrar.h"
#include "flutter/standard_method_codec.h"
#include <jni.h>
#include <memory>
namespace battery {
class BatteryPlugin : public flutter::Plugin {
public:
static void RegisterWithRegistrar(flutter::PluginRegistrar* registrar) {
auto channel =
std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
registrar->messenger(), "samples.flutter.dev/battery",
&flutter::StandardMethodCodec::GetInstance());
auto plugin = std::make_unique<BatteryPlugin>();
channel->SetMethodCallHandler(
[plugin_pointer = plugin.get()](const auto& call, auto result) {
plugin_pointer->HandleMethodCall(call, std::move(result));
});
registrar->AddPlugin(std::move(plugin));
}
void HandleMethodCall(const flutter::MethodCall<flutter::EncodableValue>& call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
if (call.method_name().compare("getBatteryLevel") == 0) {
// 这里获取实际的电量信息,示例中简单返回一个固定值
int batteryLevel = 80;
result->Success(flutter::EncodableValue(batteryLevel));
} else {
result->NotImplemented();
}
}
};
} // namespace battery
void RegisterBatteryPlugin(flutter::PluginRegistrar* registrar) {
battery::BatteryPlugin::RegisterWithRegistrar(registrar);
}
- 事件流(Event Stream):引擎层向框架层推送实时数据。
- 场景:如传感器数据(加速度计、陀螺仪等)实时更新,引擎层获取到数据后需要及时传递给框架层。
- 实现方式:
- Dart端:使用
EventChannel
类。创建EventChannel
实例并监听事件。例如监听设备方向变化事件:
- Dart端:使用
import 'package:flutter/services.dart';
class OrientationStream {
static const EventChannel _channel =
const EventChannel('samples.flutter.dev/orientation');
static Stream<dynamic> get orientationStream {
return _channel.receiveBroadcastStream();
}
}
然后在需要的地方订阅这个流:
OrientationStream.orientationStream.listen((event) {
print('Device orientation changed: $event');
});
- **C++端(引擎层)**:在C++中创建与Dart端对应的`EventChannel`,并在数据更新时发送事件。例如:
#include "flutter/event_channel.h"
#include "flutter/plugin_registrar.h"
#include "flutter/standard_method_codec.h"
#include <jni.h>
#include <memory>
namespace orientation {
class OrientationPlugin : public flutter::Plugin {
public:
static void RegisterWithRegistrar(flutter::PluginRegistrar* registrar) {
auto channel =
std::make_unique<flutter::EventChannel<flutter::EncodableValue>>(
registrar->messenger(), "samples.flutter.dev/orientation",
&flutter::StandardMethodCodec::GetInstance());
auto plugin = std::make_unique<OrientationPlugin>();
channel->SetStreamHandler(plugin.get());
registrar->AddPlugin(std::move(plugin));
}
virtual ~OrientationPlugin() = default;
flutter::EventSink event_sink_;
flutter::PluginRegistrar* registrar_;
// 模拟数据更新,这里简单发送一个固定值
void SendOrientationUpdate() {
if (event_sink_) {
event_sink_(flutter::EncodableValue("landscape"));
}
}
// 处理流的订阅和取消订阅
flutter::ErrorOr<flutter::EventSink> OnListenInternal(
const flutter::EncodableValue* arguments,
std::unique_ptr<flutter::EventSink> events) override {
event_sink_ = std::move(*events);
// 这里可以开始监听实际的传感器数据更新,并调用SendOrientationUpdate方法发送数据
return event_sink_;
}
flutter::ErrorOr<void> OnCancelInternal(
const flutter::EncodableValue* arguments) override {
event_sink_ = nullptr;
return {};
}
};
} // namespace orientation
void RegisterOrientationPlugin(flutter::PluginRegistrar* registrar) {
orientation::OrientationPlugin::RegisterWithRegistrar(registrar);
}