Hummer 引擎优化系列 - PlatformView的演进历史与优化探索

# 前言

官方宣称,Flutter 3.0 是一个重大的版本更新,它带来了许多新功能和改进,其中最值得关注的是对 Flutter 的桌面稳定支持。但,这对 PlatformView 功能来说似乎是个例外:

  1. 截止Flutter 3.7.9,PlatformView仍然仅支持移动端,桌面端尚不可用;

  2. Android端新引入的TLHC模式,使得PlatformView的模式选择变得异常混乱和复杂;

  3. iOS的可变刷新率暴露了Hybrid Composition模式的缺陷;

本文首先会沿着PlatformView方案的演进历史,剖析各个方案的实现原理和优缺点,给出最佳实践,帮助读者理解和正确选择PlatformView的渲染模式。接着会介绍Hummer在FY23中所做的优化和探索。最后,会聊聊对PlatformView未来工作的规划。

# PlatformView演进历史

Flutter框架将开发人员描述的Widget树转换为内部层次结构,并决定实际渲染的像素。它控制纹理并直接渲染,而不使用原生视图层次结构。这意味着默认情况下,Flutter UI不会包含原生视图。这对于希望在Flutter应用程序中包含复杂原生视图的开发人员来说是一个问题。

为了解决这个问题,Flutter引入了PlatformView功能,以便开发者复用现有的原生视图,例如,地图或WebView。

但是,当前的Flutter架构是将整个Widget树都渲染到了单个纹理上,如何才能将原生视图嵌入到Flutter Widget树的内部层次结构中,并在它们之间交错呢?

下面,让我们一起探究 PlatformView 的演进历史,并深入分析各个方案的实现原理及优缺点。

## Android端

按照时间先后顺序,Android端实现了三种PlatformView模式:

  1. Virtual Display(VD)

  2. Hybrid Composition(HC)

  3. Texture Layer Hybrid Composition(TLHC)

每种后续模式都旨在解决前一种模式的缺陷。下面将详细介绍每种模式的实现原理和存在的问题。

### Virtual Display(VD)

Virtual Display是Android端PlatformView的最早实现方案,解决了开发者在Flutter应用中嵌入原生视图的强烈诉求。

#### 实现原理

图片

图片

图(1)

图(2)

如图(1)所示,Virtual Display模式是通过将原生视图渲染到Virtual Display中来实现的。Virtual Display类似于一个虚拟显示区域,它将虚拟显示区域的内容(原生视图)渲染到一个Surface上。然后,Flutter引擎可以通过相应的texture Id获取到原生视图的渲染数据,并将其与自己内部的Widget树的其余部分进行合成,最后作为Flutter在Android上更大纹理输出的一部分进行渲染。

#### 方案缺陷

该方案确实解决了将Android视图嵌入Flutter的诉求,但也存在一些严重的缺陷:

  1. 如图(2)所示,Android视图位于Virtual Display自己独立的View层次结构中,并且与包含Flutter UI的View层次结构完全隔离,View相关信息不能被正确获取。因此这种方法会带来难以解决的功能问题,例如,触摸事件、文本输入、辅助功能等;

  2. Virtual Display 上的 Window 绘制是在 Primary Display之后,输出会有1帧的延迟;

  3. 原生视图的每个像素都流经额外的中间图形缓冲区,对内存和性能有一定影响;

#### 使用方式

在TLHC模式上线前(Flutter3.0之前),开发者可以简单地通过返回AndroidView(Flutter框架会隐式地调用initAndroidView方法创建一个TextureAndroidViewController对象),显式地选择Virtual Display模式。

示例代码如下:

Widget build(BuildContext context) {  // This is used in the platform side to register the view.  const String viewType = '<platform-view-type>';  // Pass parameters to the platform side.  final Map<String, dynamic> creationParams = <String, dynamic>{};
  return AndroidView(    viewType: viewType,    layoutDirection: TextDirection.ltr,    creationParams: creationParams,    creationParamsCodec: const StandardMessageCodec(),  );}

### Hybrid Composition(HC)

为了解决了Virtual Display的问题,特别是触摸事件、文本输入、辅助功能等功能性问题。在Flutter 1.20.0中,官方引入了Hybrid Composition模式。

