赞
踩
点击“开发者技术前线”,选择“星标?”
13:21 在看|星标|留言, 真爱
来自:腾讯在线教育腾讯教育从去年开始接入Flutter,今年上半年重构腾讯课堂和企鹅辅导iPad端,80%代码都采用Flutter实现,对于教育最重要的点播功能同样也需要迁移到Flutter上进行渲染。
目前正在研究的实现渲染的方案主要有2种形式PlatformView和Texture Widget。下面文章就先大概讲述一下Flutter的渲染框架原理和实现,然后会对这两种方案进行对比分析。
Flutter的框架主要包括Framework和Engine两层,应用是基于Framework层开发的,Framework负责渲染中的Build,Layout,Paint,生成Layer等。Engine层是C++实现的渲染引擎,负责把Framework生成的Layer组合,生成纹理,然后通过OpenGL接口向GPU提交渲染数据。
Flutter:Framework的最底层,提供工具类和方法 Painting :封装了Flutter Engine提供的绘制接口,主要是为了在绘制控件等固定样式的图形时提供更直观、更方便的接口 Animation :动画相关的类 Gesture :提供了手势识别相关的功能,包括触摸事件类定义和多种内置的手势识别器 Rendering:渲染库,Flutter的控件树在实际显示时会转换成对应的渲染对象(RenderObject)树来实现布局和绘制操作
当GPU发出Vsync信号时,会执行Dart代码绘制新UI,Dart会被执行为Layer Tree,然后经过Compositor合成后交由Skia引擎渲染处理为GPU数据,最后通过GL/Vulkan发给GPU,具体流程如下:
当需要更新UI的时候,Framework通知Engine,Engine会等到下个Vsync信号到达的时候通知Framework,Framework进行animations,build,layout,compositing,paint,最后生成layer提交给Engine。Engine再把layer进行组合,生成纹理,最后通过OpenGL接口提交数据给GPU,具体流程如下:
接下来分别分析一下两个方案的各自的特点以及使用的方式。
PlatformView是Flutter官方在1.0版本推出的组件,以解决开发者想在Flutter中嵌入Android和iOS平台原生View的Widget。例如想嵌入地图、视频播放器等原生组件,对于想尝试Flutter,但是又想低成本的迁移复杂组件的团队,可以尝试PlatformView,在 Dart 中的类对应到 iOS 和 Android 平台分别是UIKitView和AndroidView。
那么PlatformView在点播功能中应该怎么实现,如下图所示:
其中的ARMPlatformView代表业务View。
关联类的作用是Native和Dart侧的桥梁,其中id需要和Native获取对应。
- class VodPlayerController {
- VodPlayerController._(int id)
- : _channel = MethodChannel('ARMFlutterVodPlayerView_$id');
- final MethodChannel _channel;
- Future<void> play(String url) async {
- return _channel.invokeMethod('play', url);
- }
- Future<void> stop() async {
- return _channel.invokeMethod('stop');
- }
- }
typedef void VodPlayerViewWidgetCreatedCallback(VodPlayerController controller);
- class VodVideoWidget extends StatefulWidget {
- final VodPlayerViewWidgetCreatedCallback callback;
- final x;
- final y;
- final width;
- final height;
- VodVideoWidget({
- Key key,
- @required this.callback,
- @required this.x,
- @required this.y,
- @required this.width,
- @required this.height,
- });
- @override
- _VodVideoWidgetState createState() => _VodVideoWidgetState();
- }
- class _VodVideoWidgetState extends State<VodVideoWidget> {
- @override
- Widget build(BuildContext context) {
- return UiKitView(
- viewType: 'ARMFlutterVodPlayerView',
- onPlatformViewCreated: _onPlatformViewCreated,
- creationParams: <String,dynamic>{
- 'x': widget.x,
- 'y': widget.y,
- 'width': widget.width,
- 'height': widget.height,
- },
- creationParamsCodec: new StandardMessageCodec(),
- );
- }
- void _onPlatformViewCreated(int id){
- if(widget.callback == null) {
- return;
- }
- widget.callback(VodPlayerController._(id));
- }
- }
- @implementation ARMFlutterVodPlugin
- + (void)registerWithRegistrar:(nonnull NSObject<FlutterPluginRegistrar> *)registrar {
- ARMFlutterVodPlayerFactory* vodFactory =
- [[ARMFlutterVodPlayerFactory alloc] initWithMessenger:registrar.messenger];
- [registrar registerViewFactory:vodFactory withId:@"ARMFlutterVodPlayerView"];
- }
- @end
- + (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
- [ARMFlutterVodPlugin registerWithRegistrar:[registry registrarForPlugin:@"ARMFlutterVodPlugin"]];
- }
- @implementation ARMFlutterVodPlayerFactory
- - (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger> *)messager {
- self = [super init];
- if (self) {
- _messenger = messager;
- }
- return self;
- }
- - (NSObject<FlutterMessageCodec> *)createArgsCodec {
- return [FlutterStandardMessageCodec sharedInstance];
- }
- - (nonnull NSObject<FlutterPlatformView> *)createWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args {
- return [[ARMFlutterVodPlayerView alloc] initWithWithFrame:frame viewIdentifier:viewId arguments:args binaryMessenger:self.messenger];
- }
- @end
- @implementation ARMFlutterVodPlayerView
- - (instancetype)initWithWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id)args binaryMessenger:(NSObject<FlutterBinaryMessenger> *)messenger{
- if (self = [super init]) {
- NSDictionary *dic = args;
- CGFloat x = [dic[@"x"] floatValue];
- CGFloat y = [dic[@"y"] floatValue];
- CGFloat width = [dic[@"width"] floatValue];
- CGFloat height = [dic[@"height"] floatValue];
- ARMFlutterVodManager.shareInstance.mainView.frame = CGRectMake(x, y, width, height);
- NSString* channelName = [NSString stringWithFormat:@"ARMFlutterVodPlayerView_%lld", viewId];
- _channel = [FlutterMethodChannel methodChannelWithName:channelName binaryMessenger:messenger];
- __weak __typeof__(self) weakSelf = self;
- [_channel setMethodCallHandler:^(FlutterMethodCall * call, FlutterResult result) {
- [weakSelf onMethodCall:call result:result];
- }];
- }
- return self;
- }
- - (nonnull UIView *)view {
- return ARMFlutterVodManager.shareInstance.mainView;
- }
- - (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result{
- if ([[call method] isEqualToString:@"play"]) {
- NSString *url = [call arguments];
- [ARMFlutterVodManager.shareInstance play:url];
- } else {
- result(FlutterMethodNotImplemented);
- }
- }
- @end
textures = [self.registrar textures];
3.获得textureId
self.textureId = [textures registerTexture:self];
4.重写
- (CVPixelBufferRef _Nullable)copyPixelBuffer
返回CVPixelBufferRef 5.通知Texture获取CVPixelBufferRef
- - (void)onDisplayLink {
- [textures textureFrameAvailable:self.textureId];
- }
- MethodChannel _globalChannel = MethodChannel("ARMFlutterTextureVodPlayer");
- class _ARMPlugin {
- MethodChannel get channel => MethodChannel("ARMFlutterTextureVodPlayer/$textureId");
- int textureId;
- _ARMPlugin(this.textureId);
- Future<void> play() async {
- await channel.invokeMethod("play");
- }
- Future<void> pause() async {
- await channel.invokeMethod("pause");
- }
- Future<void> stop() async {
- await channel.invokeMethod("stop");
- }
- Future<void> setNetworkDataSource(
- {String uri, Map<String, String> headers = const {}}) async {
- await channel.invokeMethod("setNetworkDataSource", <String, dynamic>{
- "uri": uri,
- "headers": headers,
- });
- }
- }
- @implementation ARMFlutterTextureVodPlugin
- - (instancetype)initWithRegistrar:(NSObject <FlutterPluginRegistrar> *)registrar {
- self = [super init];
- if (self) {
- self.registrar = registrar;
- }
- return self;
- }
- + (instancetype)pluginWithRegistrar:(NSObject <FlutterPluginRegistrar> *)registrar {
- return [[self alloc] initWithRegistrar:registrar];
- }
- + (void)registerWithRegistrar:(NSObject <FlutterPluginRegistrar> *)registrar {
- FlutterMethodChannel *channel = [FlutterMethodChannel
- methodChannelWithName:@"ARMFlutterTextureVodPlayer"
- binaryMessenger:[registrar messenger]];
- ARMFlutterTextureVodPlugin *instance = [ARMFlutterTextureVodPlugin pluginWithRegistrar:registrar];
- [registrar addMethodCallDelegate:instance channel:channel];
- }
- - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
- }
- @end
- + (instancetype)armWithRegistrar:(NSObject <FlutterPluginRegistrar> *)registrar {
- return [[self alloc] initWithRegistrar:registrar];
- }
- - (instancetype)initWithRegistrar:(NSObject <FlutterPluginRegistrar> *)registrar {
- if (self = [super init]) {
- self.registrar = registrar;
- textures = [self.registrar textures];
- self.textureId = [textures registerTexture:self];
- NSString *channelName = [NSString stringWithFormat:@"ARMFlutterTextureVodPlayer/%lli", self.textureId];
- channel = [FlutterMethodChannel methodChannelWithName:channelName binaryMessenger:[registrar messenger]];
- __weak typeof(&*self) weakSelf = self;
- [channel setMethodCallHandler:^(FlutterMethodCall *call, FlutterResult result) {
- [weakSelf handleMethodCall:call result:result];
- }];
- }
- return self;
- }
- - (CVPixelBufferRef _Nullable)copyPixelBuffer {
- CVPixelBufferRef newBuffer = [self.vodPlayer framePixelbuffer];
- if (newBuffer) {
- CFRetain(newBuffer);
- CVPixelBufferRef pixelBuffer = latestPixelBuffer;
- while (!OSAtomicCompareAndSwapPtrBarrier(pixelBuffer, newBuffer, (void **) &latestPixelBuffer)) {
- pixelBuffer = latestPixelBuffer;
- }
- return pixelBuffer;
- }
- return NULL;
- }
- displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(onDisplayLink)];
- displayLink.frameInterval = 1;
- [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
- - (void)onDisplayLink {
- [textures textureFrameAvailable:self.textureId];
- }
- EAGLContext *prevContext = [EAGLContext currentContext];
- [EAGLContext setCurrentContext:_context];
- _renderer = nil;
- glBindTexture(GL_TEXTURE_2D, 0);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- if (_framebuffer) {
- glDeleteFramebuffers(1, &_framebuffer);
- _framebuffer = 0;
- }
- if (_renderbuffer) {
- glDeleteRenderbuffers(1, &_renderbuffer);
- _renderbuffer = 0;
- }
- if (_program) {
- glDeleteProgram(_program);
- _program = 0;
- }
- _context = nil;
- [EAGLContext setCurrentContext:prevContext];
---END---
选择”开发者技术前线 “星标?,内容一触即达。点击原文更多惊喜!
开发者技术前线 汇集技术前线快讯和关注行业趋势,大厂干货,是开发者经历和成长的优秀指南。
历史推荐
点个在看,解锁更多惊喜!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。