精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

攜程酒店Flutter性能優化實踐

開發
攜程酒店業務使用Flutter技術開發的時間快接近兩年,這期間有列表頁、詳情頁、相冊頁等頁面使用了Flutter技術棧進行了跨平臺整合,大大提高了研發效率。

作者簡介 | Qifan,攜程高級工程師,專注移動端開發;Yinuo,攜程高級工程師,專注移動端開發;popeye,攜程軟件技術專家,關注移動端跨端技術,致力于快速,高性能地支撐業務開發。

一 、前言

攜程酒店業務使用Flutter技術開發的時間快接近兩年,這期間有列表頁、詳情頁、相冊頁等頁面使用了Flutter技術棧進行了跨平臺整合,大大提高了研發效率。在開發過程中,也遇到了一些性能相關問題和用戶反饋,比如長列表滾動卡頓、頁面打開時間較長、頁面打開后部分數據加載時間較長等問題。為解決這些問題,我們選用了多個性能指標監控業務運行狀態,借助性能檢測工具定位問題,并查閱源碼、文檔等資源解決問題,形成了這篇文章。

同時在不斷的需求迭代和代碼更新過程中,APP的性能穩定性持續受到挑戰,為此我們建立了線上性能監控系統,通過量化,治理,監控三方面手段,持續改善APP性能和用戶體驗。目前頁面的各種性能指標諸如FPS、TTI、內存等都達到了不錯的效果,本文將介紹我們在優化過程中所遇到的問題和采取的主要優化方案。

二、FPS&TTI提升性能優化

2.1 常用性能指標和卡頓定義

對于客戶端應用來說,流暢度是影響用戶使用體驗的關鍵因素。流暢度低主要有:低FPS、高TTI、卡頓。這些現象出現時,頁面會出現不連續的動畫,頁面刷新會短暫停頓,打開新頁面速度較慢,新頁面出現白屏或者較長時間的加載動畫,用戶做點擊滑動等交互時頁面不響應。

用戶操作 FPS 的定義是每秒傳輸幀數 (Frames Per Second),是圖像領域的概念。對于手機客戶端來說,主流顯示屏的刷新率為60Hz,高端手機顯示屏刷新率可以達到120Hz及以上。理想情況下,頁面繪制的FPS和屏幕刷新率一致。屏幕畫面刷新次數越多,屏幕可以展示的動態細節越多,所以數值越高越好。TTI的定義是從頁面加載開始到頁面處于完全可交互狀態 (Time To Interactive),完全可交互狀態指的是頁面有內容呈現并且用戶可以進行操作。

2.2 FPS優化的工具介紹

Flutter官方提供了三種應用編譯選項,debug模式、release模式和profile模式。當我們需要做性能分析的時候,需要打包profile模式的應用,這個模式的性能接近release模式,并且有性能相關的信息分析。我們使用的工具是官方提供開發者工具中的Performance View,并選擇了Enhance tracing模式。

圖片

圖1 幀渲染時間柱狀圖

上圖是幀渲染時間,橫坐標是幀號,縱坐標是繪制時間,藍色代表該幀滿足60fps,橙色代表不滿足60fps。從這張圖可以快速定位到繪制時間較長的幀,而下圖是選中某幀之后,UI繪制和光柵化時間,如果選擇了Enhance tracing模式,可以看到耗時較長的方法、widget build。

前文已經介紹過FPS的定義,對于flutter繪制而言,每幀繪制耗時前三的是UI繪制時間、光柵化時間、vsync ahead。UI繪制時間主要是widget build、layout、paint,簡單認為是CPU時間;光柵化時間可以簡單認為是GPU時間;vsync ahead是vsync信號與widget build之間的延時。

圖片

圖2 Widget build耗時與對應執行的方法

2.3 具體實踐方案

a) 控制setState次數,使用Provider機制減小刷新范圍

我們的業務開發是MVVM結構的,數據驅動UI更新。UI的繪制占了性能開銷的很大部分,減少不必要的UI繪制、控制UI繪制的范圍這兩種方法能顯著改善性能。

減少不必要的UI繪制是通過控制build次數實現的。widget build是通過setState方法或者builder方法觸發的,在業務中,盡量減少非必要的setState,只有真正頁面數據發生變化,頁面狀態變化時才調用setState方法。對于builder方法,可以實現shouldRebuild等接口,增加觸發builder方法的限制。

控制UI繪制的范圍是通過改變widget樹層級實現的。MVVM中數據觸發UI更新的方式有很多,我們的業務主要用到了Provider機制,這是一種觀察者模式設計。如下圖所示,對于左邊的widget樹,如果只需要更新Container容器配置和Icon圖標配置,那么可以將selector拆分到這兩個widget的雙親widget,實現了Text widget不刷新。對于widget樹較大的業務,這樣的改動能顯著提升FPS。

