愛奇藝開播助手
愛奇藝開播助手專案,又稱”直播機”,該專案標的是透過一個移動平臺為主播提供多樣化的直播內容。現階段所涵蓋的直播內容包括:遊戲直播,美女攝像直播,小劇場直播,其中游戲直播相對主播數量最多,3種推流樣式所涉及的推流SDK基本一致,推流邏輯存在部分差異。
該專案的Android端和iOS端架構類似,主要由APP、SDK和so三層構成,APP層負責介面展示和互動,由各端Native程式碼實現,so層負責封裝核心的推流、播放等功能,由於更接近底層硬體,使用C實現,而中間的SDK層負責呼叫這些so庫的功能。
由於雙端的業務幾乎完全一樣,雙端為了提高程式碼的復用率,我們試圖接入一套跨平臺的框架同時開發兩端的APP。
為什麼選擇Flutter
移動端跨平臺一直是開發者老生常談的話題,為了盡可能的增加程式碼復用,降低開發成本,各大科技巨頭都有自己的跨平臺框架,比如Facebook的React-Native、阿裡的Weex、Cordova等。這些跨平臺框架各有優劣,Google也“不甘寂寞”,在2018年Google開發者大會上重點介紹了自己的跨平臺框架Flutter。
和RN和Weex將javascript轉化為原生控制元件渲染不同,Flutter完全掙脫了原生控制元件的“束縛”,如圖1所示,Flutter使用了分層架構,分為Framework和Engine兩個部分,其中Framework層提供各種基礎元件庫,包括各種Widget,動畫等,Engine層則完全由C和C++實現,使用Skia進行渲染(對!就是chrome用的那個圖形渲染框架),官方宣稱可以達到原生app的渲染效能。
下圖是和RN、Weex之間的對比:
可以看到目前Flutter從各個方面都已經不遜於前兩位,而且在Google新作業系統Fuchsia(被認為是Android的繼任者)也使用Flutter作為其UI框架,今後的發展不可限量。
除了渲染效能之外,Flutter還有一個非常誘人的特性:HotReload,在debug下的Flutter工程可以快速熱多載到真機上,修改完程式碼後Ctrl+S就能實時展現在真機介面上,不需要重新安裝apk包,想想就興奮!
如果你對HotReload原理感興趣,可以移步Flutter官網進一步瞭解 HotReload
總體來看,Flutter有效能好、開發效率高、跨平臺和可無縫接入原有工程等優勢,所以我們嘗試使用Flutter進行開播助手的改造實踐。
開播助手Android端接入
下麵詳細介紹一下Android和iOS是如何接入的。
在Android中新增Flutter元件
目前開播助手中使用了Flutter的Fragment和View兩種方式,如下麵兩段程式碼所示:
使用Flutter.createFragment()和Flutter.createView()兩個方法,這兩個方法可以傳回Flutter建立的供Android使用的Fragment和View,接下來和原生的Fragment和View使用方法就是一模一樣了。(是不是很簡單!)
-
使用Flutter Fragment
Flutter.createFragment("settings")
-
使用Flutter View
Flutter.createView(getActivity(), getLifecycle(), "settings");
當然為了告訴Flutter需要使用哪個介面,使用了路由的機制,建立fragment或view的時候需要傳入一個路由的字串,在Flutter工程中也需要使用此字串,程式碼如下:
void main() { runApp(_widgetForRoute(window.defaultRouteName));}
Widget _widgetForRoute(String route) {
switch (route) {case 'settings':return MaterialApp(home:
SettingsPage()); .... }}
在Flutter工程的入口處匹配傳入的字串,來決定實體化哪個頁面傳回。
使用Module接入
開發過程中我們可以使用Moudle依賴來接入,只需要在setting.gradle中新增以下程式碼即可:
setBinding(new Binding([gradle: this]))
evaluate(new File(settingsDir.parentFile,'flutter_liveshow/.android/include_flutter.groovy'))
使用aar接入
Android使用aar接入Flutter十分的簡單,只用下麵兩步就可以順利的將使用Flutter開發的介面接入原生的工程。
使用gradle工具打aar資源包
如果要Android可以使用Flutter的工程,可以將Flutter工程打成aar的包,如下圖所示,進入工程中的.android/目錄,使用./gradlew Flutter:assembleRelease即可。
將aar包加入工程並依賴
如下圖所示,首先將打好的release包放入libs目錄下
目前最新版本的Flutter在整合時需要將sdk中的icudtl.dat檔案放入資源目錄中一起打包,否則會出錯,官方正在修複此問題,相信不久就能解決。
開播助手iOS端接入
Podfile接入Flutter
flutter_application_path = '../flutter_liveshow/'
eval(File.read(File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')), binding)
eval(string [, binding [, filename [,lineno]]]) → obj
Evaluates 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.
新增完成後執行pod install。這段程式碼實際就是在Podfile中加入一段Flutter
所需要的指令碼。如果基於Flutter master channel開發,生成的podhelper.rb中會增加post_install hooks,如果專案中也使用該hooks,需要手動合併。所幸這個檔案只有在修改Flutter plugin依賴並執行Flutter package get之後才會重新生成。
Dart程式碼編譯設定
“TARGET APP -> Build Phases -> New Run Script Phase” 新增script phase填入下方程式碼
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed
“Build Settings -> Add User-Defined Setting” 新增 FLUTTER_ROOT 欄位。
接入Host App
-
AppDelegate.swift
import Flutter
import FlutterPluginRegistrant // Only if you have Flutter Plugins.
@UIApplicationMainclass AppDelegate: FlutterAppDelegate {
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
GeneratedPluginRegistrant.register(with: self);
......}
-
在App中接入Flutter開發的頁面
let flutterViewController = FlutterViewController()
flutterViewController.setInitialRoute("settings")
navigationController?.pushViewController(flutterViewController, animated: true)
實際效果和今後的計劃
目前已經接入了使用Flutter開發的設定頁面和搜尋節目單結果介面,具體兩端的效果如下圖所示:
iOS
Android
經過實際接入發現使用Flutter開發的介面的流暢度和原生開發的介面幾乎沒有區別,可以說是完全無縫的體驗,使用Flutter開發部分獨立性較強的頁面還是沒有任何問題的。
Flutter目前還處於推廣階段,考慮到其各種優秀的特性,以後一定會發展的越來越好。開播助手後面還準備將更多頁面接入Flutter,先從部分串列頁開始,並且維護一個Flutter的元件庫,供今後頁面開發使用,提高兩端程式碼復用率,逐步實現一套程式碼雙端執行的目的。