爱奇艺开播助手Flutter跨平台Hybrid实践\n

\u003ch2\u003e爱奇艺开播助手简介\u003c/h2\u003e\n\u003cp\u003e爱奇艺开播助手项目,又称\u0026quot;直播机\u0026quot;,该项目目标是通过一个移动平台为主播提供多样化的直播内容。现阶段所涵盖的直播内容包括:游戏直播,美女摄像直播,小剧场直播,其中游戏直播相对主播数量最多,3 种推流模式所涉及的推流 SDK 基本一致,推流逻辑存在部分差异。\u003c/p\u003e\n\u003cp\u003e该项目的 Android 端和 iOS 端架构类似,主要由 APP、SDK 和 so 三层构成,APP 层负责界面展示和交互,由各端 Native 代码实现,so 层负责封装核心的推流、播放等功能,由于更接近底层硬件,使用 C 实现,而中间的 SDK 层负责调用这些 so 库的功能。\u003c/p\u003e\n\u003cp\u003e由于双端的业务几乎完全一样,双端为了提高代码的复用率,我们试图接入一套跨平台的框架同时开发两端的 APP。\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://static.geekbang.org/infoq/5c66198ae29c2.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003ch2\u003e为什么选择 Flutter\u003c/h2\u003e\n\u003cp\u003e移动端跨平台一直是开发者老生常谈的话题,为了尽可能的增加代码复用,降低开发成本,各大科技巨头都有自己的跨平台框架,比如 Facebook 的 React-Native、阿里的 Weex、Cordova 等。这些跨平台框架各有优劣,Google 也“不甘寂寞”,在 2018 年 Google 开发者大会上重点介绍了自己的跨平台框架 Flutter。\u003c/p\u003e\n\u003cp\u003e和 RN 和 Weex 将 javascript 转化为原生控件渲染不同,Flutter 完全挣脱了原生控件的“束缚”,如图 1 所示,Flutter 使用了分层架构,分为 Framework 和 Engine 两个部分,其中 Framework 层提供各种基础组件库,包括各种 Widget,动画等,Engine 层则完全由 C 和 C++ 实现,使用 Skia 进行渲染(对!就是 chrome 用的那个图形渲染框架),官方宣称可以达到原生 app 的渲染性能。\u003c/p\u003e\n\u003cp\u003e下图是和 RN、Weex 之间的对比:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://static.geekbang.org/infoq/5c6619d04711f.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003cp\u003e可以看到目前 Flutter 从各个方面都已经不逊于前两位,而且在 Google 新操作系统 Fuchsia(被认为是 Android 的继任者) 也使用 Flutter 作为其 UI 框架,今后的发展不可限量。\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://static.geekbang.org/infoq/5c6619ffe5eaf.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003cp\u003e除了渲染性能之外,Flutter 还有一个非常诱人的特性:HotReload,在 debug 下的 Flutter 工程可以快速热重载到真机上,修改完代码后 Ctrl+S 就能实时展现在真机界面上,不需要重新安装 apk 包,想想就兴奋!\u003c/p\u003e\n\u003cp\u003e如果你对 HotReload 原理感兴趣,可以移步 Flutter 官网进一步了解 HotReload:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://static.geekbang.org/infoq/5c661e04670cf.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003cp\u003e总体来看,Flutter 有性能好、开发效率高、跨平台和可无缝接入原有工程等优势,所以我们尝试使用 Flutter 进行开播助手的改造实践。\u003c/p\u003e\n\u003ch2\u003e开播助手 Android 端接入\u003c/h2\u003e\n\u003cp\u003e下面详细介绍一下 Android 和 iOS 是如何接入的。\u003c/p\u003e\n\u003ch3\u003e在 Android 中添加 Flutter 组件\u003c/h3\u003e\n\u003cp\u003e目前开播助手中使用了 Flutter 的 Fragment 和 View 两种方式,如下面两段代码所示:使用 Flutter.createFragment() 和 Flutter.createView() 两个方法,这两个方法可以返回 Flutter 创建的供 Android 使用的 Fragment 和 View,接下来和原生的 Fragment 和 View 使用方法就是一模一样了。(是不是很简单!)\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e使用Flutter Fragment\u003c/li\u003e\n\u003c/ul\u003e\n\u003cpre\u003e\u003ccode\u003eFlutter.createFragment(\u0026quot;settings\u0026quot;)\n\u003c/code\u003e\u003c/pre\u003e\n\u003cul\u003e\n\u003cli\u003e使用Flutter View\u003c/li\u003e\n\u003c/ul\u003e\n\u003cpre\u003e\u003ccode\u003eFlutter.createView(getActivity(), getLifecycle(), \u0026quot;settings\u0026quot;);\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e当然为了告诉Flutter需要使用哪个界面,使用了路由的机制,创建fragment或view的时候需要传入一个路由的字符串,在Flutter工程中也需要使用此字符串,代码如下:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003evoid main() { runApp(_widgetForRoute(window.defaultRouteName));}\n\n\u003c/code\u003e\u003c/pre\u003e\n\u003cpre\u003e\u003ccode\u003eWidget _widgetForRoute(String route) {\n switch (route) {case 'settings':return MaterialApp(home: \n SettingsPage()); .... }}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e在Flutter工程的入口处匹配传入的字符串,来决定实例化哪个页面返回。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e使用Module接入\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e开发过程中我们可以使用Moudle依赖来接入,只需要在setting.gradle中添加以下代码即可:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003esetBinding(new Binding([gradle: this]))\nevaluate(new File(settingsDir.parentFile,'flutter_liveshow/.android/include_flutter.groovy'))\n\u003c/code\u003e\u003c/pre\u003e\n\u003cul\u003e\n\u003cli\u003e使用aar接入\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eAndroid使用aar接入Flutter十分的简单,只用下面两步就可以顺利的将使用Flutter开发的界面接入原生的工程。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e使用gradle工具打aar资源包\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e如果要Android可以使用Flutter的工程,可以将Flutter工程打成aar的包,如下图所示,进入工程中的.android/目录,使用./gradlew Flutter:assembleRelease即可。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e将aar包加入工程并依赖\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e如下图所示,首先将打好的release包放入libs目录下\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://static.geekbang.org/infoq/5c661afba4889.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003cp\u003e目前最新版本的Flutter在集成时需要将sdk中的icudtl.dat文件放入资源目录中一起打包,否则会出错,官方正在修复此问题,相信不久就能解决。\u003c/p\u003e\n\u003ch2\u003e开播助手iOS端接入\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003ePodfile接入Flutter\u003c/li\u003e\n\u003c/ul\u003e\n\u003cpre\u003e\u003ccode\u003eflutter_application_path = '../flutter_liveshow/'\neval(File.read(File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')), binding)\n\n\u003c/code\u003e\u003c/pre\u003e\n\u003cblockquote\u003e\n\u003cp\u003eeval(string [, binding [, filename [,lineno]]]) → obj\u003cbr /\u003e\nEvaluates the Ruby expression(s) in string. If binding is given, which must be a Binding object, the evaluation is performed in its context. If the optional filename and lineno parameters are present, they will be used when reporting syntax errors.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e添加完成后执行pod install。这段代码实际就是在Podfile中加入一段Flutter\u003cbr /\u003e\n所需要的脚本。如果基于Flutter master channel开发,生成的podhelper.rb中会增加post_install hooks,如果项目中也使用该hooks,需要手动合并。所幸这个文件只有在修改Flutter plugin依赖并运行Flutter package get之后才会重新生成。\u003c/p\u003e\n\u003ch3\u003eDart代码编译设置\u003c/h3\u003e\n\u003cp\u003e“TARGET APP -\u0026gt; Build Phases -\u0026gt; New Run Script Phase” 新增script phase填入下方代码:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026quot;$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\u0026quot; build\n\u0026quot;$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\u0026quot; embed\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e“Build Settings -\u0026gt; Add User-Defined Setting” 新增 FLUTTER_ROOT 字段。\u003c/p\u003e\n\u003ch3\u003e接入Host App\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eAppDelegate.swift\u003c/li\u003e\n\u003c/ul\u003e\n\u003cpre\u003e\u003ccode\u003eimport Flutter\nimport FlutterPluginRegistrant // Only if you have Flutter Plugins.\n\n@UIApplicationMainclass AppDelegate: FlutterAppDelegate {\n override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -\u0026gt; Bool {\n GeneratedPluginRegistrant.register(with: self);\n ......}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cul\u003e\n\u003cli\u003e在App中接入Flutter开发的页面\u003c/li\u003e\n\u003c/ul\u003e\n\u003cpre\u003e\u003ccode\u003elet flutterViewController = FlutterViewController()\nflutterViewController.setInitialRoute(\u0026quot;settings\u0026quot;)\nnavigationController?.pushViewController(flutterViewController, animated: true)\n\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2\u003e敲黑板,划重点\u003c/h2\u003e\n\u003ch3\u003e实际效果和今后的计划\u003c/h3\u003e\n\u003cp\u003e目前已经接入了使用Flutter开发的设置页面和搜索节目单结果界面,具体两端的效果如下图所示:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://static.geekbang.org/infoq/5c661d7f517e9.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003ccenter\u003e iOS\u003c/center\u003e\n\u003cp\u003e\u003cimg src=\"https://static.geekbang.org/infoq/5c661dba0343d.png\" alt=\"\" /\u003e\u003c/p\u003e\n\u003ccenter\u003e Android\u003c/center\u003e\n\u003cp\u003e经过实际接入发现使用 Flutter 开发的界面的流畅度和原生开发的界面几乎没有区别,可以说是完全无缝的体验,使用 Flutter 开发部分独立性较强的页面还是没有任何问题的。\u003c/p\u003e\n\u003cp\u003eFlutter 目前还处于推广阶段,考虑到其各种优秀的特性,以后一定会发展的越来越好。开播助手后面还准备将更多页面接入 Flutter,先从部分列表页开始,并且维护一个 Flutter 的组件库,供今后页面开发使用,提高两端代码复用率,逐步实现一套代码双端运行的目的。\u003c/p\u003e\n\u003cp\u003e更多内容,请关注前端之巅。\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://static.geekbang.org/infoq/5c661e696678f.png\" alt=\"\" /\u003e\u003cbr /\u003e\n\u003cstrong\u003e会议推荐\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e2019年6月,GMTC全球大前端技术大会2019即将到来。小程序、Flutter、移动AI、工程化、性能优化…大前端的下一站在哪里?点击下图了解更多详情。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://gmtc.geekbang.org/?utm_source=infoq\u0026amp;utm_medium=bottombanner\"\u003e\u003cimg src=\"https://static.geekbang.org/infoq/5c6a6324955d6.png\" alt=\"\" /\u003e\u003c/a\u003e\u003c/p\u003e\n

你可能感兴趣的:(爱奇艺开播助手Flutter跨平台Hybrid实践\n)