成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

一種Android客戶端架構設計分享

移動開發(fā) Android
技術發(fā)展日新月異,業(yè)界各種Android客戶端架構設計,五花八門,但我們不能簡單地說哪種架構更好,因為脫離業(yè)務談架構是沒有任何意義的,適合業(yè)務的才是好架構。而架構也不是一成不變的,隨著業(yè)務的發(fā)展,也許當初設計的架構已不足以支撐目前的業(yè)務,那么就需要改變之前的架構。

前言:技術發(fā)展日新月異,業(yè)界各種Android客戶端架構設計,五花八門,但我們不能簡單地說哪種架構更好,因為脫離業(yè)務談架構是沒有任何意義的,適合業(yè)務的才是好架構。而架構也不是一成不變的,隨著業(yè)務的發(fā)展,也許當初設計的架構已不足以支撐目前的業(yè)務,那么就需要改變之前的架構。接下來將分享下我們Android客戶端的架構設計,在App的某個業(yè)務發(fā)展階段或許有一些參考意義。

分層化與模塊化

分層化與模塊化應該是任何軟件開發(fā)的共識。

分層化

在Android應用開發(fā)中通常可以分為如下幾層:  

 

  • SDK層:主要是Android SDK及第三方的SDK(可能基于Android SDK或為獨立的SDK),這些SDK為上層框架提供核心功能的支持。
  • 基礎框架層:這里所謂的基礎框架,指多數App都必需的基礎功能,是具體業(yè)務邏輯實現的基礎。主要有網絡請求功能、圖片加載與緩存功能、SQLite數據庫管理功能、Log管理功能等,當然根據對業(yè)務邏輯支持的不同,基礎框架層的功能支持也不一定相同,上述幾個應該是大部分App都要支持的,當然Crash監(jiān)控與常用工具類也可歸為該層次。

具體到每個基礎框架的實現則沒有任何限制,如網絡功能可以使用Volley、OkHttp或者自己封裝實現網絡請求邏輯;對于圖片管理功能則可以使用Glide、Fresco、Picasso,亦或自己實現……總之每個基礎框架都要遵循一定的實現原則,保持功能模塊的獨立性,與具體業(yè)務解耦并對外提供良好的交互接口。

  • 業(yè)務邏輯層:如果把App架構比作高層建筑,那么上述兩層就是地基。地基打好之后,就可以在上面任意發(fā)揮了,至于如何發(fā)揮,那就必須結合實際的業(yè)務需求,不同的應用往往有不同的業(yè)務功能模塊。

另一方面,業(yè)務功能模塊也并非完全是并列的級別,有一些業(yè)務邏輯也是可以抽象出來的,作為通用的功能模塊,比如登錄、分享、掃描、統(tǒng)計等,其他的業(yè)務模塊可能會調用到這些功能。

這里需要注意的是SDK層與基礎框架層并不是一成不變的,但它們的變化周期往往是比較長的,一般來說當基礎功能不能滿足最上層的業(yè)務邏輯時,就需要對其做擴展。由于基礎框架層的功能模塊已經是功能級別的粒度劃分,因此擴展往往是模塊級別的擴展,通常是新增基礎功能框架而不是修改原有基礎功能框架,這也符合“開放-閉合”原則。

模塊化

至于模塊化,對于分層化來說則是更細粒度的劃分,即將每一層細分為不同的模塊,各功能模塊盡可能遵循“高內聚、低耦合”的原則,功能模塊之間僅提供必要的交互接口。

對于基礎框架層,由上圖可見,往往是根據功能來劃分。這里的基礎框架層細分為網絡支持功能、圖片庫、日志系統(tǒng)、數據庫支持等模塊,如果不足以支撐業(yè)務發(fā)展,可能會新增其他基礎功能模塊。

而業(yè)務邏輯層則主要由業(yè)務需求來決定,如分為掃描功能、電商、快遞查詢等模塊。業(yè)務邏輯層的模塊化還有一種驅動因素,那就是通用功能的封裝,這一點大家應該都有體會,隨著App業(yè)務邏輯的增加,不同業(yè)務功能之間可能會用到相同的功能,如用戶登錄、分享功能等,我們不希望在每個需要的地方都復寫一遍相關代碼,于是就需要把通用功能抽取成獨立于具體業(yè)務需求的模塊,如登錄模塊、分享模塊,在模塊內部實現通用的業(yè)務邏輯,同時對外暴露調用接口,不同的業(yè)務只需調用通用模塊即可。