圖片

圖3 Widget樹結構優化以減少build次數

b) 預構建widget (AnimatedBuilder)

圖片

圖4 酒店詳情頁頭部使用預構建減少build次數

上圖是酒店詳情頁頭部沉浸式動畫的UI,頭部展開的過程中,圖片和圖片上的蒙層需要重新繪制,圖片上部SHA logo不需要重新繪制,圖片下部tab欄不需要重新繪制,對于這個需求的做法是用AnimatedBuilder。

AnimatedBuilder提供了幾個可選參數,animation是對動畫的監聽,builder是動畫過程中需要重新繪制的部分,child是動畫過程中不需要重新繪制的部分,child作為參數會傳入builder中。下面的偽代碼是一個例子,動畫過程中Text并不會多次繪制。

@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
child: Container(
width: 200.0,
height: 200.0,
color: Colors.green,
child: const Center(
child: Text('Text!'),
),
),
builder: (BuildContext context, Widget child) {
return Transform.rotate(
angle: _controller.value * 2.0 * math.pi,
child: child,
);
},
);
}

對于詳情頁頭部沉浸式動畫的例子,可以把widget樹進行拆分,只有圖片和圖片蒙層放入builder方法中,其余的widget作為child傳入builder,同時用Stack widget實現兩部分UI的組合,這樣改進之后,FPS在動畫過程中有較大提升。

c) const widget

對于dart語法,需要分清楚final和const關鍵字的區別。關鍵字final的意思是一次賦值,不能改變;而關鍵字const的意思是常量,確定的值。這兩者的區別是final變量在第一次使用時被初始化,而const 變量是一個編譯時替換為常量值。同樣的,對于const widget,這個widget在編譯階段就已經確定,不會有狀態的變化和成員變量更新。const widget特別適合于標簽、特殊Icon等可以復用的UI,性能開銷較小。

d) 減少耗時計算,放到Isolate

Flutter應用中的Dart代碼執行在UI Runner中,而Dart是單線程的,我們平時使用的異步任務Future都是在這個單線程的Event Queue之中,通過Event Loop來按順序執行。需要避免將一些耗時計算放在UI線程,可以把耗時計算放到Isolate去執行。

e) 懶加載

能夠實現懶加載的有ListView.builder、PageView.builder和GridView.builder,這些widget可以用戶長列表或重復容器結構的UI,通過判斷單個item是否在屏幕內或者將要進入屏幕位置而進行繪制。與之對應的是Column、Row等一次性繪制widget,對于重復結構的數據,盡量避免使用這些組件。

如下圖中,酒店周邊景點美食購物列表和附近同類型酒店列表都實現了按需加載。酒店周邊景點美食購物列表的卡片數量超過20個,最初使用Row 組件構建時,第一次構建時間超過25ms,達不到60FPS的16ms繪制時間要求。當然,按需加載也有性能開銷,出現在列表的滑動過程中。如果一次性全部構建了列表,滑動過程中不會觸發新的構建,滑動流暢度體驗更好,但是第一次構建時的卡頓感明顯。

圖片

圖5 酒店詳情頁周邊內容運用懶加載減少構建次數

f) 分幀渲染

錯峰加載方案使用分幀渲染,分幀渲染的原理是將一棵Widget樹中的部分繪制時間較長的節點在第一幀時只占位不繪制,等到下一幀開始時,節點替換占位UI,單獨使用一幀時間繪制。

在酒店詳情頭部信息繪制中運用了分幀渲染技術,下左圖未使用分幀渲染,下右圖對圖片tab欄、酒店設施標簽、點評模塊、地址欄使用分幀渲染。從結果看,減少了3次卡頓和1次輕微卡頓,流暢幀占比超過90%。

圖片

圖6 分幀渲染在詳情頁頭部運用的效果

布局與繪制的基本單位是一棵widget樹,分幀渲染的原理是將布局與繪制時間較長的子widget先用Container占位,再等下一幀開始時單獨渲染。使用占位widget的偽代碼如下,build方法返回占位widget,并在widget構建幀結束時替換占位widget并觸發繪制。

@override
void initState() {
super.initState();
result = widget.placeHolder;
replaceWidget ();
}
@override
Widget build(BuildContext context) {
return result;
}
void replaceWidget() {
SchedulerBinding.instance.addPostFrameCallback((t) {
TaskQueue.instance.scheduleTask(() {
if (mounted)
setState(() {
result = widget.child;
});
}, Priority.animation, id: widget.index);
});
}

幀的繪制狀態可以從SchedulerBinding獲得,同時建立隊列保證一幀執行一個子widget繪制。

// 等待當前幀結束時替換占位widget并觸發繪制
await SchedulerBinding.instance.endOfFrame;
// 執行任務隊列中的繪制任務
final TaskEntry<dynamic> entry = _taskQueue.first;
entry.run();

