FSA全栈行动 FSA全栈行动
首页
  • 移动端文章

    • Android
    • iOS
    • Flutter
  • 学习笔记

    • 《Kotlin快速入门进阶》笔记
    • 《Flutter从入门到实战》笔记
    • 《Flutter复习》笔记
前端
后端
  • 学习笔记

    • 《深入浅出设计模式Java版》笔记
  • 逆向
  • 分类
  • 标签
  • 归档
  • LinXunFeng
  • GitLqr

公众号:FSA全栈行动

记录学习过程中的知识
首页
  • 移动端文章

    • Android
    • iOS
    • Flutter
  • 学习笔记

    • 《Kotlin快速入门进阶》笔记
    • 《Flutter从入门到实战》笔记
    • 《Flutter复习》笔记
前端
后端
  • 学习笔记

    • 《深入浅出设计模式Java版》笔记
  • 逆向
  • 分类
  • 标签
  • 归档
  • LinXunFeng
  • GitLqr
  • AndroidUI

  • Android第三方SDK

  • Android混淆

  • Android仓库

  • Android新闻

  • Android系统开发

  • Android源码

  • Android注解AOP

  • Android脚本

  • AndroidTv开发

  • AndroidNDK

  • Android音视频

  • Android热修复

  • Android性能优化

  • Android云游戏

  • Android插件化

  • iOSUI

  • iOS工具

  • iOS底层原理与应用

  • iOS组件化

  • iOS音视频

  • iOS疑难杂症

  • iOS之Swift

  • iOS之RxSwift

  • iOS开源项目

  • iOS逆向

  • Flutter开发

    • Dart - 抽象类的实例化
    • Flutter - 打印好用的Debug日志
    • Flutter - 混合开发
    • Flutter - 解决混合开发iOS脚本打包遇到的问题
    • Flutter - 低版本在iOS14上遇到的问题与解决方案
    • Flutter - 解决原生弹窗的触摸事件被Flutter响应的问题
    • Flutter - 实现列表上下拉切换header
    • Flutter - 获取ListView当前正在显示的Widget信息
    • Flutter - 列表滚动定位超强辅助库,墙裂推荐!🔥
    • Flutter - 快速实现聊天会话列表的效果,完美💯
    • Flutter - 聊天输入框更新文本时的必备优化点🔖
    • Flutter - 我给官方提PR,解决run命令卡住问题 😃
    • Flutter - 探索run命令到底做了什么 🤔
    • Flutter - 引擎调试(iOS篇)🛠
    • Flutter - 引擎调试bug到提交PR实战 🐞
    • Flutter - 船新升级😱支持观察第三方构建的滚动视图💪
    • Flutter - 瀑布流交替播放视频 🎞
    • Flutter - IM保持消息位置大升级(支持ChatGPT生成式消息) 🤖
    • Flutter - 滚动视图中的表单防遮挡 🗒
    • Flutter - 秒杀1/2曝光统计 📊
    • 一天内加入 Flutter 和 FlutterCandies 两大组织是什么体验 🧐
    • Flutter - 如何快速搓一个微信通讯录列表(azlist) 📓
    • Flutter - 混编项目集成Shorebird热更新🐦(安卓篇)
    • Flutter - 混编项目集成Shorebird热更新🐦(iOS篇)
    • Flutter - 解决返回原生页面时dispose方法未被触发的问题 🐞
    • Flutter - 升级3.19之后页面多次rebuild?🤨
    • Flutter - 热更新 Shorebird 1.0 正式版来了 🐦
    • Flutter - 使用Pigeon实现视频缓存插件 🐌
      • 一、概述
      • 二、创建 Plugin
      • 三、原生依赖
        • iOS
        • 安卓
      • 四、Pigeon
        • 添加依赖
        • 定义通信接口
        • 生成交互代码
        • 坑点
      • 五、编写原生代码
        • iOS
        • 安卓
      • 六、开源库
      • 七、结尾
      • 八、资料
    • Flutter - 轻松搞定屏幕旋转功能 😎
    • Flutter - 解决Connection closed before full header was received
    • Flutter - 实现聊天键盘与功能面板的丝滑切换 🍻
    • Flutter - 支持观察NestedScrollView,兼容性更强 😈
    • Flutter - 聊天键盘与面板丝滑切换的强势升级 🍻
    • Flutter - 升级到3.24后页面还会多次rebuild吗?🧐
    • Flutter - 轻松实现PageView卡片偏移效果
    • Flutter - 轻松搞定炫酷视差(Parallax)效果
    • Flutter - 危!3.24版本苹果审核被拒!
    • Flutter - 子部件任意位置观察滚动数据
    • Flutter - iOS编译加速
    • Flutter - Xcode16 还原编译速度
  • 移动端
  • Flutter开发
LinXunFeng
2024-04-27
目录

Flutter - 使用Pigeon实现视频缓存插件 🐌

欢迎关注微信公众号:[FSA全栈行动 👋]

# 一、概述

Pigeon 是一个可以帮助我们生成 Flutter 与 原生 的通信代码的工具,我们只需要关注其两侧主要的数据处理逻辑即可,从而提升效率。

