国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語(yǔ)言|JavaScript|易語(yǔ)言|vb.net|

服務(wù)器之家 - 編程語(yǔ)言 - 編程技術(shù) - Web前端工程師實(shí)現(xiàn)Native APP需求,用Flutter做可攻可守的混合開(kāi)發(fā)

Web前端工程師實(shí)現(xiàn)Native APP需求,用Flutter做可攻可守的混合開(kāi)發(fā)

2022-03-07 23:05王群,李東,偉國(guó) 編程技術(shù)

技術(shù)的發(fā)展使得作為一名Web前端工程師實(shí)現(xiàn)Native APP需求更加容易,可以考慮使用Flutter做可攻可守的混合開(kāi)發(fā),隨著業(yè)務(wù)的穩(wěn)定逐漸將穩(wěn)定業(yè)務(wù)原生化,借助 web 高性價(jià)比的開(kāi)發(fā)成本又不會(huì)因?yàn)殚_(kāi)發(fā)效率措施商機(jī)。

導(dǎo)讀:近些年來(lái)前端技術(shù)不斷推陳出新,使得web前端工程師開(kāi)發(fā)Native App的成本逐漸降低,跨端技術(shù)也使得應(yīng)用開(kāi)發(fā)成本大幅度降低。這種背景下,越來(lái)越多的web前端工程師團(tuán)隊(duì)正在逐漸嘗試獨(dú)立開(kāi)發(fā)App,使用各種綜合性方案做著性能與效率上的平衡。

開(kāi)發(fā)一款Native APP往往都是一件復(fù)雜的事情,現(xiàn)在市面上大多數(shù)的APP都不是單純使用某種技術(shù)研發(fā)而成,而是隨著項(xiàng)目需求的復(fù)雜程度、迭代效率、團(tuán)隊(duì)具體情況等諸多原因的影響逐步發(fā)展成為各種技術(shù)混合開(kāi)發(fā)的APP架構(gòu)。

技術(shù)無(wú)分好壞,適合自己團(tuán)隊(duì)的才是最合適的技術(shù)選型。啥意思?我來(lái)說(shuō)說(shuō)吧,比如你的團(tuán)隊(duì)幾乎都使用vue開(kāi)發(fā),你非要選擇使用React Native全員開(kāi)發(fā)APP,成本就很大也很難推動(dòng);又比如你的團(tuán)隊(duì)客戶端研發(fā)工程師很多,那就沒(méi)必要非得用React Native,特別不人道。這就好像這么多年來(lái),人們一直倡導(dǎo)通過(guò)優(yōu)化工具提升效率,而不是直接通過(guò)技術(shù)干掉那些人。

那么,什么是合適的技術(shù)選型?我覺(jué)得對(duì)于一個(gè)團(tuán)隊(duì),一方面能夠滿足需求優(yōu)化體驗(yàn),另一方面就是降本提效簡(jiǎn)單靈活。

為了幫助Web前端工程師實(shí)現(xiàn)APP需求理清楚知識(shí)脈絡(luò),本文將簡(jiǎn)單介紹開(kāi)發(fā)Native APP常用的技術(shù)方法,再介紹如何用flutter做可攻可守的混合開(kāi)發(fā)。

01 純?cè)_(kāi)發(fā)

移動(dòng)端APP能夠帶來(lái)很好的體驗(yàn),能夠利用很多系統(tǒng)能力加持使之密閉生態(tài)發(fā)展迅速。但是使用原生方式(Native)來(lái)開(kāi)發(fā) App,不僅要求分別針對(duì) iOS 和 Android 平臺(tái),使用不同的語(yǔ)言實(shí)現(xiàn)同樣的產(chǎn)品功能,還要對(duì)不同的終端設(shè)備和不同的操作系統(tǒng)進(jìn)行功能適配,并承擔(dān)由此帶來(lái)的測(cè)試維護(hù)升級(jí)工作。

Web前端工程師實(shí)現(xiàn)Native APP需求,用Flutter做可攻可守的混合開(kāi)發(fā)

圖:原生開(kāi)發(fā)App語(yǔ)言

02 純跨端方案

為了減少開(kāi)發(fā)維護(hù)成本,近年來(lái)很多“一套代碼,多端運(yùn)行”的跨平臺(tái)方案猶如雨后春筍般的涌現(xiàn)出來(lái),跨端方案針對(duì)不同業(yè)務(wù)目標(biāo),有Native APP的跨端,有小程序的跨端,也有橫向的PC端移動(dòng)端和web的跨端,這些技術(shù)方案都有各自的優(yōu)缺點(diǎn),其中 React Native 和 Flutter 都是非常具有代表性的技術(shù)方案。

Web前端工程師實(shí)現(xiàn)Native APP需求,用Flutter做可攻可守的混合開(kāi)發(fā)

圖:跨端視圖效果

1、React Native

RN 希望開(kāi)發(fā)者能夠在性能、展示、交互能力和迭代交付效率之間做到平衡。它在 Web 容器方案的基礎(chǔ)上,優(yōu)化了加載、解析和渲染這三大過(guò)程,以相對(duì)簡(jiǎn)單的方式支持了構(gòu)建移動(dòng)端頁(yè)面必要的 Web 標(biāo)準(zhǔn),保證了便捷的前端開(kāi)發(fā)體驗(yàn)。并且在保留基本渲染能力的基礎(chǔ)上,用原生自帶的 UI 組件實(shí)現(xiàn)代替了核心的渲染引擎,從而保證了良好的渲染性能。