業(yè)務數據流程設計

由于業(yè)務邏輯、數據處理邏輯或網絡框架的不同,相信各家應用都有自己的一套數據請求流程。最直接的就是從Activity或Fragment中調用網絡請求的方法,然后通過回調將結果返回到Activity或Fragment中,雖然流程最清晰,但這種方式存在幾個嚴重的問題:

  • 網絡數據直接返回到Activity或Fragment中,后續(xù)需要對數據進行解析、過濾、轉換、緩存等操作,這些工作將會大大加重Activity或Fragment的負擔。
  • Activity或Fragment的代碼量猛增,邏輯繁雜(不僅包含了View的邏輯還包含了數據處理的邏輯)
  • 從整個應用的角度來看,每個頁面甚至每個接口都需要重復上述相同的冗余工作,完全可以抽象出來。

上述設計思路是需要摒棄的,結合自身業(yè)務及架構演化,我們沒有跟風MVP、MVVM,而是設計了下面一套業(yè)務數據請求流程: 

 

 

業(yè)務數據請求流程 

首先,視圖層通常表現為Activity或Fragment,并由視圖層發(fā)起數據請求,與上述不同,視圖層并不直接跟網絡框架打交道,而是先將數據請求發(fā)送到數據代理層DataAgent。需要注意到是,視圖層與數據代理層之間沒有采用直接通信的方式,而是插入了一個消息調度器MessageScheduler中轉。這樣做的好處是將視圖層與數據代理層解耦,視圖層無需關注數據代理層的具體實現,有了MessageScheduler,視圖層所要做的就是發(fā)出一個數據請求的消息而已,然后就可以靜靜等待一個回復消息,該回復消息會附帶最終需要的數據對象,這樣在視圖層就免除了數據處理的邏輯,拿到結果直接展示到UI上即可。使用這種方式,一般來講Activity或Fragment三五百行代碼即可搞定,UI邏輯或接口邏輯(如一個頁面有多個接口)比較復雜的代碼量基本也能控制在1000行左右,邏輯非常清爽。

消息調度器將視圖層的請求消息轉發(fā)到數據代理層后,DataAgent解析出數據請求類型DataType(該類型對應著具體數據對象模型)、必要參數(接口參數、是否需要緩存結果、分頁頁碼等),然后再執(zhí)行具體的操作:

  • 如果要取緩存的數據,則DataAgent直接向緩存模塊發(fā)送請求。緩存的數據可以是初始JSON數據,也可以是解析處理后得到的數據對象Model,可根據具體需求配置。如果從緩存中取到的是JSON,則DataAgent先要解析處理得到對應Model;如果從緩存中取到的是Model,則不做處理,然后將Model封裝發(fā)回到消息調度器,再由MessageScheduler分發(fā)給具體的請求者,如Activity或Fragment。
  • 由于Android的數據來源有多種,如果數據來自持久化存儲,如SQLite或File等,仍然統(tǒng)一由DataAgent來跟它們通信,獲取數據并加工后通過MessageScheduler發(fā)回視圖層。
  • 最常見的是從服務器獲取數據,此種場景下,DataAgent將與網絡框架交互,將從MessageScheduler中獲取的參數提供給網絡框架構造請求url。至于網絡框架使用Volley或OkHttp或者其他都沒關系,網絡框架負責向Server請求數據,數據通常以JSON格式返回。DataAgent收到返回的JSON數據后,根據DataType將JSON數據校驗后拋給解析器,解析器會將JSON解析為視圖層需要的Model。當然數據解析過程可能伴隨數據的過濾、轉換等邏輯。另外需要注意的是,還需要根據視圖層需求對數據進行是否緩存的操作,可選擇緩存JSON還是Model。經過一系列操作,得到最終Model后,DataAgent將其通過MessageScheduler發(fā)回視圖層。

當然,由于數據請求流程是耗時的,因此上述步驟都是走的線程池,這點上圖中并未注明。

數據代理層

DataAgent在上文中已簡單提及,它的主要作用是對數據的一系列操作,包括實際的數據請求、數據解析處理、數據緩存等邏輯。下圖為從服務端接口獲取JSON數據并處理的流程: 

 

 