Flutter 端对于视频缓存功能主要还是依赖原生端比较成熟的实现方案,如下两个开源库

  • iOS: https://github.com/ChangbaDevs/KTVHTTPCache (opens new window)
  • 安卓: https://github.com/danikula/AndroidVideoCache (opens new window)

其功能是:丢给它一个视频链接,它将生成一个具备缓存功能的播放代理链接。

接下来我们一起看看,如何使用 Pigeon 并结合上述两个库来实现视频缓存插件。

# 二、创建 Plugin

使用如下命令生成插件项目,这里我指定iOS使用的是 Swift,安卓使用的是 Kotlin

flutter create --template=plugin --platforms=android,ios -i swift -a kotlin 项目名

# 如:
# flutter create --template=plugin --platforms=android,ios -i swift -a kotlin video_cache

# 三、原生依赖

# iOS

打开在 ios 目录下的 podspec 文件(这里是 video_cache.podspec),添加相关的第三方库依赖,比如我这里依赖的是 KTVHTTPCache。

#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
# Run `pod lib lint video_cache.podspec` to validate before publishing.
#
Pod::Spec.new do |s|
  s.name             = 'video_cache'
  s.version          = '0.0.1'
  s.summary          = 'A new Flutter plugin project.'
  s.description      = <<-DESC
A new Flutter plugin project.
                       DESC
  s.homepage         = 'http://example.com'
  s.license          = { :file => '../LICENSE' }
  s.author           = { 'Your Company' => 'email@example.com' }
  s.source           = { :path => '.' }
  s.source_files = 'Classes/**/*'
  s.dependency 'Flutter'
  s.platform = :ios, '11.0'

  # Flutter.framework does not contain a i386 slice.
  s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
  s.swift_version = '5.0'

+  # KTVHTTPCache
+  s.dependency 'KTVHTTPCache', '~> 3.0.0'
end

# 安卓

打开在 android 目录下的 build.gradle 文件,添加

...
android {
    ...

    dependencies {
        testImplementation 'org.jetbrains.kotlin:kotlin-test'
        testImplementation 'org.mockito:mockito-core:5.0.0'
+        implementation 'com.danikula:videocache:2.7.1'
    }

    ...
}

然后在 example/android 目录下的 build.gradle 和 settings.gradle 文件添加如下 maven,否则会找不到依赖库

// build.gradle

allprojects {
    repositories {
+        maven { url "https://jitpack.io" }
+        maven { url 'https://maven.aliyun.com/repository/public' }
        google()
        mavenCentral()
    }
}
...
// settings.gradle

pluginManagement {
    ...
    repositories {
+        maven { url "https://jitpack.io" }
+        maven { url 'https://maven.aliyun.com/repository/public' }
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}
...

# 四、Pigeon

# 添加依赖

在 pubspec.yaml 的 dev_dependencies 下添加 pigeon 依赖

dev_dependencies:
  pigeon: ^17.3.0

# 定义通信接口

在 lib 目录外创建一个用来定义通信接口的 dart 文件。

这里我们新建了一个与 lib 目录同级的 pigeons 文件夹,来存放与 Pigeon 相关的文件

.
├── lib
│   ├── ...
│   └── ...
├── pigeons
│   ├── cache.dart
│   └── ...
├── ...

cache.dart 就是我用来定义视频缓存功能相关的通信接口的文件。

import 'package:pigeon/pigeon.dart';

// https://github.com/flutter/packages/blob/main/packages/pigeon/example/README.md
@ConfigurePigeon(PigeonOptions(
  dartOut: 'lib/plugin/pigeon.g.dart',
  kotlinOut:
      'android/src/main/kotlin/com/lxf/video_cache/VideoCacheGeneratedApis.g.kt',
  kotlinOptions: KotlinOptions(
    // https://github.com/fluttercommunity/wakelock_plus/issues/18
    errorClassName: "LXFVideoCacheFlutterError",
  ),
  swiftOut: 'ios/Classes/LXFVideoCacheGeneratedApis.g.swift',
))
@HostApi()
abstract class LXFVideoCacheHostApi {
  /// 转换为缓存代理URL
  String convertToCacheProxyUrl(String url);
}

# 生成交互代码

再执行如下命令,指定根据 cache.dart 来生成相应的繁杂且重要的交互代码。

flutter pub run pigeon --input pigeons/cache.dart

# 坑点

一定一定,一定要自定义 kotlinOptions 里的 errorClassName,不然它会给你生成默认的 FlutterError,单单自己的插件编译可能不会怎样,但是一旦集成的项目里也有用到其它用 Pigeon 生成了 FlutterError 的插件时,就会报如下错误了

Type FlutterError is defined multiple times

自定义 kotlinOptions 里的 errorClassName:

@ConfigurePigeon(PigeonOptions(
  ...
  kotlinOptions: KotlinOptions(
    // https://github.com/fluttercommunity/wakelock_plus/issues/18
    errorClassName: "LXFVideoCacheFlutterError"
  ),
  ...
))

# 五、编写原生代码

# iOS

进入到 example/ios 目录下,安装依赖

cd example/ios 
pod install --repo-update

使用 Xcode 打开 Runner.xcworkspace 开始编写原生代码

// VideoCachePlugin.swift

import Flutter
import UIKit
import KTVHTTPCache

// 创建插件时自动生成的类
public class VideoCachePlugin: NSObject, FlutterPlugin {
  public static func register(with registrar: FlutterPluginRegistrar) {
    // 注册实现
    LXFVideoCacheHostApiSetup.setUp(
      binaryMessenger: registrar.messenger(), 
      api: LXFVideoCacheHostApiImplementation()
    )
  }
}

class LXFVideoCacheHostApiImplementation: LXFVideoCacheHostApi {
  /// 是否可以代理
  private var canProxy: Bool?
  
  func convertToCacheProxyUrl(url: String) throws -> String {
    // 还未试过开启代理服务
    if (self.canProxy == nil) {
      self.canProxy = ((try? KTVHTTPCache.proxyStart()) != nil)
    }
    // 无法代理
    if !self.canProxy! { return url }
    // 无法转 URL 对象
    guard let urlObj = URL(string: url) else { return url }
    
    guard let proxyUrlObj = KTVHTTPCache.proxyURL(withOriginalURL: urlObj) else {
      // 代理失败
      return url
    }
    // 代理成功
    return proxyUrlObj.absoluteString
  }
}

# 安卓

使用 AndroidStudio 打开 example/android,找到外层的 android 项目开始编写原生代码

package com.lxf.video_cache

import LXFVideoCacheHostApi
import com.danikula.videocache.HttpProxyCacheServer

import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result

/** VideoCachePlugin */
class VideoCachePlugin : FlutterPlugin, MethodCallHandler {

    private lateinit var videoCacheHostApiImplementation: LXFVideoCacheHostApiImplementation

    override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
        videoCacheHostApiImplementation = LXFVideoCacheHostApiImplementation(flutterPluginBinding)
        // 初始化插件
        LXFVideoCacheHostApi.setUp(
                flutterPluginBinding.binaryMessenger,
                videoCacheHostApiImplementation,
        )
    }

    override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
        // 关闭服务
        videoCacheHostApiImplementation.shutdown()
    }

    override fun onMethodCall(call: MethodCall, result: Result) {}
}