2.4  UI GPU 問題定位與優化

GPU 問題主要集中在底層渲染耗時上。有時候 Widget 樹雖然構造起來容易,但在 GPU 線程下的渲染卻很耗時。涉及 Widget 裁剪、蒙層這類多視圖疊加渲染,或是由于缺少緩存導致靜態圖像的反復繪制,都會明顯拖慢 GPU 的渲染速度。可以使用性能圖層提供的兩項參數,負責檢查多視圖疊加的視圖渲染開關checkerboardOffscreenLayers和負責檢查緩存的圖像開關checkerboardRasterCacheImages來檢查這種模塊的存在。

a) checkerboardOffscreenLayers

多視圖疊加通常會用到 Canvas 里的savaLayer 方法,這個方法在實現一些特定的效果(比如半透明)時非常有用,但由于其底層實現會在 GPU 渲染上涉及多圖層的反復繪制,因此會帶來較大的性能問題。對于 saveLayer 方法使用情況的檢查,我們只要在 MaterialApp 的初始化方法中,將 checkerboardOffscreenLayers 開關設置為 true,分析工具就會自動幫我們檢測多視圖疊加的情況了,使用了 saveLayer 的 Widget 會自動顯示為棋盤格式,并隨著頁面刷新而閃爍。

不過,saveLayer 是一個較為底層的繪制方法,因此我們一般不會直接使用它,而是會通過一些功能性 Widget,在涉及需要剪切或半透明蒙層的場景中間接地使用。所以一旦遇到這種情況,我們需要思考一下是否一定要這么做,能不能通過其他方式來實現。如下圖所示,因為詳情頭部bar用到高斯模糊,同時使用ClipRRect裁切圓角,ClipRRect會調到savelayer接口,所以該部分產生閃爍。

圖片

圖7 詳情頁頭部圖片標題欄中裁切樣式應用

b) checkerboardRasterCacheImages

從資源的角度看,另一類非常消耗性能的操作是,渲染圖像。這是因為圖像的渲染涉及 I/O、GPU 存儲,以及不同通道的數據格式轉換,因此渲染過程的構建需要消耗大量資源。

為了緩解GPU 的壓力,Flutter 提供了多層次的緩存快照,這樣Widget 重建時就無需重新繪制靜態圖像了。與檢查多視圖疊加渲染的checkerboardOffscreenLayers 參數類似,Flutter 也提供了檢查緩存圖像的開關 checkerboardRasterCacheImages,來檢測在界面重繪時頻繁閃爍的圖像(即沒有靜態緩存)。

我們可以把需要靜態緩存的圖像加到 RepaintBoundary 中,RepaintBoundary 可以確定 Widget 樹的重繪邊界,如果圖像足夠復雜,Flutter 引擎會自動將其緩存,避免重復刷新。當然,因為緩存資源有限,如果引擎認為圖像不夠復雜,也可能會忽RepaintBoundary。

2.5 頁面預加載提升TTI

網頁應用的主要流程有三步,通過鏈接打開頁面,發送服務請求獲得頁面數據,將頁面數據展示在頁面上。對客戶端應用來說,頁面之間跳轉是相對確定的,數據在頁面之間存在共享的可能,預加載的工作是在打開頁面之間預先獲得頁面的數據,從而減少打開頁面到頁面展示的時間。

預加載數據有三種常見方法,第二個頁面的數據在第一個頁面的服務結果中獲得;第二個頁面的數據在客戶端其它頁面中預先獲得并緩存;第二個頁面的服務請求在打開頁面之前發送。

a) 預加載頁面數據

頁面數據預獲取的方案,實現方法是在上一個頁面提前獲取服務數據,在用戶跳轉到當前頁面時,直接從緩存獲取,節省了數據的網絡傳輸時間,達到快速展示當前頁面內容的效果。目前在酒店核心預訂流程,都運用了數據預加載技術,如下圖所示。

圖片

圖8 酒店業務預加載頁面數據的應用

結合酒店業務特點,數據預加載需要考慮幾個方面問題,第一,酒店預訂流程頁面PV量都很高,酒店列表和詳情頁PV都是千萬級別,所以需要考慮數據預加載的時機,避免服務的資源浪費。第二,酒店列表,詳情,填單頁都有價格信息,價格信息對用戶來說是動態信息,實時都有變價可能,所以需要考慮數據預加載的緩存策略,避免因為價格的前后不一致造成用戶誤解。

在實現全流程預加載方案之后,我們酒店預訂流程頁面的慢加載率從初始值的42.90%降低至現階段的8.05%。

b) 預加載ViewModel

與數據預獲取的方案相比,預加載ViewModel更進一步,將預獲取的數據處理成ViewModel形式,在打開頁面時直接用ViewModel進行展示。這種方案減少了業務對數據處理的時間。