從服務端接口獲取JSON數據并處理的流程 

從上圖可知,DataAgent的大致工作流程為:

  • DataAgent將真正的數據請求發(fā)送給各數據源,數據源可能為緩存、SQLite或文件,但通常是從服務端獲取數據,因此DataAgent會將數據請求發(fā)到網絡框架層,然后等待數據返回。
  • 由于數據源不同,返回數據也可能不同,這里簡化為兩種:原始JSON或Model。
  • DataAgent拿到數據后,則開始數據處理流程。以從網絡請求的JSON數據為例,先對返回的JSON進行數據校驗,檢查數據的有效性與正確性,如果數據校驗通過,接下來根據需求來決定要不要寫入緩存,然后再進行數據加工(如精度處理、數據拼接、數據裁剪等),***進行數據解析得到視圖層需要的Model。如果數據校驗沒有通過,則嘗試從緩存中讀取,從緩存中讀取后也需要校驗(檢查數據的時效性、有效性、正確性),校驗通過后同樣進行數據處理、解析等流程。如果緩存中讀取得到的就是Model,那么則可以省略數據處理和解析的流程。得到最終的Model后,DataAgent將其包裝發(fā)送給MessageScheduler。另外DataAgent還要具有一定的容錯功能,因為任何數據源都無法保證能夠返回合法的數據,如果不對數據錯誤進行容錯處理,那么就可能無法解析為對應的Model,從而導致視圖層無數據甚至異常。如果接口及緩存都無法返回正確的數據,DataAgent需要做特殊處理,以保證視圖層能給用戶以反饋。

業(yè)務視圖邏輯

雖然不同的業(yè)務頁面有不同的視圖邏輯,這里以一個應用中最常見的頁面為例來說明,假設該頁面有一個列表。大家都知道ListView(此處為泛指,可能大家都在用RecyclerView了)的工作方式,它需要ViewHolder來填充視圖,需要Adapter來填充數據,如果每個需要ListView的界面都維護各自的一套ViewHolder及Adapter,那么頁面邏輯又將變得臃腫。

我們在實踐中是這樣做的:

  • 封裝一個Adapter公共處理類,提供多種構造函數,其中有一個type參數,用來標明需要使用哪個ViewHolder。
  • 封裝一個ViewHolder抽象類,定義數據設置的邏輯,并交由具體的ViewHolder實現。
  • 構建一個叫做ViewHolderFactory的類,顧名思義該類主要作用是用來構建ViewHolder,它主要提供兩個方法createViewHolder()與createConvertView(),其中createConvertView()是個中間方法,用于生成ViewHolder。
  • 在Adapter的getView方法中,根據上述type參數,獲取具體的ViewHolder實現,調用設置數據的邏輯。

經過上述封裝之后,視圖層只需要向Adapter公共處理類傳入一個type參數即可得到對應的Adapter;等數據返回到視圖層后,再將數據傳給Adapter公共處理類,其他什么都不用管,就可以展示列表數據了。原本需要很多代碼實現的邏輯從視圖層抽離之后,視圖層只需要幾行代碼就能夠完成一個列表展示了。

Hybrid框架

自Android誕生以來,就有Native App與Web App之爭,這兩種開發(fā)方式雖然各有優(yōu)缺點,但Native App一直占據上風。近一兩年來,移動應用中的Web頁面越來越多,而純Native的應用則相對越來越少。但是純Web App由于其渲染效率、性能問題、對硬件的調用限制導致其也并未廣泛地應用。于是一種折中的方案成為主流,即Hybrid App。

所謂Hybrid App,即混合開發(fā)方式,部分功能使用Native開發(fā),部分功能使用H5開發(fā)。為了充分利用Web開發(fā)的優(yōu)點并避開其缺點,并非所有業(yè)務功能都適合使用Web方式來開發(fā)。在我們的應用中,主要將H5用于以下方面:

  • 節(jié)日活動或游戲頁、秒殺或團購頁等具有時效性的頁面。
  • 使用說明、公告等偏展示、少交互的頁面。
  • 經常更新、交互較少且不涉及硬件調用的頁面或模塊,如電商商品首頁展示、積分兌換模塊。

截止到目前,我們App中的Web頁所占比重是上升的,大概占到所有功能的25%左右。使用Web開發(fā)的優(yōu)勢非常明顯,可以支持多變的UI視圖效果、節(jié)省開發(fā)人力(Android、iOS共用)、Bug的在線修復而不用App發(fā)版等。