Web前端工程師實(shí)現(xiàn)Native APP需求,用Flutter做可攻可守的混合開(kāi)發(fā)

圖:RN 原理圖

但是,由于 React Native 的技術(shù)方案所限,使用原生控件承載界面渲染,在犧牲了部分 Web 標(biāo)準(zhǔn)靈活性的同時(shí),固然解決了不少性能問(wèn)題,但也引入了新的問(wèn)題。除開(kāi)通過(guò) JavaScript 虛擬機(jī)進(jìn)行原生接口的調(diào)用,JavaScript只能解釋執(zhí)行,而帶來(lái)的通信低效不談,由于框架本身不負(fù)責(zé)渲染,而是由原生代理,因此我們還需要面對(duì)大量平臺(tái)相關(guān)的邏輯。要用好 React Native,除了掌握這個(gè)框架外,開(kāi)發(fā)者還必須同時(shí)熟悉 iOS 和 Android 系統(tǒng)。

2、Flutter

在 Google 的強(qiáng)力帶動(dòng)下,F(xiàn)lutter 開(kāi)辟了全新的思路,提供了一整套從底層渲染邏輯到上層開(kāi)發(fā)語(yǔ)言的完整解決方案。視圖渲染完全閉環(huán)在其框架內(nèi)部,一切皆widget,不依賴于底層操作系統(tǒng)提供的任何組件,依靠底層圖像渲染引擎 Skia從根本上保證了視圖渲染在 Android 和 iOS 上的高度一致性。

Web前端工程師實(shí)現(xiàn)Native APP需求,用Flutter做可攻可守的混合開(kāi)發(fā)

圖:Flutter 架構(gòu)圖

Flutter 的開(kāi)發(fā)語(yǔ)言 Dart,是 Google 專門為大前端開(kāi)發(fā)量身打造的專屬語(yǔ)言,借助于先進(jìn)的工具鏈和編譯器,成為了少數(shù)同時(shí)支持JIT和AOT的語(yǔ)言之一,開(kāi)發(fā)期調(diào)試效率高,發(fā)布期運(yùn)行速度快、執(zhí)行性能好,在代碼執(zhí)行效率上可以媲美原生 App。Dart 避免了搶占式調(diào)度和共享內(nèi)存,可以在沒(méi)有鎖的情況下進(jìn)行對(duì)象分配和垃圾回收,在性能方面表現(xiàn)相當(dāng)不錯(cuò)。雖然 Dart 開(kāi)發(fā)語(yǔ)言有一定的學(xué)習(xí)成本,但是學(xué)習(xí)成本并不高,很容易上手。

2022年3月 Flutter 2 發(fā)布,聲稱使用相同的代碼庫(kù)為 iOS、Android、Windows、macOS 和 Linux 五種操作系統(tǒng)構(gòu)建原生應(yīng)用,甚至可以嵌入到汽車、電視和智能家電,為環(huán)境計(jì)算提供最普適、可移植的體驗(yàn)。

Web前端工程師實(shí)現(xiàn)Native APP需求,用Flutter做可攻可守的混合開(kāi)發(fā)

圖:跨端方案的對(duì)比圖

從 Web 容器時(shí)代到以 React Native 為代表的泛 Web 容器時(shí)代,最后再到以 Flutter 為代表的自繪引擎時(shí)代,這些優(yōu)秀的跨平臺(tái)開(kāi)發(fā)框架們慢慢抹平了各個(gè)平臺(tái)的差異,使得操作系統(tǒng)的邊界變得越來(lái)越模糊。

03 混合開(kāi)發(fā)

混合開(kāi)發(fā)也稱為Hybird開(kāi)發(fā)方式,是一種非常高效的APP開(kāi)發(fā)方式,主要通過(guò)多種技術(shù)共同研發(fā)同一個(gè)APP應(yīng)用,讓專業(yè)的人做專業(yè)的部分,在技術(shù)、效率、能力、目標(biāo)之間做平衡,達(dá)到核心部分體驗(yàn)優(yōu)秀、部分功能迭代迅速成本低的開(kāi)發(fā)方案。目前大多數(shù)APP也都是采用的多技術(shù)共用的開(kāi)發(fā)方式,比如淘寶、美團(tuán)、百度等APP都有混合開(kāi)發(fā)的影子。

下面介紹幾種常見(jiàn)的混合開(kāi)發(fā)方案。

1、原生 + webview 方案

這個(gè)方案主要是通過(guò)原生開(kāi)發(fā)出Native APP的外殼,內(nèi)部的頁(yè)面和功能主要通過(guò)常規(guī)的web技術(shù)實(shí)現(xiàn),系統(tǒng)端能力通過(guò)原生殼子暴露出API給web實(shí)現(xiàn)能力調(diào)用。

這種技術(shù)對(duì)于不同的業(yè)務(wù)側(cè)重點(diǎn)會(huì)有些不同,如果客戶端主導(dǎo)的業(yè)務(wù)native會(huì)重一些,如果是純web前端的團(tuán)隊(duì)web實(shí)現(xiàn)會(huì)非常多。這個(gè)主要是根據(jù)團(tuán)隊(duì)現(xiàn)狀和業(yè)務(wù)需要做實(shí)現(xiàn)上的調(diào)整,如果一個(gè)部分非常可能會(huì)成為未來(lái)核心業(yè)務(wù)則大概率會(huì)使用Native及時(shí)實(shí)現(xiàn)或者重構(gòu)。