#### 实现原理

与「Virtual Display」不同,该模式直接在视图层次结构中显示原生Android视图:

  1. 如图(3)所示,Flutter widget树被分为两个不同的Android原生视图(FlutterImageView),一个在platform view下方,一个在其上方。具体方案是,通过ImageReader获取Flutter UI的输出,并将其转为FlutterImageView,然后将其与原生Android视图一起添加到视图层次结构中,进行渲染。

  2. 为了避免撕裂或其他视觉残影,Flutter的合成必须在platform线程上完成,而不是raster线程,所以需要将raster线程合并到platform线程。

由于原生视图直接显示,就像在非Flutter应用程序中一样,因此,该模式具有非常好的兼容性。

图片

图片

  图(3)图(4)

#### 方案缺陷

该方案确实解决了「Virtual Display」的痛点,但是自身也存在一定缺陷:

  1. 在SDK 29(Android 10)之前的Android版本上,由于不支持通过HardwareBuffer直接创建Bitmap,Flutter帧需要进行GPU->CPU->GPU往返拷贝,这会严重影响性能。(https://github.com/googleads/googleads-mobile-flutter/issues/269)

  2. Raster线程与Platform线程合并带来了潜在的性能损失,以及死锁问题(https://github.com/flutter/flutter/issues/94524)

#### 使用方式

在TLHC模式上线前(Flutter3.0之前),开发者可以简单地通过initSurfaceAndroidView接口创建一个SurfaceAndroidViewController对象来显式地选择Hybrid Composition模式。

示例代码如下:


Widget build(BuildContext context) {
  // This is used in the platform side to register the view.
  const String viewType = '<platform-view-type>';
  // Pass parameters to the platform side.
  const Map<String, dynamic> creationParams = <String, dynamic>{};

  return PlatformViewLink(
    viewType: viewType,
    surfaceFactory:
        (context, controller) {
      return AndroidViewSurface(
        controller: controller as AndroidViewController,
        gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{},
        hitTestBehavior: PlatformViewHitTestBehavior.opaque,
      );
    },
    onCreatePlatformView: (params) {
      return PlatformViewsService.initSurfaceAndroidView(
        id: params.id,
        viewType: viewType,
        layoutDirection: TextDirection.ltr,
        creationParams: creationParams,
        creationParamsCodec: const StandardMessageCodec(),
        onFocus: () {
          params.onFocusChanged(true);
        },
      )
        ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
        ..create();
    },
  );
}

### Texture Layer Hybrid Composition(TLHC)

Hybrid Composition(HC)解决了Virtual Display(VD)的问题,但因自身的性能问题饱受诟病。于是,Flutter 3.0又推出了Texture Layer Hybrid Composition(TLHC)方案。

TLHC最初的目的是完全替代HC和VD。一度,VD的代码被官方移除,HC也默认不能使用(这是导致Android端模式选择混乱复杂的原因)。然而,事实证明TLHC存在一些限制,无法完全替代它们。

#### 实现原理

图片

图片

图(5)图(6)

如图(5)所示,TLHC使用自定义的FrameLayout,它与Virtual Display类似,将Android View绘制重定向到支持Flutter纹理的canvas上;然后,Flutter引擎将该纹理与Flutter UI的其余部分进行合成(无需Hybrid Composition的分层和线程复杂性);最后,将其一起输出。

#### 方案缺陷

该方案性能表现良好,大多数情况下可以作为开发者首选,但是存在一定「兼容性」问题:

  1. 要求API level>= 23 (Android 6.0或以上)
    由于涉及Surface#lockHardwareCanvas API,这需要SDK级别23或更高版本。

  2. 不支持Android SurfaceView
    Android SurfaceView绕过了Android视图的正常绘制机制,因此没有按预期重定向到指定纹理。如果PlatformView本身是或包含SurfaceView,该模式将无法正常工作,SurfaceView将被绘制在错误的位置和/或z-index。为了缓解该问题,当SurfaceView存在时,PlatformView会尝试自动回退到VD或HC。但,这仅在创建PlatformView时SurfaceView存在时才有效。

  3. Hummer发现并修复的兼容性问题:

    1. 在部分Android Q上,PlatformView显示白屏([pull/31698](https://github.com/flutter/engine/pull/31698))

    2. onTrimMemory时,Android Q及以上机型出现崩溃([pull/33655](https://github.com/flutter/engine/pull/33655))

    3. 其他问题,例如,原生视图与Flutter UI显示不同步的问题([issues/121686](https://github.com/flutter/flutter/issues/121686))

#### 使用方式

非常遗憾的是,TLHC方案上线后,并没有为新的模式增加新接口,而是粗暴地将现有的HC和VD的接口的底层实现全部修改为走TLHC方案。当发现TLHC不能完全替换VD和HC时,为了处理兼容性问题,官方又增加了一系列的回退方案,这直接导致了,如今的模式选择非常混乱和复杂。

总结来说:

  1. 先前的 VD 和 HC 接口现在默认使用 TLHC 模式,但在特定情况下可能会自动回退到 VD 和 HC 模式(具体细节,请查看示例代码中的注释);

  2. 新增了 initExpensiveAndroidView 接口,用于显式指定使用 HC 模式;

  3. VD 模式仅用于回退,开发者无法显式指定使用;

示例代码如下:

if (usingAndroidViewSurface) {  return PlatformViewLink(    viewType: viewType,    surfaceFactory: (context, controller) {      return AndroidViewSurface(        controller: controller as AndroidViewController,        gestureRecognizers: const <            Factory<OneSequenceGestureRecognizer>>{},        hitTestBehavior: PlatformViewHitTestBehavior.opaque,      );    },    onCreatePlatformView: (params) {      if (usingHybridComposition) {        // 1. 开发者显式指定始终使用「Hybrid composition」模式        // (该API是在Flutter 3.0中引入的,始终使用HC模式)        return PlatformViewsService.initExpensiveAndroidView(          id: params.id,          viewType: viewType,          layoutDirection: TextDirection.ltr,          creationParams: creationParams,          creationParamsCodec: const StandardMessageCodec(),          onFocus: () {            params.onFocusChanged(true);          },        )          ..addOnPlatformViewCreatedListener(              params.onPlatformViewCreated)          ..create();      } else {        // 2. 引擎默认使用Texture Layer Hybrid composition模式;        // 如果不支持,则自动fallback回「Hybrid composition」模式。        // 如果当前SDK版本<23或平台视图层次结构在创建时包含SurfaceView(或子类),则触发回退。        //        // 【已知问题】:        // 如果视图层次结构在创建时不包含SurfaceView,但稍后添加了一个,则渲染将无法正常工作;        //        // 【解决方案】        // 1. 在创建时在视图层次结构中包含一个0x0的SurfaceView,以触发回退到HC;        // 2. 或者切换到initExpensiveAndroidView显式指定HC;        //  (上述行为适用于Flutter 3.7+。Flutter 3.0未包括回退到HC,在Flutter <3.0中始终使用HC。)        return PlatformViewsService.initSurfaceAndroidView(          id: params.id,          viewType: viewType,          layoutDirection: TextDirection.ltr,          creationParams: creationParams,          creationParamsCodec: const StandardMessageCodec(),          onFocus: () {            params.onFocusChanged(true);          },        )          ..addOnPlatformViewCreatedListener(              params.onPlatformViewCreated)          ..create();      }    },  );} else {  // 3. 默认使用Texture Layer Hybrid composition模式;  // 如果不支持,则自动回退到「Virtual display」模式。  // 如果当前SDK版本<23或平台视图层次结构在创建时包含SurfaceView(或子类),则触发回退。  //  // 【已知问题】:  // 如果视图层次结构在创建时不包含SurfaceView,但稍后添加了一个,则渲染将无法正常工作;  //  // 【解决方案】  // 1. 在创建时在视图层次结构中包含一个0x0的SurfaceView,以触发回退到VD;  // 2. 或者切换到initExpensiveAndroidView显式指定HC;  // (上述行为适用于Flutter 3.3+。Flutter 3.0未包括回退到VD,在Flutter <3.0中始终使用VD。)  return AndroidView(    viewType: viewType,    layoutDirection: TextDirection.ltr,    creationParams: creationParams,    creationParamsCodec: const StandardMessageCodec(),  );}

## IOS

### Hybrid Composition(HC)

在iOS端,PlatformView只有Hybrid Composition一种实现方案,从Flutter 1.22开始默认启用,不再需要在Info.plist中添加io.flutter.embedded_views_preview标志。

#### 实现原理

图片

 图(7)

与Android端的HC方案类似,嵌入的原生视图将Flutter UI分成多个部分,最下面的部分被绘制到FlutterView,上面的多个部分被渲染到多个FlutterOverlayView上(取决于Flutter UI和原生视图之间的交错层数)。

当然,同样也需要动态合并线程,以解决渲染同步问题。

#### 方案缺陷

与Android端类似,合并线程带来了一些问题:

1. 性能问题,例如,高刷机抖动问题([#issues/116640](https://github.com/flutter/flutter/issues/116640))

2. 死锁风险,例如,[#issues/94524](https://github.com/flutter/flutter/issues/94524)

#### 使用方式

Widget build(BuildContext context) {  // This is used in the platform side to register the view.  const String viewType = '<platform-view-type>';  // Pass parameters to the platform side.  final Map<String, dynamic> creationParams = <String, dynamic>{};
  return UiKitView(    viewType: viewType,    layoutDirection: TextDirection.ltr,    creationParams: creationParams,    creationParamsCodec: const StandardMessageCodec(),  );}

## 挖洞模式

为了解决当时Virtual Display的性能问题,Hummer在1.17.4版本上线了「挖洞模式」渲染方案,支持Android和iOS双端,取得了不错的效果。

### 实现原理

挖洞模式不需要合并线程,简单高效:

1. 将原生视图放到FlutterView的下方,并将相应位置透明化处理(挖洞),这样原生视图与Flutter UI的渲染互不干扰,但从用户视觉上看是一个整体;

2. 在Raster线程中计算原生视图的位置、大小、透明度等信息,并在合适时机通知Platform线程更新,以此做到线程分离,并优化了原生视图与Flutter UI的显示同步;

图片

图片

 图(8)图(9)

### 方案缺陷

与官方的方案相比,挖洞模式 在性能和兼容性等方面都有明显的优势。

但,暂不支持半透明混合,可能导致部分场景使用受限。

### 使用方式

在Hummer3.0上,「挖洞模式」不再默认开启,需要开发者显式指定。为了兼容Hummer3.0以下版本和官方的多种渲染模式,我们做了以下修改:

1)保留了前一个版本的`renderType`参数;

2)新增`initSimpleAndroidView`接口;

具体使用方式,请查看下面示例代码和注释:

@override
Widget build(BuildContext context) {
  // Pass parameters to the platform side.
  final Map<String, dynamic> creationParams = <String, dynamic>{
    'PenetratedDisplay': usingPenetratedDisplay,
  };

  switch (defaultTargetPlatform) {
    case TargetPlatform.android:
      if (usingAndroidViewSurface) {
        return PlatformViewLink(
          viewType: viewType,
          surfaceFactory: (context, controller) {
            return AndroidViewSurface(
              controller: controller as AndroidViewController,
              gestureRecognizers: const <
                  Factory<OneSequenceGestureRecognizer>>{},
              hitTestBehavior: PlatformViewHitTestBehavior.opaque,
            );
          },
          onCreatePlatformView: (params) {
            if (usingPenetratedDisplay) {
              // 1. 显式使用「挖洞模式」(Hummer3.0新增)
              return PlatformViewsService.initSimpleAndroidView(
                id: params.id,
                viewType: viewType,
                layoutDirection: TextDirection.ltr,
                creationParams: creationParams,
                creationParamsCodec: const StandardMessageCodec(),
                onFocus: () {
                  params.onFocusChanged(true);
                },
              )
                ..addOnPlatformViewCreatedListener(
                    params.onPlatformViewCreated)
                ..create();
            } else if (usingHybridComposition) {
              // 2. 显式使用「Hybrid composition」模式
              return PlatformViewsService.initExpensiveAndroidView(
                id: params.id,
                viewType: viewType,
                layoutDirection: TextDirection.ltr,
                creationParams: creationParams,
                creationParamsCodec: const StandardMessageCodec(),
                onFocus: () {
                  params.onFocusChanged(true);
                },
              )
                ..addOnPlatformViewCreatedListener(
                    params.onPlatformViewCreated)
                ..create();
            } else {
              // 3. 默认使用Texture Layer Hybrid composition模式;
              // 如果不支持(例如,SDK_INT小于23或者包含surface view),
              // 则自动fallback回「Hybrid composition」模式。
              return PlatformViewsService.initSurfaceAndroidView(
                id: params.id,
                viewType: viewType,
                layoutDirection: TextDirection.ltr,
                creationParams: creationParams,
                creationParamsCodec: const StandardMessageCodec(),
                onFocus: () {
                  params.onFocusChanged(true);
                },
              )
                ..addOnPlatformViewCreatedListener(
                    params.onPlatformViewCreated)
                ..create();
            }
          },
        );
      } else {
        // 4. 默认使用Texture Layer Hybrid composition模式;
        // 如果不支持(例如,SDK_INT小于23或者包含surface view),
        // 则自动fallback回「Virtual display」。
        return AndroidView(
          viewType: viewType,
          // 5. 新增「挖洞模式」可选参数
          renderType: (usingPenetratedDisplay
              ? PlatformViewRenderType.penetratedDisplay
              : null),
          layoutDirection: TextDirection.ltr,
          creationParams: creationParams,
          creationParamsCodec: const StandardMessageCodec(),
        );
      }
    case TargetPlatform.iOS:
      return UiKitView(
        viewType: viewType,
        // 6. 新增「挖洞模式」可选参数
        renderType: (usingPenetratedDisplay
            ? PlatformViewRenderType.penetratedDisplay
            : null),
        layoutDirection: TextDirection.ltr,
        creationParams: const <String, dynamic>{},
        creationParamsCodec: const StandardMessageCodec(),
      );
    default:
      throw UnsupportedError('Unsupported platform view');
  }
}

# PlatformView模式选择

## 模式汇总

在前面的章节中,我们梳理了 PlatformView 方案的演进历史,深入分析了各个方案的实现原理和优缺点,现将其整理如下,以供参考:

嵌入方案优 点缺 点支持平台
VD

——

1. 辅助功能、触摸事件、文本输入等问题比较多;

2. Flutter 3.0后,该模式只会被动fallback,不能主动选择

Android
HC兼容性好1. 线程合并引发的系列问题;2. AndroidQ以下机型性能差Android & iOS
TLHC

性能次优

兼容比较性差,根据下面两种情况会「自动」fallback到VC或HC模式:

1. 不支持SurfaceView

2. 不支持Android6.0以下的机型 

Android
挖洞模式性能最佳

不支持半透明混合

Android & iOS

【注】也可以基于 DrawFunctor 来实现PlatformView,其优点是内存占用少,可以实现PlatformView与Flutter UI的严格帧同步,但,它的最大的弊端是存在「系统兼容性问题」。

## 选择建议

对于开发者如何选择这些模式,这里给出以下一般性建议,遇特殊情况还需酌情考虑:

  1. 交互复杂,对兼容性要求高的场景,优先考虑 Hybrid Composition;

  2. 不需要半透明混合,对性能要求比较高的场景,优先尝试 挖洞模式;

  3. 在Android端,其他场景使用TLHC,需注意fallback到VD或HC的影响;

# PlatformView优化和新方案探索

在刚过去FY23财年,Hummer在PlatformView方面做了如下几个方面的工作:

  1. 积极参与Flutter社区的沟通和交流, 结合我们的业务,打磨和优化现有功能;

  2. 挖洞模式代码重构和功能增强;

  3. 新渲染方案的研究和探索;

  4. 桌面端的调研和验证;

## 社区贡献

Flutter官方PlatformView方案的在不断演进和迭代,Hummer一如既往地深度参与其中,与社区一起推动PlatformView走向更好。下面列举了部分Hummer发现和解决的问题:

### 1. 部分Android Q上,TLHC模式显示白屏([#pull/31698](https://github.com/flutter/engine/pull/31698))

图片

该问题非常隐晦,很难跟踪。最终的原因是,在部分Android Q手机上,主要是Android Q早期版本,因为兼容性问题,系统SurfaceTexture要求强制读写同步,也就是没有额外的一帧缓冲,前一帧消费掉后才允许提交新的一帧。否则,接下来向BufferQueue请求帧缓冲都会失败,从而导致PlatformView区域显示白屏。

### 2. onTrimMemory触发时,Android Q及以上机型出现崩溃([#pull/33655](https://github.com/flutter/engine/pull/33655))

图片

这是TLHC的另一严重的高崩问题。该问题的原因是,系统在收到低内存通知(level=80)时,Android Q及以上的系统会释放底层的Surface,而不会通知上层(也没有可用的通知渠道)。

### 3. 其他问题

除了上述问题,在Hummer 3.0升级过程中我们还发现了其他一些问题,其中大部分还没来得及去分析:

1. [[Android & HC] 在共享引擎的场景下,打开新页面或后退闪现白屏](https://github.com/flutter/flutter/issues/97188)

2. [[Android & HC] 在共享引擎的场景下,打开新页面会残留前一个页面的PlatformView](https://github.com/flutter/flutter/issues/113826)

3. [[iOS] 滑动包含PlatformView的页面,页面顶部闪现白色区域](https://github.com/flutter/flutter/issues/119485)

4. [[Android & TLHC] 打开新页面时,PlatformView比Flutter UI晚几帧显示](https://github.com/flutter/flutter/issues/121686)

5. [[Android & HC] 后退时,PlatformView比Flutter UI晚几帧消失](https://github.com/flutter/flutter/issues/121687)

6. ~~[[iOS] 在高刷机上滑动PlatformView列表页面出现严重抖动](https://github.com/flutter/flutter/issues/116640)~~

## 挖洞模式优化

Hummer的挖洞模式简单高效,仍有很大的价值,有进一步优化使用体验的必要。在Hummer3.0中,我们对挖洞模式进行了重大重构,具体如下:

  1. 优化了设计,方便后续维护和升级;

  2. 支持缩放/旋转/圆角等矩阵变换;

  3. 优化了滑动体验;

  4. 优化了开发者接口;

### 支持缩放/旋转/圆角

               Android                 iOS

,时长00:30

,时长00:22

 视频(1) 视频(2)

注:在视频中,PlatformView背景颜色变化是手势点击触发的。

### 解决iOS滑动残影

                优化前                 优化后

,时长00:06

,时长00:06

视频(3)视频(4)

## 桌面端调研

Flutter的 2023年路线图([Roadmap](https://github.com/flutter/flutter/wiki/Roadmap))规划了今年可能会实现的一些重要功能,其中就明确包括了PlatformView。

图片

但是,截止目前(Flutter 3.7.9)桌面端的PlatformView仍不可用,仅对macOS有了非常有限的支持,Windows还无任何代码提交,Linux平台暂无明确计划。看起来,Flutter的大部分人力都投入到了impeller和web中去了,可能人力不太足~

macOS目前仅支持嵌入简单原生视图,不支持半透明、旋转、缩放、圆角等变换,也不支持与Flutter UI交错渲染。下图是我们在macOS上的测试效果([demo源码](https://github.com/0xZOne/platform_view_macos_example)):

图片

图片

 图(14) 图(15)

# 结语

Flutter自2018年开始在移动端支持PlatformView,并经过几年的迭代演进,日趋完善,形成了现在多种方案共存的局面。由于没有一种方案能覆盖所有场景,需要业务开发者熟悉每种方案的优缺点,并根据不同场景选择合适的方案。Hummer接下来在移动端所需要做的事情是,结合业务对现有方案进行完善和优化,特别是官方没太重视的混合栈场景。

对于桌面端,Hummer才刚扬帆起航,还没有收到业务对PlatformView功能的强烈诉求,我们会提前做些技术储备。具体而言,一方面,我们会密切关注官方桌面端PlatformView的进展,做好随时切入的准备;另一方面,会考虑将简单高效的挖洞模式移植到桌面端。

PlatformView功能涉及多端,加上混合栈场景,很多问题复现和处理起来并不容易。欢迎大家一起共建和探讨~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值