圖片

圖9 酒店詳情頁預加載ViewModel技術的應用

上圖是杭州綠城尊藍錢江豪華精選酒店在酒店列表頁和酒店詳情頁頭部的UI對比。可以看出,酒店詳情頁頭部的信息主要是酒店名稱、星級、榜單、特色設施、點評、開業裝修時間等信息,這些信息和列表頁酒店卡片信息存在重合。如果用戶瀏覽的軌跡為從酒店列表頁到酒店詳情頁,那么可以直接將列表頁的數據帶入酒店詳情頁作為頭部展示。

圖片

圖10 酒店詳情頁預加載ViewModel的數據流

上圖為詳情頁頭部預加載的主要流程。我們的flutter業務代碼采用MVVM的結構,將服務請求的結果處理完的數據放入ViewModel中,ViewModel的數據更新通過Provider機制觸發頁面UI更新。

圖中可以開到,詳情頁頭部ViewModel的數據有兩個來源,分別是列表頁服務請求的結果和詳情頁服務請求的結果。這兩個服務請求結果到ViewModel的業務流程不一樣,列表頁的服務結果數據通過URL參數的方式傳入詳情頁,而詳情頁服務結果可以直接生成詳情頁頭部的ViewModel。

圖中還有一個重要模塊是列表頁服務結果和詳情頁服務結果之間的通用緩存DataCache,它的功能是實現頁面之間數據的一致性。頁面上的數據可以由服務更新,也可以由用戶交互更新。業務的ViewModel依賴這個通用緩存,數據更新會觸發頁面UI更新。

三、Flutter服務通道優化

3.1 背景

因為我們APP采用的私有服務協議,目前發服務的動作還是在Native代碼上,而酒店的核心頁面已經轉到了Flutter上。通過Flutter框架提供的通道技術Native到Flutter的數據傳輸通道需要對數據做一次額外的序列化及反序列化的傳輸,同時傳輸的過程比較耗時,會阻塞UI的渲染主線程,對頁面的加載會造成明顯的影響。我們檢測到這個環節之后和框架一起對Flutter的底層框架進行了改造,可以實現數據流直接的透傳,同時不阻塞UI主線程,性能得到了極大的提升。

優化前,通過服務返回的數據流傳遞到flutter使用,整個過程要經歷以下4步:

  • PB反序列化
  • Response到JsonString的編碼
  • JsonString到MethodChannel(使用JsonMethodCodec編解碼)
  • 傳輸JsonString到Reponse的解碼

整個過程鏈路長,數據傳輸量大,效率低,影響到頁面加載性能,如下圖所示

圖片

圖11 優化前的業務服務請求數據流

改造后,通過服務返回的數據流,直接傳輸到Flutter側,在Flutter直接進行PB的反序列化,傳輸性能得到極大提升。

  • ?PB的數據流到MethodChannel(使用StandardMethodCodec編解碼)傳輸
  • PB反序列化到Response

整個過程鏈路短,數據傳輸量小,效率高,如下圖所示:

圖片

圖12 優化后的業務服務請求數據流

其中MethodChannel的編解碼器由JsonMethodCodec換成了StandardMethodCodec。因為StandardMethodCodec可以避免轉換JsonString的操作,能節省傳輸時間。

3.2 Flutter中使用Protobuf

在flutter中使用Protobuf,首先需要將proto契約文件轉化成dart文件,可以借助官方編譯工具protoc進行編譯。

a) 獲取protoc工具

安裝C+

+sudo apt-get install autoconf automake libtool curl make g++ unzip

安裝Protobuf發行版

https://github.com/protocolbuffers/protobuf/releases

下載完成之后,解壓,進到目錄中執行下面命令編譯安裝

./configure
make
make check
sudo make install
sudo ldconfig # refresh shared library cache.
安裝protoc-gen-dart插件
dart pub global activate protoc_plugin
在Terminal中執行protoc命令生成dart文件
protoc --dart_out=. <文件名>.proto

圖片

圖13 生成的契約文件結構

b)  使用生成的dart契約文件

執行flutter pub add protobuf命令,修改項目的pubspec.yaml,在dependencies中加上: protobuf: ^2.0.1

編寫如下測試代碼:

圖片

圖14 使用契約的樣例代碼

執行后可以得到如下結果:

圖片

圖15 執行結果

其中,生成Person的類繼承了Protobuf包里的GeneratedMessage類,序列化和反序列化由基類實現。但是這種方式不能根據需要定制化生成契約文件。因此,為了更好的兼容Json格式的數據,可以使用FreeMarker模板引擎定制化生成契約文件。

圖片

圖16 使用FreeMarker生成契約的文件結構

3.3 使用FreeMarker定制化生成dart契約文件