Web前端工程師實(shí)現(xiàn)Native APP需求,用Flutter做可攻可守的混合開(kāi)發(fā)

圖:原生 + Webview 方案

2、原生 + Flutter方案

原生APP的開(kāi)發(fā)成本是非常高的,就像前文所說(shuō)的同一個(gè)APP需要分別針對(duì) iOS 和 Android 平臺(tái)進(jìn)行開(kāi)發(fā),也就是說(shuō)有幾種平臺(tái)就需要幾個(gè)技術(shù)方向的人力,這樣的產(chǎn)品實(shí)現(xiàn)是非常消耗人力的。所以人們一直在尋找合適的方案一次開(kāi)發(fā)就能夠運(yùn)行在不同的平臺(tái)上,F(xiàn)lutter 就是 Google 開(kāi)發(fā)的優(yōu)秀的跨端開(kāi)發(fā)Native APP技術(shù)方案,通過(guò)使用統(tǒng)一的編程語(yǔ)言 Dart ,根據(jù)一切皆 widget 的結(jié)構(gòu)組織,通過(guò)觸發(fā)自研渲染引擎Skia在系統(tǒng)應(yīng)用中繪制頁(yè)面,能夠達(dá)到無(wú)差異的渲染效果。

無(wú)差異的繪制UI和交互表現(xiàn),這也是Flutter最強(qiáng)大的地方,對(duì)于UI視圖的渲染和交互體驗(yàn)基本上與原生開(kāi)發(fā)的表現(xiàn)差距不大,但是能夠節(jié)省大約1 / 2的人力,這樣的降本提效方法十分受到歡迎。

Web前端工程師實(shí)現(xiàn)Native APP需求,用Flutter做可攻可守的混合開(kāi)發(fā)

圖:原生 + Flutter 通信交互圖

Web前端工程師實(shí)現(xiàn)Native APP需求,用Flutter做可攻可守的混合開(kāi)發(fā)

圖:Flutter原生混編

當(dāng)然,不同項(xiàng)目的面臨的情況也不同,我們主要介紹的是以flutter為主的開(kāi)發(fā)結(jié)構(gòu),但是很多業(yè)務(wù)原來(lái)使用原生開(kāi)發(fā)的,后來(lái)為了實(shí)現(xiàn)一些低成本的需求開(kāi)發(fā),所以在原生開(kāi)發(fā)的結(jié)構(gòu)上接入 Flutter 技術(shù)進(jìn)行混編開(kāi)發(fā)。

3、原生 + Flutter + Webview 方案

如果團(tuán)隊(duì)中有很多易變的需求,或者說(shuō)團(tuán)隊(duì)中純web前端成員占據(jù)大多數(shù),那么引入 webview 是一個(gè)不錯(cuò)的方案,雖然 webview 也存在很多問(wèn)題,但是相對(duì)來(lái)說(shuō)比起 RN 還是容易定位和解決的。

Web前端工程師實(shí)現(xiàn)Native APP需求,用Flutter做可攻可守的混合開(kāi)發(fā)

圖:原生 + Flutter + Webview

常見(jiàn)webview插件依賴于Flatter嵌入Android和 iOS本機(jī)視圖的機(jī)制,底層使用AndroidView和UiKitView。iOS12版本已經(jīng)廢棄UIWebView強(qiáng)推WKWebView,WKWebView 在獨(dú)立進(jìn)程中加載網(wǎng)頁(yè)。其中 webview_flutter 是官方維護(hù)的 WebView插件,特性是基于原生和 Flutter SDK封裝,繼承 StatefulWidget,因此支持內(nèi)嵌于 flutter Widget 樹(shù)中,這是比較靈活的。

Web前端工程師實(shí)現(xiàn)Native APP需求,用Flutter做可攻可守的混合開(kāi)發(fā)

圖:Flutter Webview插件對(duì)比

04 用Flutter做混合開(kāi)發(fā)的經(jīng)驗(yàn)

我們的項(xiàng)目包括APP必備的開(kāi)屏,然后是登陸頁(yè)面,登錄成功后是加載頁(yè)面,然后打開(kāi)首頁(yè),在首頁(yè)可以打開(kāi)直播浮窗,跳轉(zhuǎn)到大多數(shù)頁(yè)面直播浮窗都懸浮在那里,包括首頁(yè)和其他頁(yè)面都是迭代頻率非常高的。

Web前端工程師實(shí)現(xiàn)Native APP需求,用Flutter做可攻可守的混合開(kāi)發(fā)

圖:項(xiàng)目需求

假設(shè)我們有一個(gè) APP 項(xiàng)目,你的團(tuán)隊(duì)大部分人都是 vue 的深度依賴開(kāi)發(fā)者,項(xiàng)目初期很多需求可能都會(huì)不斷底推倒重來(lái),需求迭代頻率非常高,但是也需要一定程度的保證用戶體驗(yàn),并且支持多個(gè)平臺(tái)。這種背景下,使用 flutter + webview + 原生 的開(kāi)發(fā)方案確實(shí)能夠順利的完成任務(wù)。