為了滿足App的Web頁面需求,于是我們在基礎框架層擴展了一個Hybrid功能模塊。該框架主要是自行封裝了Android原生的WebView控件,且分為不同層級的封裝,可根據需要靈活使用,核心功能及特性如下:

  • 支持完整的Web頁面,即整個頁面的內容全部是H5實現,外部容器為Activity或Fragment。
  • 支持局部的Web頁面,即部分頁面的內容是H5實現,可單獨使用自定義的WebView或者嵌入Fragment使用。
  • 定義了一套較為完整的交互協(xié)議,支持Native與JS的互相調用,典型的場景如H5頁面點擊跳轉Native功能頁面(支持傳參)、JS喚起Native對話框或Toast等,同時Java也能調用JS函數。基于此套交互協(xié)議,基本能夠滿足日常App中Web開發(fā)需求。
  • 避免了JS注入漏洞。
  • 支持同一個Web頁面中Http與Https混合的場景。
  • 向業(yè)務邏輯層暴露接口,可根據需求定制WebViewClient與WebChromeClient。
  • 對外提供接口,可根據需求控制縮放、Cookie管理、緩存管理、硬件加速等。
  • 經過試驗與摸索,兼容多種Android設備及版本。

雖然后來出現了React Native,但由于學習成本及其Android版本的局限性,結合我們自己團隊的人力資源原因,我們尚未在應用中正式使用。目前仍然以Hybrid開發(fā)為主,且其在整個應用中的比重越來越大,因此Hybrid框架是我們架構中重要的一個組成部分。

消息調度中心

前面業(yè)務數據流程的設計中,在視圖層與數據代理層之間插入了一個消息調度器——MessageScheduler,MessageScheduler主要功能就是管理消息及消息調度。

MessageScheduler核心原理是維護了一個哈希表,當收到視圖層的數據請求時就使用唯一的key將發(fā)起者保存到哈希表中,以便稍后收到DataAgent的返回數據后,能夠找到發(fā)起者。存儲好消息發(fā)起者的信息后,即向DataAgent發(fā)送數據請求,多個數據請求是可以并行的,主要在于線程池的線程數控制機制。DataAgent返回數據之后,MessageScheduler根據唯一key找到初始的請求者,同樣利用消息機制將請求結果返回給視圖層,同時在哈希表中清除該元素。其示意圖如下: 

 

 

 

消息分發(fā)器

既然有了消息調度機制,就需要消息分發(fā)器MessageDispatcher,來負責發(fā)送消息。

MessageDispatcher本質上是利用了Android的消息機制來對業(yè)務需求進行封裝和擴展。看過Android Framework層源碼就會發(fā)現其實Android框架本身就有很多地方使用了消息機制來進行通信,Android消息機制可以在模塊頁面間、線程間通信,甚至可以在進程間使用Messenger通信(Messenger方式是利用了消息機制,當然還有其他進程間通信方式)。

MessageDispatcher功能比較簡單,支持兩種方式:

  • 點對點的通信,如兩個頁面之間,通信目標唯一,如上文提到的從視圖層發(fā)送數據請求消息到消息調度器。
  • 點對面的通信,類似于廣播,也有點像EventBus,一條消息發(fā)出,凡是注冊(或叫訂閱)過的頁面都能收到通知;也可以進一步通過Tag控制達到一對一發(fā)送。

其示意圖如下: 

 

 

 

模塊路由中心

一個完整的應用中,免不了模塊之間、功能頁面之間的跳轉。當然在需要的地方通過Intent可以實現跳轉,但這不是一個好的方案,很明顯不同模塊或頁面之間的耦合度增加了。而我們的原則是模塊和頁面之間盡可能解耦,于是設計了一個模塊路由(Module Routing)中心,App中所有的頁面跳轉均由其控制。

模塊路由的核心原理是給功能頁面進行唯一編碼,編碼的邏輯可以跟隨產品版本定義到應用中,并保證兼容之前版本。這樣就可以在應用的任何地方只需要向模塊路由中心發(fā)送對應模塊頁面的編碼即可,由模塊路由負責打開目標頁面。