FreeMarker是一款模板引擎:即一種基于模板和要改變的數據,并用來生成輸出文本(HTML網頁、電子郵件、配置文件、源代碼等)的通用工具。它不是面向最終用戶的,而是一個Java類庫,是一款程序員可以嵌入他們所開發產品的組件。

下面介紹如何使用FreeMarker和protoc命令生成任意編程語言的契約文件

1)下載FreeMarker最新版jar包

https://freemarker.apache.org/freemarkerdownload.html

2)下載Protobuf對應版本的jar包

https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java

3)在Java項目中導入對應jar包

圖片

圖17 項目中導入工具方法

4)編寫Java程序

圖片

圖18 程序流程圖

程序的流程如上圖所示。首先使用protoc命令生成對應的描述文件,其次將描述文件轉換成對應java對象,最后使用FreeMarker模板引擎生成任意語言的契約文件。

圖片

圖19 程序的實現

由上圖可知,模板引擎的輸入是一個classModel對象。如下圖實現了將描述文件轉化成classModel對象的功能。

圖片

圖片

圖20 程序的實現(續)

FTL模板文件如下圖所示:

圖片

圖21 模版文件

5)執行代碼輸出契約文件

圖片

圖22 輸出的契約文件

這樣就可以實現了根據proto文件自定義生成任意編程語言的契約文件。

3.4 Json與Protobuf的性能對比

我們對比了相同報文情況下Json和Protobuf在序列化和反序列化上所花費的時間。從下圖可知,Protobuf在序列化和反序列化相同大小報文時比Json花費的時間大大減少了,也大大提高了我們獲取數據的速度。

圖片

圖23 序列化、反序列化時間

四、內存泄漏治理

4.1 內存泄漏的常用監控手段

內存泄漏是一個比較嚴重的問題,如果出現,對App的穩定性和用戶體驗都有非常大影響。因此對這塊的監控和治理也是我們非常關注的一塊。

在監控方面Flutter現在比較通用的方法就是利用Expando中的弱引用去監控我們要檢查是否有泄漏的對象,如果出現則從VM中獲取其引用鏈接,從而分析其泄漏原因。我們的框架也利用此方法監控了我們app中的每個頁面是否在退出時還存在泄漏。

另外通過Flutter的Dev tool中的內存監控工具也能實現對泄漏對象的發現。比如對于酒店詳情頁面,反復進入和退出此頁面,如果有泄漏會發現,在內存監控工具中出現此頁面多個的對象存活,此時基本可以判斷出此頁面出現了泄漏了。下圖的第一列是類名,第二、三列是實例數量,第四、五列是對應分配的字節數。

圖片

圖24 酒店詳情的內存泄漏監控

4.2 內存泄漏的治理

下面介紹一下,我們在我們頁面的內存泄漏治理中發現的一些導致泄漏的原因和解決的辦法。

a) 調用Native的Plugin時,對Future的Then設置的閉包沒有關閉

在調用Native的Plugin接口時,有時會設置一個Then的閉包,期望在這個閉包里去處理這個Plugin的返回結果。這個閉包會注冊到引擎的全局變量里面,如果Native調用了result的listener,這個Then的閉包會走到,然后會被清除掉。如果某些case,Native沒有調用,則這個閉包會泄露,如果這個閉包所屬的Model能引用到頁面對象的話,則會造成整個頁面的泄露。

比如下面這個例子,我們進入flutter頁面時會調這個plugin,但是native對應的result則必須在某些case情況下才會回調。而大部分情況下,是不會回調的,從而造成整個頁面的泄露。解決方法是把future轉換成stream,然后我們在頁面退出時cancel掉,就能避免閉包的泄漏。

例子:調用Native的Plugin時出現泄漏的情況

Flutter側的調用:

void callNative() {
FlutterBridge.callNative("method", map).then((value) {
do some thing;});
}

Native的響應:

override fun flutterPluginAction ( result: MethodChannel.Result){
if (condition) {
result.success(ret)
} else {
do something;
}
}

可以看到Native在接受到這個plugin調用時,對于result的調用返回不是一直都會做的,它需要等到滿足條件才會做這件事情,而如果它不做這件事情,對應的flutter那邊的閉包就會一直被保存在引擎中,這個引用鏈也會一直存在,從而造成這個引用鏈上的對象都泄漏了。

解決的方法:

void callNative () {
Future future = FlutterBridge. callNative ("method");
_streamSubscription?.cancel();
_streamSubscription = future?.asStream()?.listen((value)
{
do something;
});
}

我們的解決方式,就是對這種異步但不能確定回調是否一定完成的情況,換成用StreamSubscription去監聽。然后當頁面退出時做一下cancel的動作,這樣就能避免泄漏的發生。

void onPageDestroy() {
_ streamSubscription?.cancel();
}