class LXFVideoCacheHostApiImplementation(
        private val flutterPluginBinding: FlutterPlugin.FlutterPluginBinding
) : LXFVideoCacheHostApi {
    /// 懒加载缓存服务
    private val cacheServer by lazy { HttpProxyCacheServer.Builder(flutterPluginBinding.applicationContext).build() }

    /// 重写并通过 cacheServer 将原 url 转换为具备缓存功能的 url
    override fun convertToCacheProxyUrl(url: String): String {
        return cacheServer.getProxyUrl(url)
    }

    /// 关闭服务
    fun shutdown() {
        cacheServer.shutdown()
    }
}

# 六、开源库

上述视频缓存插件已开源,并发布至 GitHub:https://github.com/LinXunFeng/flutter_video_cache (opens new window)

你可以通过如下步骤集成使用:

在 pubspec.yaml 中添加 video_cache 依赖

dependencies:
  video_cache: latest_version

使用

// 导入
import 'package:video_cache/video_cache.dart';

// 将原视频链接转为缓存代理链接
String url = 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4';
url = await VideoCache().convertToCacheProxyUrl(url);

// url转换结果
// http://localhost:50050/https%3A%2F%2Fflutter%2Egithub%2Eio%2Fassets%2Dfor%2Dapi%2Ddocs%2Fassets%2Fvideos%2Fbee%2Emp4/KTVHTTPCachePlaceHolder/KTVHTTPCacheLastPathComponent.mp4

然后把转换后的 url 丢给播放器就可以了~

# 七、结尾

以上就是 Flutter 与原生交互拿到代理 url 的例子,使用的是 @HostApi,而如果你如果在原生端去调用 Flutter 的 api,则使用 @FlutterApi 去标注相关抽象类即可,使用方法是差不多的。

需要注意的是,当你使用 Swift 去写插件,且使用了 @FlutterApi 去生成相应的原生代码后编译,可能会遇到这个错误

type 'FlutterError' does not conform to protocol 'Error'

添加如下拓展即可

// https://github.com/flutter/flutter/issues/136081
extension FlutterError: Error {}

# 八、资料

  • https://github.com/flutter/packages/blob/main/packages/pigeon/example/README.md (opens new window)
#Dart#Flutter#视频#缓存#视频缓存
Flutter - 热更新 Shorebird 1.0 正式版来了 🐦
Flutter - 轻松搞定屏幕旋转功能 😎

← Flutter - 热更新 Shorebird 1.0 正式版来了 🐦 Flutter - 轻松搞定屏幕旋转功能 😎→

最近更新
01
Flutter - Xcode16 还原编译速度
04-05
02
AI - 免费的 Cursor 平替方案
03-30
03
Android - 2025年安卓真的闭源了吗
03-28
更多文章>
Theme by Vdoing | Copyright © 2020-2025 FSA全栈行动
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×