那么,根據(jù)項(xiàng)目需求我們可以簡(jiǎn)單劃分各個(gè)部分設(shè)計(jì)。例如開(kāi)屏、登錄和加載頁(yè)面的變更頻率不高而且用戶體驗(yàn)要求平穩(wěn)順滑,這里可以采用 flutter 技術(shù)來(lái)實(shí)現(xiàn);對(duì)于1對(duì)1視頻通話部分可以考慮使用 RTC 和 webRTC 技術(shù),而原生方式接入 sdk 和實(shí)現(xiàn)需求的穩(wěn)定性和延時(shí)是比較可靠的,所以使用原生方式實(shí)現(xiàn);其他的頁(yè)面如果面臨業(yè)務(wù)初級(jí)階段、變化頻率高等情況,我們可以考慮使用 web 技術(shù)實(shí)現(xiàn)(H5),如果存在對(duì)于客戶端能力或者系統(tǒng)能力的調(diào)用,可以使用 Jsbridge 中間層實(shí)現(xiàn)。

Web前端工程師實(shí)現(xiàn)Native APP需求,用Flutter做可攻可守的混合開(kāi)發(fā)

圖:設(shè)計(jì)簡(jiǎn)圖

1、結(jié)構(gòu)實(shí)現(xiàn)

因?yàn)槭菑?0 到 1 的項(xiàng)目,我們選擇創(chuàng)建一個(gè)flutter項(xiàng)目,按照設(shè)計(jì)的結(jié)構(gòu)進(jìn)行研發(fā)。項(xiàng)目入口在 main.dart 中實(shí)現(xiàn);通過(guò) routers.dart 的業(yè)務(wù)邏輯實(shí)現(xiàn)頁(yè)面間的切換;具體的頁(yè)面實(shí)現(xiàn)在 pages 目錄中相應(yīng)的同名文件;UI 組件位于 widgets 中,可以通過(guò) import 進(jìn)行使用;熟悉客戶端研發(fā)的同學(xué)們會(huì)發(fā)現(xiàn),在flutter項(xiàng)目里有 Android 和 iOS 目錄,原生開(kāi)發(fā)在相應(yīng)的原生目錄中實(shí)現(xiàn)就可以;flutter擴(kuò)展插件可以通過(guò)引用放置到plugins目錄中,具體的配置在 pubspec.yaml 文件中實(shí)現(xiàn)。

app-project |----android // Android原生目錄 | |--gradle | |--build.gradle // Android配置文件 | |--key.jks | |--key.properties | |--app | |--libs | |--src | |--profile | | |--AndroidManifest.xml | |--main | |--kotlin | |--res | |--AndroidManifest.xml |----ios | |--Gemfile | |--Podfile // ios 的pod 依賴文件 | |--Flutter | |--scripts | | |--AppIcon.sh | | |--flutterbuild.sh | | |--setup.sh | |--Runner | |--Info.plist | |--main.m | |--Assets.xcassets | |--Base.lproj | |--AppDelegate.h | |--AppDelegate.m |----lib // Flutter 工作區(qū) | |--assets // 本地的資源工作區(qū) | |--config | |--jsBridge // 提供webview jsbrige | | |--live | | |--login | | |--cache | |--pages // flutter 頁(yè)面 | | |--live | | |--loading | | |--webViewPage | | |--login | |--routers | | |--routers.dart | |--utils | | |--cache.dart | | |--color.dart | | |--network.dart | | |--events.dart | |--widgets | | |--routers.dart | |--main.dart // 入口文件 |----plugins | |--flutter-inappwebview //webview庫(kù) | |--flutter-login |----test |----web |----pubspec.yaml // flutter 配置文件 

1.1 頁(yè)面實(shí)現(xiàn)

如果你有過(guò)原生系統(tǒng)(Android、iOS)或原生 JavaScript 開(kāi)發(fā)經(jīng)驗(yàn)的話,應(yīng)該知道視圖開(kāi)發(fā)是命令式的,需要精確地告訴操作系統(tǒng)或?yàn)g覽器用何種方式去做事情。與此不同的是,F(xiàn)lutter 的視圖開(kāi)發(fā)是聲明式的,其核心設(shè)計(jì)思想就是將視圖和數(shù)據(jù)分離,這與 React 的設(shè)計(jì)思路完全一致。例如代碼實(shí)現(xiàn)如下:

// 頁(yè)面實(shí)現(xiàn)事例 import 'package:flutter/widgets.dart'; class MyAPP extends StatelessWidget { @override Widget build(BuildContext context) { return const Center(child: Text('Hello Qun'));
  }
} void main() => runApp(new MyAPP());

當(dāng)你所要構(gòu)建的用戶界面不隨任何狀態(tài)信息的變化而變化時(shí),需要選擇使用 StatelessWidget,反之則選用 StatefulWidget。 渲染也非常有意思,Widget 是 Flutter 世界里對(duì)視圖的一種結(jié)構(gòu)化描述,里面存儲(chǔ)的是有關(guān)視圖渲染的配置信息; Element 則是 Widget 的一個(gè)實(shí)例化對(duì)象,將 Widget 樹(shù)的變化做了抽象,能夠做到只將真正需要修改的部分同步到真實(shí)的 Render Object 樹(shù)中,最大程度地優(yōu)化了從結(jié)構(gòu)化的配置信息到完成最終渲染的過(guò)程; 而 RenderObject,則負(fù)責(zé)實(shí)現(xiàn)視圖的最終呈現(xiàn),通過(guò)布局、繪制完成界面的展示。