以下幾點需要注意:

  • 整個應用中的功能頁編碼都必須保證唯一
  • 如打開某些功能頁面除了具體編碼外,還可能需要額外參數。如打開商品詳情頁,除了知道商品詳情頁的編碼外,還需要商品ID,模塊路由需要對附加參數提供支持。
  • 模塊路由支持打開Web頁面,即Hybrid頁面也支持上述特定編碼,所以在Web頁面上點擊跳轉Native頁面使用的協(xié)議也是由模塊路由支持的。

使用模塊路由的好處有:

  • 大量減少應用中的跳轉Intent
  • 模塊之間、頁面之間解耦
  • 適配變化,統(tǒng)一管理,修改方便

其他

日志系統(tǒng)

在開發(fā)過程中,甚至運行過程中,日志都是很重要的一部分。當然Android提供了Log相關的API,但不建議這一行那一行地零星使用,否則如果想統(tǒng)一控制Tag或關閉Log時非常麻煩。建議對Log API進行簡單封裝或者使用現有第三方Log庫,將Log功能獨立出來,提供統(tǒng)一的調用接口、級別控制、開關控制,這樣既方便調試也方便管理,同時也能為整個應用代碼的清晰做出一點貢獻。

線上崩潰監(jiān)控

對線上應用的Crash監(jiān)控是提高應用穩(wěn)定性、優(yōu)化應用性能的一個重要方法。我們構建了一個小型的全局監(jiān)控系統(tǒng),主要由以下功能特性:

  • 對用戶不可見,用戶無感知
  • 全局注冊即可開啟監(jiān)控
  • 捕捉線上崩潰,保存到本地文件
  • 線上崩潰信息按一定策略上傳服務器,上傳后同時刪除本地文件
  • 崩潰信息主要包括Android設備信息(如手機型號、系統(tǒng)版本等)、App版本號、異常信息等

服務器收到上傳的線上崩潰信息后,也按一定策略通過郵件方式通知到開發(fā)者,以便開發(fā)者及時修復異常。線上崩潰監(jiān)測系統(tǒng)雖然小而簡單,但作用非常重要,利用線上崩潰反饋可以有效地提高應用的穩(wěn)定性,建議在應用設計中務必給它留出一個位置。

統(tǒng)計系統(tǒng)

相信大部分應用都有統(tǒng)計分析后臺,可以統(tǒng)計應用的日活、PV、UV或其他用戶行為,也可能有一部分應用是使用的第三方統(tǒng)計功能,如友盟等。結合公司BI部門的統(tǒng)計需求,我們客戶端自行設計了一套統(tǒng)計方案,用于Android與iOS兩個客戶端。之所以不用第三方統(tǒng)計,主要是因為我們無法根據需求自由定制且數據不在自家服務器,另一方面也有些許數據泄露的風險。

基于客戶端的統(tǒng)計系統(tǒng)主要包括三個方面的功能:

  • 數據采集
  • 數據存儲
  • 數據上傳

對于數據采集,主要針對統(tǒng)計部門的需求,如采集設備信息、定位信息、App啟動時間次數、PV、UV、甚至用戶行為,如點擊、切換Tab、頁面流向跟蹤等。

為了避免每次采集完數據后就即時上傳,因此需要數據存儲,將采集的統(tǒng)計數據暫存到本地,一般使用SQLite。然后采用一定策略進行上傳,如數據累積到50條或者應用切換到后臺時進行上傳。

對于數據上傳,除了上傳時機的選擇策略外,還要遵循一定的結構字段,該結構可以根據數據統(tǒng)計部門的需求來定義。數據上傳的流程同樣可以使用之前的數據請求框架,只不過返回值可能為一個成功提示而已。

基于上述功能,我們自定義的統(tǒng)計功能模塊提供了方便的調用接口,并支持靈活擴展,目前可以***支持日常的統(tǒng)計需求,調用也非常簡單,只需要在需要統(tǒng)計的地方插入一行代碼即可。

域名劫持應對策略

最近遇到域名劫持的問題,真是頭疼,另一方面也說明我們的流量引起運營商注意了。目前主流的有幾下幾種方案:

  • 向運營商投訴。此方法非常被動且效果不佳,完全掌控在運營商手中。
  • 使用httpDNS。此方法使用http的方式直接獲取***IP,繞過localDNS的解析,可謂徹底解決了域名劫持。
  • 先使用域名嘗試,域名失敗后再使用IP嘗試。此方案屬于容災方案,并不能避免域名劫持。