這種等待對異步調用的回調監聽其實都可能存在類似問題,只不過如果是單純在Dart中的異步調用一般不會存在這種不回調的情況。但是對于plugin這種跟native的交互的地方,我們在初期接觸flutter時沒有關注到這塊,有可能會造成遺漏。

b) 一些觀察者模式中的訂閱者在頁面退出時沒有取消訂閱

這種是大家比較熟悉的一種情況。常見的例子有例如像Timer,EventBusCenter.defaultBus和LifeCycleObserver等。這些訂閱者如果在頁面退出時不需要了,需要記得取消掉。否則也會造成內存泄漏,這種情況我們也應該避免。

五、小結

性能優化是一件不斷持續,不斷深入的事情。我們通過本文中所介紹的改進措施對頁面性能實現了很大的優化,達到了不錯的效果。后續也會在此基礎之上對還可提高的地方繼續加深,同時也會對已經驗證實行有效的方案去做一些抽象,封裝工作,后續提供通用的解決方案。

責任編輯:未麗燕 來源: 攜程技術
相關推薦

2022-07-15 09:20:17

性能優化方案

2024-09-10 16:09:58

2023-11-24 09:44:07

數據攜程

2023-07-07 12:26:39

攜程開發

2024-04-18 09:41:53

2024-03-22 15:09:32

2022-04-14 17:53:50

攜程AWS上云

2022-10-21 10:40:08

攜程酒店MySQL慢查詢

2021-09-17 12:54:05

AI 數據人工智能

2022-06-03 08:58:24

APP攜程流暢度

2023-04-24 15:10:23

優化方案

2024-09-25 15:37:46

2022-04-07 17:30:31

Flutter攜程火車票渲染

2022-03-30 18:39:51

TiDBHTAPCDP

2023-07-07 14:18:57

攜程實踐

2016-09-01 09:39:20

攜程無線

2015-05-28 14:05:02

2023-03-14 14:01:00

內存優化

2022-07-15 12:58:02

鴻蒙攜程華為

2022-05-13 09:27:55

Widget機票業務App
點贊
收藏

51CTO技術棧公眾號