Web前端工程師實(shí)現(xiàn)Native APP需求,用Flutter做可攻可守的混合開(kāi)發(fā)

圖:界面生成的“三棵樹(shù)”

Dart 是單線程的,這意味著代碼是有序的,按照在 main 函數(shù)出現(xiàn)的次序一個(gè)接一個(gè)地執(zhí)行,不會(huì)被其他代碼中斷。關(guān)于組件層面的原始指針事件的監(jiān)聽(tīng),F(xiàn)lutter 提供了 Listener Widget,可以監(jiān)聽(tīng)其子 Widget 的原始指針事件。

// 事件響應(yīng) Listener( child: Container( color: Colors.yellow,// 背景色 width: 700, height: 700,
  ), // 手勢(shì)按下回調(diào) onPointerDown: (event) => print("down $event"), // 手勢(shì)移動(dòng)回調(diào) onPointerMove:  (event) => print("move $event"), // 手勢(shì)抬起回調(diào) onPointerUp:  (event) => print("up $event")
);

1.2 路由設(shè)置

如果說(shuō) UI 框架的視圖元素的基本單位是組件,那應(yīng)用程序的基本單位就是頁(yè)面了。對(duì)于擁有多個(gè)頁(yè)面的應(yīng)用程序而言,需要有一個(gè)統(tǒng)一的機(jī)制來(lái)管理頁(yè)面之間的跳轉(zhuǎn),通常被稱為路由管理或?qū)Ш焦芾怼?

在 Flutter 中,頁(yè)面之間的跳轉(zhuǎn)是通過(guò) Route 和 Navigator 來(lái)管理的。根據(jù)是否需要提前注冊(cè)頁(yè)面標(biāo)識(shí)符,F(xiàn)lutter 中的路由管理可以分為兩種方式:

  • 基本路由:無(wú)需提前注冊(cè),在頁(yè)面切換時(shí)需要自己構(gòu)造頁(yè)面實(shí)例。
// 基本路由 class FirstPage extends StatelessWidget { @override Widget build(BuildContext context) { return RaisedButton( // 打開(kāi)頁(yè)面 onPressed: ()=> Navigator.push(context, MaterialPageRoute( builder: (context) => SecondPage()
      ));
    );
  }
} class SecondPage extends StatelessWidget { @override Widget build(BuildContext context) { return RaisedButton( // 回退頁(yè)面 onPressed: ()=> Navigator.pop(context)
    );
  }
}
  • 命名路由:需要提前注冊(cè)頁(yè)面標(biāo)識(shí)符,在頁(yè)面切換時(shí)通過(guò)標(biāo)識(shí)符直接打開(kāi)新的路由。
// 命名路由 MaterialApp( ... // 注冊(cè)路由 routes:{ "uri_page":(context)=>UriPage(),
    },
); // 使用名字打開(kāi)頁(yè)面 Navigator.pushNamed(context,"uri_page");

1.3 插件引用

依賴庫(kù)的依賴是通過(guò)pubspec.xml中配置完成。

// 版本配置,iOS最終是以identify中的版本配置為準(zhǔn) version: 1.0.30+1 // flutter sdk環(huán)境配置 environment: sdk: ">=2.12.0-0 <3.0.0" flutter: ">=1.22.2" // 依賴配置  dependencies: flutter: sdk: flutter permission_handler: ^7.1.0 flutter_inappwebview: path: ./plugins/flutter_inappwebview joymo_app_upgrade: path: ./plugins/joymo_app_upgrade tal_login: path: ./plugins/tal_login student_101_live: path: ./plugins/student_101_live dev_dependencies: flutter_test: sdk: flutter 

1.4 原生接入

開(kāi)發(fā)過(guò)程不免需要原生底層能力的支持,上文已經(jīng)介紹了使用platform channel方式完成對(duì)接,這里用代碼示例下(原生的直播能力如何對(duì)接,以單獨(dú)的插件StudentLive為例子)。

// 定義一個(gè) flutter 側(cè)的 名稱為"student_101_live"的MethodChannel  static const MethodChannel _channel = const MethodChannel('student_101_live'); // 展示原生端直播UI static Future<void> showLive(Map<String,dynamic>? map) async { try{ // 調(diào)用原生方法名為:"showLiveDialog"方法 await _channel.invokeMethod('showLiveDialog',map);
  }on MissingPluginException catch(e){ print('MissingPluginException, please check plugin has been registered or not,error message:${e.message}');
  }on PlatformException catch(e){ print('invoke method log---showLiveDialog failed,error message:${e.message}');
  }
}

iOS側(cè)的代碼,每個(gè)plugin自動(dòng)編譯會(huì)生成一個(gè)對(duì)應(yīng)的Student101LivePlugin類:

// 這個(gè)plugin類是模版編譯生成,繼承自FlutterPlugin @implementation Student101LivePlugin + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar { // 定義一個(gè)和flutter側(cè)相同名稱的"student_101_live" 的FlutterMethodChannel  FlutterMethodChannel* channel = [FlutterMethodChannel methodChannelWithName:@"student_101_live" binaryMessenger:[registrar messenger]]; Student101LivePlugin* instance = [[Student101LivePlugin alloc] init]; // 設(shè)置一個(gè)對(duì)應(yīng)dart的methodDelegate [registrar addMethodCallDelegate:instance channel:channel];
    
} - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { // 判斷是否和flutter側(cè)的方法對(duì)應(yīng)起來(lái)  if ([@"showLiveDialog" isEqualToString:call.method]) { // 這里method delegate的處理,完成原生方法的調(diào)用 if (call.arguments != nil) {
            [self initLiveView:call.arguments];
        } result(@"iOS showLiveDialog");
    }else { result(FlutterMethodNotImplemented);
    }
}