理論上講第二種是***方案,但由于httpDNS為第三方服務,也無法保證效果,外加上付費及接入成本等因素,我們暫時采用了第三種容災方案,主要實施邏輯如下:

  • 應用預先內置IP。
  • 每次啟動應用時獲取***IP,并保存到應用本地。
  • 請求數據時,先使用域名走正常的邏輯,一旦遇到疑似劫持的問題后,使用本地的IP進行直連嘗試。

上述步驟其實是有漏洞的,比如啟動時獲取***IP的接口如果被劫持了,那么就無法獲取***IP,假如剛好同時服務器IP也改變了,因此預先內置的IP已經失效,此時就徹底沒辦法了。不過上述兩個條件同時滿足的概率比較小,因此可以使用該方案解決很大一部分域名劫持問題。另外從服務端獲取的IP,如果有多個的話,還需要增加一些策略,即考慮到負載均衡、訪問速度、穩(wěn)定性、網絡運營商等因素,如何確定客戶端拿到的哪一個是***IP,當然這點可以優(yōu)化,但首先能保證用戶看到頁面數據或許更加重要。

上述應對域名劫持的策略本身并不能獨立成一個模塊,我們把它集成為網絡框架的擴展。

總結

上文提到的是我們Android應用架構中的核心部分,可能你發(fā)現并沒有什么花哨的、潮流的玩意兒,沒有MVP,沒有RxAndroid,沒有插件化,也沒有熱修復……但就是這樣它仍然支撐起了上億的用戶量。世上沒有***的架構,只有符合自身業(yè)務的架構,上述架構還有很多缺點,我們也在有選擇、有步驟地重構,而隨著業(yè)務需求的擴展,架構也會不斷演化,***希望本文能給大家?guī)硪稽c參考意義。 

責任編輯:龐桂玉 來源: Android開發(fā)中文站
相關推薦

2023-03-31 13:31:45

2016-05-09 09:26:06

架構ios網絡層

2013-09-04 12:38:56

架構設計架構設計構思

2017-07-05 14:09:04

系統(tǒng)設計與架構java云計算

2023-12-26 08:16:56

Kafka緩存架構客戶端

2014-09-02 10:54:20

架構設計權限系統(tǒng)

2009-12-22 18:18:11

WCF客戶端編程

2010-02-22 11:10:17

WCF獲取客戶端IP

2011-03-07 13:50:20

2011-07-01 10:00:11

Ubuntu OneAndroid

2020-11-22 08:10:05

架構運維技術

2009-01-15 09:43:51

Web架構設計緩存

2013-03-20 11:01:37

Redis客戶端連接

2019-07-22 15:59:21

2012-05-24 10:19:42

QQ瀏覽器Android設計分享

2020-05-14 14:48:15

架構模式單庫

2011-08-17 10:10:59

2020-10-19 13:05:32

架構模式

2021-09-22 15:46:29

虛擬桌面瘦客戶端胖客戶端

2021-06-11 00:11:23

GPS數據協(xié)議
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲成人精选 | 国产一区二区影院 | 久久久成人一区二区免费影院 | 午夜网站视频 | 国内精品在线视频 | 久久精品亚洲精品国产欧美kt∨ | 日韩视频一区二区三区 | 国产男女视频网站 | 免费在线一区二区 | 亚洲国产一区在线 | 欧洲一级毛片 | 成人av网站在线观看 | 在线视频成人 | 国产色99精品9i | 午夜影视大全 | 国产成人在线看 | 免费观看一级毛片 | 女同videos另类 | 日韩一区二区在线看 | 手机看片1 | 一级免费看 | 日韩一区二区三区在线视频 | 热99| 精品欧美黑人一区二区三区 | 中文字幕日韩在线 | 精品久久久久久亚洲综合网 | 精品天堂| 久久久久久久国产精品视频 | 免费在线成人 | 亚洲成人自拍 | 国产精品夜夜夜一区二区三区尤 | 伊人久久在线 | 玖玖视频网 | 国产精品一区二区三区在线 | 亚洲欧美日韩一区 | 久久日韩精品一区二区三区 | 一区二区三区视频在线观看 | 九九久久精品 | 看片91 | 午夜精品福利视频 | 国产片一区二区三区 |