欧美一区二区三区电影| 99re这里只有精品首页| xx视频.9999.com| 91看片在线免费观看| 日本最黄一级片免费在线| 极品少妇一区二区| 78色国产精品| 色老板免费视频| 欧美电影免费网站| 在线播放日韩导航| 丰满爆乳一区二区三区| 69久久夜色| 99视频国产精品| 国产欧美精品一区二区三区介绍| 亚洲一区二区91| 久久精品国产大片免费观看| 亚洲福利视频网| 亚洲精品www.| 中文字幕一区久| 亚洲一区二区在线视频| 婷婷精品国产一区二区三区日韩| a天堂中文在线观看| 久久成人一区| 欧美精品成人在线| 熟女少妇a性色生活片毛片| 日本中文字幕在线一区| 日韩女优视频免费观看| 国内自拍视频网| 亚洲十八**毛片| 一区二区三区欧美| 中国成人在线视频| 成人不用播放器| 97久久超碰国产精品| 91精品免费| 亚洲中文字幕在线一区| 日日夜夜免费精品视频| 2021久久精品国产99国产精品| 国产精品国产精品88| 第九色区aⅴ天堂久久香| 日韩精品在线视频| 欧美在线一级片| 大香伊人久久精品一区二区| 91精品国产综合久久福利软件| 天堂在线资源视频| 日本精品裸体写真集在线观看| 疯狂做受xxxx欧美肥白少妇| 青青青青草视频| 欧美videosex性欧美黑吊| 亚洲视频在线一区| 日本特级黄色大片| 日本激情在线观看| 国产精品国产三级国产三级人妇| 色女人综合av| av在线女优影院| 国产精品久久久久影院色老大| 日韩精品一区二区三区丰满| 手机福利在线| 久久综合久久鬼色中文字| 久久青青草原| 美国一级片在线免费观看视频| aa级大片欧美| 快播日韩欧美| 超碰在线国产| 亚洲少妇中出一区| 日韩a级黄色片| 丰满大乳少妇在线观看网站| 午夜精品视频一区| 亚洲自偷自拍熟女另类| 色吧亚洲日本| 欧美综合亚洲图片综合区| 国产一伦一伦一伦| 美女久久精品| 亚洲国产美女久久久久| 成人精品在线观看视频| 国产99久久久国产精品成人免费| 亚洲天天在线日亚洲洲精| 欧美日韩中文字幕视频| 99久久99热这里只有精品| 欧美老女人性生活| 国产精品7777777| 久久综合中文| 91免费人成网站在线观看18| 风流少妇一区二区三区91| 91免费国产在线观看| 亚洲成人自拍| 色呦呦视频在线观看| 欧美网站在线观看| 久久这里只精品| 7777精品| 中文字幕久热精品在线视频| 成年人av电影| 男女av一区三区二区色多| 国产欧美日韩精品在线观看| 亚洲精品国产suv一区| 久久先锋资源网| 国产精品h视频| 超碰超碰人人人人精品| 6080yy午夜一二三区久久| 久久精品女同亚洲女同13| 国产综合久久久| 欧美激情第三页| 少妇无套内谢久久久久| 成人一区二区三区在线观看| 日本精品免费| 欧美人与禽性xxxxx杂性| 91黄视频在线观看| 北京富婆泄欲对白| 香蕉久久网站| 国产成人91久久精品| 亚洲成人黄色片| 亚洲国产精品成人综合色在线婷婷| av动漫在线免费观看| 精品免费av一区二区三区| 欧美成人aa大片| 91麻豆精品国产91久久综合| 在线欧美福利| 91在线观看免费| 高h视频在线| 亚洲电影一区二区| 手机看片国产精品| 91亚洲国产| 国产成人自拍视频在线观看| 亚洲黄色在线观看视频| 国产精品国产三级国产aⅴ无密码| 狠狠97人人婷婷五月| 秋霞一区二区| 日韩亚洲在线观看| 91丨九色丨海角社区| 白白色 亚洲乱淫| 日本a级片在线观看| av成人亚洲| 亚洲日本中文字幕| 91丝袜一区二区三区| 成人污污视频在线观看| 日韩国产精品毛片| 日本免费在线一区| 有码中文亚洲精品| 波多野结衣mp4| 国产午夜三级一区二区三| 激情深爱综合网| 精品福利网址导航| 久久男人的天堂| 丰满人妻一区二区三区四区53| 亚洲精品国产成人久久av盗摄| 蜜桃福利午夜精品一区| 91蜜臀精品国产自偷在线| 国产精品九九久久久久久久| 国产资源在线播放| 日本高清不卡视频| 国产成人免费观看网站| 奇米影视一区二区三区小说| 日韩欧美精品一区二区| 成人精品国产亚洲| 日韩一级黄色av| 99国产精品久久久久久久成人| 1区2区3区精品视频| 搡的我好爽在线观看免费视频| 欧美激情理论| 97视频资源在线观看| 成人ww免费完整版在线观看| 日韩一区二区三区四区五区六区| 网爆门在线观看| 国产主播一区二区三区| 国产免费xxx| 凹凸成人在线| 97香蕉久久超级碰碰高清版| 欧美日韩在线中文字幕| 91成人在线精品| 美女三级黄色片| 国产福利91精品一区二区三区| 91传媒免费视频| xxxx日韩| 国产精品扒开腿做爽爽爽视频| 91伦理视频在线观看| 91精品啪在线观看国产60岁| 免费在线观看日韩| 久久久99久久| 亚洲小视频网站| 伊人久久亚洲美女图片| 欧美一区二区三区成人久久片| 成人福利一区二区| 欧美日韩国产成人在线| 污污视频在线免费看| 在线观看国产日韩| 91嫩草|国产丨精品入口| 成人a区在线观看| 久久久久久久久久久久久久国产| 久久精品播放| 久久99精品国产一区二区三区| 国产极品久久久久久久久波多结野 | 正在播放国产一区| 国产wwwwwww| 黑人欧美xxxx| 男女性高潮免费网站| 99国产精品视频免费观看| 97人人爽人人| 亚洲看片一区| 吴梦梦av在线| 九九综合九九| 91原创国产| 欧美成人资源| 久久久免费在线观看| 四虎久久免费| 亚洲精品自产拍| 99久久婷婷国产一区二区三区| 激情久久av一区av二区av三区 | 中文字幕一区二区不卡| 污污内射在线观看一区二区少妇 | 免费在线观看日韩av| 丝袜国产日韩另类美女| 日本男女交配视频| 色小子综合网| 麻豆久久久9性大片| 激情不卡一区二区三区视频在线| 欧美有码在线观看视频| 中文在线观看免费| www.国产一区| 福利小视频在线观看| 亚洲福利在线播放| av无码精品一区二区三区宅噜噜| 欧美性xxxxx极品| 国产真实乱人偷精品视频| 国产精品你懂的| 亚洲天堂久久新| 不卡一区中文字幕| 91精品国产高清91久久久久久| 精品在线一区二区| 男人搞女人网站| 日日骚欧美日韩| 国产免费观看高清视频| 亚洲天堂黄色| h无码动漫在线观看| 一区二区电影在线观看| 日韩欧美精品在线不卡| 国产成人三级| 热re99久久精品国产99热| 欧美亚洲tv| 激情伦成人综合小说| 国产精品久久久久av蜜臀| 亚洲在线视频福利| 久久久久久亚洲精品美女| 成人免费高清完整版在线观看| 992tv国产精品成人影院| 欧美野外猛男的大粗鳮| 欧美日韩国产观看视频| 91精品国产高清| 亚洲天堂av在线| 国产精品99久久久久久www| 在线日韩影院| 国产精品igao视频| 主播大秀视频在线观看一区二区| 国产精品91久久| 成人国产综合| 成人啪啪免费看| 亚洲国产精品免费视频| 91精品在线播放| 日韩精品成人在线观看| 国产精品免费看一区二区三区| 一区二区三区国产好| 狠狠综合久久av| 欧美老女人另类| 成年人黄色在线观看| 欧美日本一区| 成人黄色av片| 老司机午夜精品视频在线观看| 精品视频无码一区二区三区| 日韩精品一卡二卡三卡四卡无卡| 日韩欧美在线免费观看视频| 蜜桃av一区二区三区电影| 91小视频在线播放| 成人精品视频.| 51妺嘿嘿午夜福利| 成人免费在线播放视频| 久青草免费视频| 色国产精品一区在线观看| 91麻豆国产在线| 精品国精品国产| 国产小视频免费在线网址| 久久九九精品99国产精品| 丁香高清在线观看完整电影视频| 欧美一级电影免费在线观看| 国产成人福利夜色影视| 粉嫩av四季av绯色av第一区| 思热99re视热频这里只精品 | 成人18在线| 欧美国产乱视频| 日本韩国欧美| 99精品99久久久久久宅男| 全球av集中精品导航福利| 一区国产精品| 亚洲影视在线| 杨幂一区二区国产精品| 久久久久久免费毛片精品| 成人免费黄色小视频| 精品国产成人在线| 91片黄在线观看喷潮| 亚洲精品理论电影| bestiality新另类大全| 奇米影视亚洲狠狠色| 国产一区二区三区免费观看在线| 欧美不卡在线一区二区三区| 亚洲五月综合| 欧美性猛交久久久乱大交小说| 国产成人精品三级麻豆| 永久免费av无码网站性色av| 亚洲动漫第一页| av网站在线免费看| 中文字幕日韩精品在线观看| a级片在线免费观看| 国产日韩欧美中文| 免费看日本一区二区| 久久手机在线视频| 麻豆精品一二三| 法国空姐电影在线观看| 亚洲综合视频网| 国产精品无码在线播放| 一本一道久久a久久精品逆3p | 国产在线观看网站| 久久久视频在线| 国产在线一区不卡| 亚洲一区影院| 久久狠狠婷婷| 国产精品久久不卡| 亚洲成人免费观看| 精品久久久久久亚洲综合网站| 中文日韩在线视频| 日本成人三级电影| 欧洲精品在线一区| 久久高清国产| 国产一级二级在线观看| 亚洲国产成人porn| 亚洲精品国偷拍自产在线观看蜜桃 | 日韩aaaaa| 亚洲观看高清完整版在线观看| 精品黑人一区二区三区国语馆| 粗暴蹂躏中文一区二区三区| 九九久久国产| 伊甸园精品99久久久久久| 蜜桃在线一区二区三区| 日本人亚洲人jjzzjjz| 在线亚洲高清视频| 大片免费播放在线视频| 国产精品久久久999| 日韩免费特黄一二三区| 青青青在线视频免费观看| 亚洲国产精品成人综合| 中文字幕有码视频| 日韩中文字幕亚洲| 亚洲午夜剧场| 粉嫩av一区二区三区天美传媒| 国产剧情av麻豆香蕉精品| 青草影院在线观看| 精品日韩在线观看| 日韩欧美精品一区二区三区| 欧美精品国产精品久久久| 日韩和欧美一区二区| 精品一区二区6| 欧美精品一卡二卡| 成人av福利| 国产精华一区二区三区| 亚洲精品少妇| 国产 欧美 在线| 欧美网站一区二区| 91极品在线| 国产伦精品一区二区三区高清| aa级大片欧美三级| a级片在线观看| 欧美日韩免费高清一区色橹橹| 免费在线观看av网站| 不卡视频一区二区三区| 国产欧美午夜| 日本黄区免费视频观看| 欧美一级片在线| 男人久久天堂| 亚洲午夜激情| 高清av一区二区| 亚洲精品成人在线视频| 日韩一区二区av| 久久激情av| 欧美成人福利在线观看| 亚洲亚洲精品在线观看| 国产中文字幕在线播放| 91成人伦理在线电影| 亚洲欧美日韩国产一区二区| 精品视频第一页| 精品福利视频一区二区三区| 亚洲天堂一区二区| 中国一级大黄大黄大色毛片| 不卡一区二区中文字幕| 在线播放成人av| 国内精久久久久久久久久人| 欧美呦呦网站| 丰满岳乱妇一区二区| 欧美日韩一区二区三区在线| 成人三级小说| 一本一生久久a久久精品综合蜜| 成人综合在线观看| 这里只有精品999| 7777kkkk成人观看| 亚洲先锋影音|