上面就已經(jīng)完成了原生的接入,那這個(gè)studen101LivePlugin 如何完成注冊(cè)的呢,系統(tǒng)已經(jīng)幫忙做了,查看ios/Runner/GeneratedPluginRegistrant.m,可以看到依賴的插件都被注冊(cè),這樣你只用關(guān)注業(yè)務(wù)代碼的編寫即可。

@implementation GeneratedPluginRegistrant + (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
  [Student101LivePlugin registerWithRegistrar:[registry registrarForPlugin:@"Student101LivePlugin"]];
  [FLTURLLauncherPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTURLLauncherPlugin"]];
}

總結(jié)一下: 在fluttter 側(cè)和原生側(cè)分別創(chuàng)建一個(gè)名稱相同的channel,然后再根據(jù)channel下的各個(gè)方法完成對(duì)接的調(diào)用,是不是很簡(jiǎn)單。

1.5 web接入

在 APP 中使用H5進(jìn)行頁(yè)面編寫的方案往往包括 web 資源離線方案和 server資源方案,這兩種方案最大的區(qū)別就是前者的靜態(tài)資源已經(jīng)下發(fā)到APP中可以離線使用,后者需要通過(guò)向 web 服務(wù)端請(qǐng)求資源返回后通過(guò)webview進(jìn)行渲染展現(xiàn)。

1.5.1 web 資源離線方案

通過(guò)為 webview 指定訪問(wèn)網(wǎng)站的 URL ,通過(guò)向服務(wù)端請(qǐng)求頁(yè)面進(jìn)行渲染。

1.5.2 service資源方案

離線方案往往與在線方案相結(jié)合,能夠提供更好的用戶體驗(yàn)和也不需要服務(wù)端承載大量的訪問(wèn)壓力。

Web前端工程師實(shí)現(xiàn)Native APP需求,用Flutter做可攻可守的混合開(kāi)發(fā)

圖:離線化web方案

前端應(yīng)用(Web)保持原有的開(kāi)發(fā)部署流程,僅通過(guò)webpack plugin修改打包流程,建立與配置平臺(tái)的聯(lián)系,并生成壓縮文件,上傳到CDN。這就讓現(xiàn)有的H5應(yīng)用方便的具備離線包的能力,后續(xù)新開(kāi)發(fā)的離線應(yīng)用也可以同時(shí)具備靜態(tài)發(fā)布的能力。

離線包配置平臺(tái)(Configuration Platform)用于管理各個(gè)離線包應(yīng)用,包括對(duì)應(yīng)用的增刪改查,版本管理,配置查看,設(shè)置檢查更新的url等功能,由webpack plugin生成的配置會(huì)通過(guò)接口入庫(kù)。每次版本更新都會(huì)存儲(chǔ)下來(lái),通過(guò)上線操作生效。

APP后端(APP Server)主要提供APP側(cè)離線包配置查詢的接口,也包含ios和android離線包開(kāi)啟與否的開(kāi)關(guān),在配置平臺(tái)設(shè)置生效的離線包應(yīng)用會(huì)以列表的形式提供給APP使用。

客戶端(APP)通過(guò)接口獲取離線包清單,如果某個(gè)應(yīng)用版本需要更新,先將本地包資源刪除,再進(jìn)行更新。通過(guò)整體考量,目前我們沒(méi)有做增量更新和差量包的維護(hù),為了 減少應(yīng)用體積過(guò)大帶來(lái)的更新問(wèn)題,我們提供分包的配置,可以分步實(shí)現(xiàn)離線化。在開(kāi)啟離線包功能的WebView中,對(duì)所有資源請(qǐng)求進(jìn)行反向代理,如果資源在本地有緩存,走本地緩存,沒(méi)有則請(qǐng)求在線資源。

1.6 開(kāi)屏配置

Flutter 開(kāi)屏頁(yè)面(也稱為啟動(dòng)頁(yè))也是在各家平臺(tái)上自己設(shè)置的,iOS 和 Android 都不相同。

iOS:由于 iOS 必須使用 Xcode storyboard 提供應(yīng)用啟動(dòng)頁(yè)面,在項(xiàng)目根項(xiàng)目下執(zhí)行 open ios/Runner.xcworkspace 打開(kāi)Flutter應(yīng)用程序的Xcode項(xiàng)目,然后選擇 Runner/Assets.xcassets,將需要的啟動(dòng)圖拖到 LaunchImage 圖像集中即可。

Android:因 Android 啟動(dòng)頁(yè)面是個(gè) AndroidView,同時(shí)Flutter第一幀也在繪制,這時(shí)候兩者之間會(huì)有空隙。Flutter 2.5之后做了優(yōu)化(將Android啟動(dòng)頁(yè)保持到Flutter的第一幀渲染完成),也改變了啟動(dòng)頁(yè)面的設(shè)置方式。簡(jiǎn)單介紹下2.5以后的設(shè)置方式,在Android AndroidManifest.xml配置

<activity android:name=".MyActivity" // 1、配置啟動(dòng)的style android:theme="@style/LaunchTheme" // ... > <meta-data // 2、配置普通的style,系統(tǒng)會(huì)從啟動(dòng)style直接過(guò)渡到這個(gè)style android:name="io.flutter.embedding.android.NormalTheme" android:resource="@style/NormalTheme" /> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> 

2、編譯發(fā)布

Flutter 雖然實(shí)現(xiàn)多端的 UI 統(tǒng)一,最終的發(fā)布還是交給了各家平臺(tái)發(fā)布流程。

Android

通常的流程如下:全程通過(guò)Android studio完成。

Flutter clean 清理自動(dòng)編譯生成的緩存文件,避免因?yàn)榫彺嫖募?dǎo)致的編譯錯(cuò)誤。

Flutter clean =========執(zhí)行輸出========= Cleaning Xcode workspace... 4.1s Deleting build... 1,116ms Deleting .dart_tool... 6ms Deleting .packages... 0ms Deleting Generated.xcconfig... 1ms Deleting flutter_export_environment.sh... 0ms Deleting .flutter-plugins-dependencies... 0ms Deleting .flutter-plugins... 0ms 

Flutter pub get 拉取工程依賴庫(kù)。

Flutter pub get =========部分執(zhí)行輸出========= Running "flutter pub get" in studentflutter... executing: [/Users/xx/talworkproject/xx/studentflutter/] /Users/xx/talworkproject/xx/fluttersdk/bin/cache/dart-sdk/bin/pub --verbose get --no-precompile FINE: Pub 2.14.4 MSG : Resolving dependencies... SLVR: fact: app_student_flutter is 1.0.30+1 SLVR: derived: app_student_flutter SLVR: fact: app_student_flutter depends on flutter any from sdk SLVR: fact: app_student_flutter depends on permission_handler ^7.1.0 SLVR: fact: app_student_flutter depends on limiting_direction_csx ^0.1.0 SLVR: fact: app_student_flutter depends on url_launcher ^6.0.0-nullsafety.4 SLVR: fact: app_student_flutter depends on package_info ^2.0.0 SLVR: fact: app_student_flutter depends on shared_preferences ^2.0.5 SLVR: fact: app_student_flutter depends on connectivity_plus ^1.0.2 SLVR: fact: app_student_flutter depends on dio ^4.0.0 SLVR: fact: app_student_flutter depends on app_settings ^4.1.0 SLVR: fact: app_student_flutter depends on fluttertoast ^8.0.7 

Flutter build apk 最終產(chǎn)生一個(gè)可以發(fā)布的APK文件,發(fā)布出去。

Flutter build apk --release/--debug 

iOS

通常流程如下:需要Xcode+Android studio/VScode完成,前兩步同Android 。

Flutter clean Flutter pub get 

Flutter build ipa 生成一個(gè)構(gòu)建歸檔。

Flutter build ipa --release/--debug/--profile 

后面的操作和純?cè)?iOS 發(fā)布流程相同,使用 xcode 完成歸檔校驗(yàn),upload to App store等。

平臺(tái)編譯流程

flutter 雖屏蔽了平臺(tái)的編譯流程,但是依然脫離不了各平臺(tái)編譯流程,那它是如何做到呢?因篇幅有限以 Android Fluttter(純Flutter項(xiàng)目) 編譯幾個(gè)重要編譯步驟來(lái)說(shuō)明:

Web前端工程師實(shí)現(xiàn)Native APP需求,用Flutter做可攻可守的混合開(kāi)發(fā)

圖:Flutter在Android上的編譯

App模塊下的 build.gradle,這個(gè)是安卓的編譯配置文件,承載編譯的重點(diǎn)內(nèi)容。

...省略 apply plugin: FlutterPlugin class FlutterPlugin implements Plugin<Project> { // ...... // 重點(diǎn)入口 @Override void apply(Project project) { //...... project.extensions.create("flutter", FlutterExtension) // 重點(diǎn):添加flutter構(gòu)建相關(guān)的各種task this.addFlutterTasks(project) //...... // flutter shell 命令 String flutterExecutableName = Os.isFamily(Os.FAMILY_WINDOWS) ? "flutter.bat" : "flutter" flutterExecutable = Paths.get(flutterRoot.absolutePath, "bin", flutterExecutableName).toFile(); String flutterProguardRules = Paths.get(flutterRoot.absolutePath, "packages", "flutter_tools", "gradle", "flutter_proguard_rules.pro") // 給所有的buildtypes 添加依賴  project.android.buildTypes.all this.&addFlutterDependencies } /**  * Adds the dependencies required by the Flutter project.  * This includes:  *    1. The embedding  *    2. libflutter.so  */ // 重要,看上面注釋就可以看出主要是The embedding和libflutter.so void addFlutterDependencies(buildType) { String flutterBuildMode = buildModeFor(buildType) //..... platforms.each { platform -> String arch = PLATFORM_ARCH_MAP[platform].replace("-", "_") // Add the `libflutter.so` dependency. addApiDependencies(project, buildType.name, "io.flutter:${arch}_$flutterBuildMode:$engineVersion")
        }
    } //...... // 重點(diǎn):整個(gè)編譯過(guò)程中的重點(diǎn)和難點(diǎn),最終是產(chǎn)出flutter層的產(chǎn)物 app.so和libs.jar,篇幅有限 private void addFlutterTasks(Project project) { //...... String taskName = toCammelCase(["compile", FLUTTER_BUILD_PREFIX, variant.name]) FlutterTask compileTask = project.tasks.create(name: taskName, type: FlutterTask) { //...... } // 中間產(chǎn)物lib.jar File libJar = project.file("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter/${variant.name}/libs.jar") Task packFlutterAppAotTask = project.tasks.create(name: "packLibs${FLUTTER_BUILD_PREFIX}${variant.name.capitalize()}", type: Jar) { destinationDir libJar.parentFile archiveName libJar.name dependsOn compileTask targetPlatforms.each { targetPlatform -> String abi = PLATFORM_ARCH_MAP[targetPlatform] from("${compileTask.intermediateDir}/${abi}") { include "*.so" // Move `app.so` to `lib//libapp.so` rename { String filename -> return "lib/${abi}/lib${filename}" }
                    }
                }
            } if (isFlutterAppProject()) { project.android.applicationVariants.all { variant -> // assemble task任務(wù),最終走到 Android assemble 任務(wù)中 Task assembleTask = getAssembleTask(variant) Task copyFlutterAssetsTask = addFlutterDeps(variant) def variantOutput = variant.outputs.first() def processResources = variantOutput.hasProperty("processResourcesProvider") ? variantOutput.processResourcesProvider.get() : variantOutput.processResources processResources.dependsOn(copyFlutterAssetsTask) // Copy the output APKs into a known location, so `flutter run` or `flutter build apk` // flutter build apk的歸檔 variant.outputs.all { output -> assembleTask.doLast { // ....... project.copy { from new File("$outputDirectoryStr/${output.outputFileName}") into new File("${project.buildDir}/outputs/flutter-apk"); rename { return "${filename}.apk" }
                        }
                    }
                }
            } // ... } // ... }
}

flutter shell -> flutter dart command -> flutter 中的 gradle 腳本(最終和平臺(tái)編譯方式結(jié)合起來(lái))-> flutter shell 將各種編譯腳本鏈路化。

常見(jiàn)問(wèn)題

  • 亂用動(dòng)畫問(wèn)題: 不要亂用 Animation 動(dòng)畫,這個(gè)動(dòng)畫占用主線程資源,如果一直高頻觸發(fā)會(huì)搶占資源影響其他代碼邏輯的執(zhí)行。
  • 交互沖突問(wèn)題:在屏幕假死的現(xiàn)象觸發(fā)后,要檢查原生、flutter和web事件之間是否沖突,這里面的邏輯關(guān)系需要搞清楚。
  • 聯(lián)調(diào)問(wèn)題:在聯(lián)調(diào)階段,web、flutter和原生之間需要考慮經(jīng)常切換環(huán)境的開(kāi)發(fā)場(chǎng)景。
  • Flutter sdk版本問(wèn)題:統(tǒng)一Flutter sdk 環(huán)境避免在不同開(kāi)發(fā)者中產(chǎn)生一些因?yàn)榄h(huán)境配置的問(wèn)題。

05 總結(jié)

技術(shù)的發(fā)展使得作為一名Web前端工程師實(shí)現(xiàn)Native APP需求更加容易,可以考慮使用flutter做可攻可守的混合開(kāi)發(fā),隨著業(yè)務(wù)的穩(wěn)定逐漸將穩(wěn)定業(yè)務(wù)原生化,借助 web 高性價(jià)比的開(kāi)發(fā)成本又不會(huì)因?yàn)殚_(kāi)發(fā)效率措施商機(jī)。整個(gè)混合開(kāi)發(fā)的全鏈路還是比較長(zhǎng)的,從各種技術(shù)的功能開(kāi)發(fā)、聯(lián)調(diào)、編譯和發(fā)布都會(huì)積累非常多的實(shí)戰(zhàn)經(jīng)驗(yàn),這份經(jīng)歷也是非常寶貴的。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 激情综合色综合久久综合 | 欧美成人激情视频 | 久久亚洲一区 | 中文字幕,久热精品,视频在线 | 久久久性色精品国产免费观看 | 亚洲精品电影在线一区 | 午夜天堂精品久久久久 | 亚洲一区成人在线观看 | 国产精品视频播放 | 中文字幕视频在线 | 日韩免费在线观看视频 | 国产精品久久久久aaaa | 亚洲乱码国产乱码精品精的特点 | 欧美日韩国产中文 | 精品欧美乱码久久久久久 | 天堂资源 | 久久婷婷av | 国内毛片毛片 | 中文字幕一区二区三 | 国产一区二区视频在线观看 | 麻豆乱码国产一区二区三区 | 日本www视频 | 福利片在线免费观看 | 午夜视频福利在线观看 | 伦理午夜电影免费观看 | 亚洲精品久久久久久久久久久久久 | 日本一本视频 | 天天澡天天狠天天天做 | 国产四区视频 | 精品免费 | 91精品久久 | 性网站在线 | 美日韩一区二区 | 亚洲成人久久久 | 91久久精品一区 | 国产尤物一区 | 日韩在线观看第一页 | 国产一区二区免费 | 国内精品久久久久久中文字幕 | 精品国产乱码久久久久久影片 | 欧美一级特黄aaaaaa大片在线观看 |