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

移動開發中如何整合HTML 5和原生代碼

譯文
移動開發
使用HTML 5開發應用比起為每一種不同的平臺從頭開始編寫代碼,是一種能跨不同平臺而代碼量較少的一種方式。在這種情況下,大部分的用戶界面,甚至全部的界面都可以通過HTML 實現。

最近業界關于移動開發中的究竟使用原生代碼還是HTML 5代碼的爭論依然持續。目前來說有三種方式去進行移動開發

•    原生代碼,
•    hybrid(混合型)移動應用
•    移動Web app(使用HTML5)
使用HTML 5開發應用比起為每一種不同的平臺從頭開始編寫代碼,是一種能跨不同平臺而代碼量較少的一種方式。在這種情況下,大部分的用戶界面,甚至全部的界面都可以通過HTML 實現。

“Hybrid應用”一詞,指的是移動應用大部分是用HTML 5去編寫界面,而部分需要訪問特定設備功能的則使用原生代碼。大部分這些原生代碼并不是自然生成可視的,而僅僅通過特定的轉換將數據返回給應用的HTML 5界面,再經過渲染返回給用戶。PhoneGap這個框架就提供了這樣的功能。

[[88958]]

大部分關于HTML 5的爭論并不是著眼于討論它是否能勝任移動應用中的用戶體驗。Facebook創始人 Mark Zuckerberg在他的對Facebook在使用HTML 5中遇到的困難一文中作了相關評論。Facebook的一些問題可以通過由Sencha Touch框架進行解決(參考SenchaTouch自己的一個例子Fastbook),也可以通過LinkedIn在其infinite scrolling post一文中解決。此外,LinkedIn隨后還提供了使用更多的原生代碼解決關于轉換方向的問題。

為何不進行整合?

與其使用全部的原生代碼或者HTML 5代碼去開發,為什么不將兩者進行整合呢?使用混合型開發移動應用,可以同時利用原生和HTML 5代碼開發用戶界面。這就能讓開發者在設計用戶界面的時候能使用最適合的工具。

很明顯,對于開發用戶應用界面使用兩種或者更多的技術是有其缺點的。最重要的是必須讓開發者既掌握原生的開發又掌握HTML 5。使用原生代碼開發的用戶界面不大容易在其他平臺使用,并需要重新開發。由于這些技術需要更廣泛的知識面和上面提到的困難,為什么還有人嘗試努力使用這它?

HTML 富文檔界面

可能你的用戶體驗告訴你文檔可以以富文本格式進行顯示更為精彩。雖然在iOS上可以使用NSAttributedString進行富本文格式化,但這種文檔的結構并不適合遷移到其他設備平臺中,也不能完整由HTML再現。如果文檔中需要使用如表格進行顯示,則象NSAttributedString的解決方案是不能奏效的??紤]下面的一家基因檢測公司的iPad應用,如下圖,是一個混合型界面的例子:

請注意看上面的圖,很難從表面上分辨這到底哪部分是用原生代碼編寫的,哪部分是用HTML 5編寫的。這個界面中的右邊部分是可變的長度。表格是包含可擴展的行,完全使用CSS3動畫效果,而頁面中其他文字部分都是相同的高度。

Documents such as this are what HTML does best. Trying to create something like this in native code would have entailed considerably greater effort, with little benefit to the user. In this application, the HTML5 is generated with a mustache template, via theGRMustache library for iOS.

像這樣的頁面使用HTML 5實現是最好的。如果嘗試使用原生代碼去實現則要花費很大功夫,而且對用戶好處不多。在這個應用中,通過使用為iOS而設的GRMustache類庫去生成相關的HTML 5模板。下圖中用顏色區別表明了哪個部分是使用HTML5,哪個部分使用原生代碼。

注意當用戶移動滾動條的時候,在右側頂部的文檔頭部會動態改變大小以顯示更多的內容,從而說明用戶界面之間是如何互動的。用戶可以點在HTML 5實現部分的連接和按鈕,以彈出一個內置的Web瀏覽器以加載相關內容。在本文接下來的部分,會看到在原生代碼的界面和HTML 5的界面之間互相連接是很容易的事情。

兩全其美的解決方法

正如我們所看到的,可以在同一個應用的屏幕中整合兩種不同的技術以提供無縫的體驗。你可能希望在iOS上使用UINavigationController和與之相關的 UINavigationBar控件,或者在Android應用中使用action bar,而剩下的部分則使用HTML 5渲染。

下面是一個使用HTML 5設計的購物車應用體驗,但依然有保留原生的導航控件和用于頁面導航的tab工具條:

這里,HTML 5內容實際上是從服務端加載的,它在整個用戶體驗中控制著流程。有相關的API允許服務端生成的HTML去修改導航控件中的內容以適應瀏覽的過程。

你可能會發現原生的控件,如iOS中的MapKit等都提供了比HTML 5控件更好的性能體驗,在這種情況下,應該使用HTML 5代碼去和iOS原生代碼層通信,讓其返回內容并呈現。

上面的兩個圖描述了一個綜合運用原生和HTML 5的購物應用。用戶首先在地圖中選擇了商店地址,然后數據將已HTML 5的方式呈現,用戶并繼續購物。

一旦用戶完成了和地圖的交互,結果則會馬上傳遞到HTML 5頁面中以作進一步處理。原生“控件”可以在任何平臺(iOS、Android、Windows等)中實現,這取決于應用是如何架構,并且能從HTML5應用中調用。使用這個策略,HTML 5代碼將提供對整個應用流程的控制,而原生代碼則在后臺運行等到需要的時候提供數據,如果處理恰當,用戶是很難區分應用的哪一個部分是使用HTML5,哪一個部分是使用原生代碼。

#p#

更容易的應用更新

大多數人都知道為了在iOs或者Android應用中更新應用,都必須重新通過相應平臺正常的渠道去重新提交。對于Ios,可能需要等待七天去獲得蘋果的批準,這就很難在當今移動應用的狂潮中滿足用戶的迫切需要。如果通過HTML和Javascript的方法去給用戶移動體驗,則意味著可以通過Web去動態滿足用戶的需要。

正如前文說的,由于可以通過Web的方法調用應用中的原生代碼,則看上去這其實是一種”橋梁”的機制。PhoneGap就是通過PhoneGap plugin interface去實現。之前的購物商店的例子就是使用這個方法。使用這個方法可以使得購物的流程如果改變后不再需要走繁瑣的原生應用的發布流程。如果一個設計良好的界面可以使得頁面和原生容器之間的通訊變為可能。

關于內容方面

對于來自Web的內容,通常最好能限制其通過這種橋接技術的流量。類似PhoneGap的內置的功能提供了很多設備的信息,其中一些可能會遠遠超出應用程序的所需要。如果由于某種原因Web內容被泄露,比如跨站點腳本攻擊,你可能會突然發現這些內容會以某種方式調用本地代碼。

基于這個原因,當需要使用混合型移動應用的時候,應該使用你自己的橋接代碼,以嚴格控制哪些代碼可以動態加載什么樣的內容。一些平臺有自己的控制方法去限制從Web viewer中對原生代碼的調用。在Android中,類必須明確自己注冊通過JavaScript訪問。在Android 4.2中,可以進一步通過在需要從Web viewer中訪問的方法前添加@ JavaScriptInterface注解去限制其調用。

需要考慮的要點

•     如果用戶在線,直接從Web服務器容是很好的。請記住,移動應用的網絡連接問題。如果網絡連接中斷,會發生什么事? 有的時候可以通過AppCache實現一些離線功能,但這需要規劃。另一種方法在稍后介紹,使用的是本地存儲。

•     托管在Web服務器上的內容需要一定的基礎設施。現在有更多的需要被視為移動應用組成部分的“組件”。它不再僅僅是一些通過AppStore部署的原生代碼和一些后端的服務。必須小心的是在網站更新的時候,不要移動應用程序與現有的接口間發生沖突。請記住,網站的內容發生了變化,則必須更新移動應用程序本身。出于這個原因,可能需要對網站的內容進行必要的版本標記以區分其內容。

•   
蘋果公司對僅僅包裹在原生應用中的Web view這樣類型的應用持悲觀看法。如果你只是在一個原生移動應用中,僅僅包含一些Web內容,則可能在提交iOS的AppStore審批時不會得到批準。蘋果公司希望你的應用程序將使用的一些設備自身的功能,或者好歹使用下Mobile Safari。。想想看,你的應用程序其實可以集成相機功能、聯系人列表、本地數據存儲,離線操作等,還要記得,這些Web view中提供的內容必須和原來應用提交的樣式等保持一致。

如果你創建了一個詞搜索游戲,然后突然開始在webview瀏覽器中有一些風馬牛不相及的內容,這將違反蘋果的條款,并可能在AppStore中下架。根據iOS開發者協議,每次的更新不能改變在提交appStore審核時闡述的該應用的主要功能。

體驗應用更新過程

正如上文提到的,從web服務器加載內容再刷新本地的內容。在這種方法中,只要web內容更新,服務器上的Web內容(HTML,CSS和JavaScript)會下載到移動應用程序客戶端。更新代碼就可以改變用戶的界面,應用流程等,這無需開發人通過使用iOS App Store或Google Play去處理。

注意的是只能更新HTML、CSS和Javascript,是不能更新原生代碼的。蘋果公司允許只要在iOs應用中在UIWebView控件的上下文中運行就可以下載Javascript。

上圖的應用是運行在Android中。HTML的內容是下載到用戶的移動設備中并存儲的。注意應用提供了導航條的用戶體驗。下圖則是運行在iOs的情景,使用的是UINavigationController :

除了加快更新過程,這種傳輸應用程序的方式能確保用戶獲得最新的代碼。在傳統的方法中,應用程序的更新是通過應用程序商店分發,并通知用戶有可用的更新,盡管他們并不需要安裝最新的更新。不是每個人都定期去更新應用程序。而在用戶瀏覽應用的時候,通過更新本地的HTML,CSS和JavaScript正好解決這個問題。

動態更新本地內容的功能,實際上是商業混合型移動應用開發平臺“Trigger.IO”的一部分。 Trigger.IO功能允許更新你移動應用的HTML部分。需要注意的是原生代碼不能以這種方式更新。但是,如果你應用程序的流程控制使用的JavaScript和增強的本機代碼,那么應用程序流程作發生變化的時候同步更新是可能的。

動態更新內容對開發和測試的好處

能動態更新一個移動應用,也可以加速開發進程。 iOS和Android的原生開發過程一般需要在桌面計算機上對應用程序代碼進行編譯,然后轉移到測試設備中去。一些發開發者項目,如Icenium、LiveSync或Ion則支持編譯應用的更新到云端,然后只需下載已經在設備上運行的應用程序,這就完成了更新。

當代碼進行了更改,并保存到服務器上,只需要一個簡單的三個手指頭的手勢動作就可以更新iOS設備上的應用程序。 Icenium也有類似的功能并可用于Android。 Adobe公司的PhoneGap通過一個被稱為Hydration的特性讓更新變得可能。在這種情況下,開發人員可以通過他們的IDE整合到各種設備上,而無需物理連接到這些移動設備,就可以推送新的代碼。

當Hydration功能啟用的時候,每次啟動應用程序時,應用程序將檢查PhoneGap構建服務器去看是否有新版本可用。如果有,用戶將被提示升級到新的版本。這可以使你的應用程序的質量得到保證,以及更容易使用。你將不必擔心簽署代碼更新、配置和測試等問題。

#p#

注意事項

雖然動態更新應用的功能很強,但記住并確保凡是來源于網絡的內容必須不能隨意調用設備的原生代碼。如果不這樣做,可能會導致你的用戶數據暴露給第三方,因此違反與蘋果或Google的協議。此外,動態更新內容不應該改變應用原來的功能。否則會可能被蘋果或者Google下架應用。

溝通兩個世界的橋梁

iOS和Android都提供了能在原生應用中使用的Web view控件,接下來的例子中將講解如何在Web view中使用Javascript調用原生代碼以及原生代碼如何調用在Web view中的Javascript。

[[88959]]

首先你必須使用PhoneGap(Apache Cordova),可以使用PhoneGap 1.4 for iOS 和PhoneGap 1.9 for Android。本文并不從PhoneGap的基礎開始講解,因此讀者可能需要到PhoneGap官網了解一些相關知識。對于iOs可以將CordovaWebView 放到原生的viewController控件中,而Android中也可以使用。這允許你的web代碼能訪問整個PhoneGap API并且訪問任何PhoneGap插件。當然可以根據PhoneGap plugin API 去將應用的原生代碼暴露給CordovaWebView。

假設你不需要使用PhoneGap的全部功能而只是需要在原生和HTML5兩個世界中使用它們部分的功能,則也可以自己去編寫代碼是實現這個橋梁。本文之前提到的三個應用都是使用“私有API”的方式去實現編碼的。

研究iOS中的UIWebView

在iOS中的UIWebView和相關的UIWebViewDelegate提供了相關的機制,并允許原生代碼和Javascript之間互相調用,下面準備了一個簡單的例子進行講解:

注意屏幕中的上半部分,淺灰色部分使用的是UIWebView進行渲染,而下半部分白色的使用的是原生代碼聲稱的。這個應用只是從Web view中傳遞JSON對象到原生代碼中,反之亦然,代碼和Android版本的可以在 這個地址下載:
https://github.com/ptraeg/html5-in-mobile-apps

從Javascript中調用原生代碼

從Javascript中去調用原生代碼很簡單,只是在方法中帶有command和以JSON格式要傳遞的參數,代碼如下:

  1.  var nativeBridge = { 
  2.    invoke: function (commandName, args) { 
  3.       console.log(commandName + ": " + JSON.stringify(args, null, 2)); 
  4.       window.location = 'js-call:' + commandName + ':' + 
  5.                       encodeURIComponent(JSON.stringify(args)); 
  6.   } 
  7. }; 


上面的代碼嘗試去將瀏覽器導航到一個新的URL,但并不是用傳統的http://或file://的URL。這里使用的是自定義協議js-call:,而在Objective-C方面,代碼如下:

 

  1. (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType (UIWebViewNavigationType)navigationType 

在這個調用的委托中,可以查詢傳入的URL是否是來自Javascript的調用:

 

  1. NSString *requestURLString = [[request URL] absoluteString]; 
  2. f ([requestURLString hasPrefix:@"js-call:"]) { 

接下來是判斷command的名字和JSON參數字符串:

 

  1.  NSArray *components = [requestURLString componentsSeparatedByString:@":"]; 
  2. NSString *commandName = (NSString*)[components objectAtIndex:1]; 
  3. NSString *argsAsString = [ (NSString*)[components objectAtIndex:2] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding ]; 

 既然我們將參數作為字符串,則可以將其轉換為NSDictionary對象:

 

  1.  NSError *error = nil; 
  2. NSData *argsData = [argsAsString dataUsingEncoding:NSUTF8StringEncoding]; 
  3. NSDictionary *args = (NSDictionary*)[NSJSONSerialization JSONObjectWithData:argsData options:kNilOptions error:&error]; 

最后,是時候調用正確的原生方法以處理Javascript調用的參數并且刷新原生UI:

 

  1. if ([commandName isEqualToString:@"updateNames"]) { 
  2.    [self updateNativeNameValuesWithFirstName:[args objectForKey:@"fname"
  3.                                     LastName:[args objectForKey:@"lname"]]; 

記得在通過js-call調用方法的時候,必須從委托中返回boolean值為no,以防止Web view嘗試訪問URL。

#p#

iOS原生代碼中訪問Javascript

這種方法比較簡單,因為我們沒有使用委托方法。我們可以簡單通過傳入要調用的JavaScript函數名和需要的參數作為字符串,傳給UIWebView的stringByEvaluatingJavaScriptFromString方法就可以了
在這里,我們從本地視圖中獲得值并且通過NSDictionary將其變成一個JSON對象。

 

  1.  NSDictionary *namesDict = @{@"fname": self.fNameTextField.text,  
  2.                             @"lname": self.lNameTextField.text}; 
  3. NSError *error; 
  4. NSData *namesData = [NSJSONSerialization dataWithJSONObject:namesDict  
  5.                                                     options:0  
  6.                                                       error:&error]; 
  7. NSString *namesJSON = [ [NSString alloc] initWithData:namesData 
  8.                                              encoding:NSUTF8StringEncoding] ; 

既然已經將JSON作為字符串傳遞,只需要格式化命令并傳遞到Web view中就可以了。

 

  1. NSString *jsCommand = [NSString stringWithFormat:@"setNames(%@)", namesJSON]; 
  2. [self.webView stringByEvaluatingJavaScriptFromString:jsCommand]; 

在Android中使用Webview

同樣,下圖是使用Android的Webview實現上面iOS例子的效果:

同樣,上部分灰色的是由Web View生成的,下半部分的是原生代碼生成。

從Javascript代碼中調用原生代碼

Android Web VIEW有個好處就是能將全部的Java類暴露給Javascript,這就不用象iOS那么麻煩了。首先只需要對Android Web view進行注冊設置就可以了,代碼如下:

 

  1. @Override 
  2. public void onStart() { 
  3.    super.onStart(); 
  4.    WebSettings webSettings = webview.getSettings(); 
  5.    webSettings.setJavaScriptEnabled(true); 
  6.    webview.loadUrl("file:///android_asset/webviewContent.html"); 
  7.    webview.addJavaScriptInterface(new WebViewInterface(this), "Android"); 

注意上面最后一行代碼,注冊了一個特殊的類名為WebViewInterface,它可以通過Javacript去訪問,此外,該類還通過字符串“Android”指出其命名空間,還可以測試是否注冊了Android命名空間,代碼為:

 

  1. if (window.Android) { 
  2.    Android.updateNames(JSON.stringify(nameData)); 

而在Java服務端,WebViewInterface是一個標準的Java類,如果使用的是Android 4.2 SDK,則必須使用@JavaScriptInterface去注解要暴露給Javascript的方法。

 

  1. public class WebViewInterface { 
  2.    Context mContext; 
  3.  
  4.    /** Instantiate the interface and set the context */ 
  5.    WebViewInterface(Context c) { 
  6.        mContext = c; 
  7.    } 
  8.  
  9.    @JavaScriptInterface 
  10.    public void updateNames(String namesJsonString) { 
  11.       Log.d(getPackageName(), "Sent from webview: " + namesJsonString); 
  12.       try { 
  13.          JSONObject namesJson = new JSONObject(namesJsonString); 
  14.          final String firstName = namesJson.getString("fname"); 
  15.          final String lastName = namesJson.getString("lname"); 
  16.  
  17.          // When invoked from JavaScript, this is executed on a thread  
  18.          // other than the UI thread. 
  19.          // Because we want to update the native UI controls, we must  
  20.          // create a runnable for the main UI thread. 
  21.          runOnUiThread(new Runnable() { 
  22.             public void run() { 
  23.                fnameEditText.setText(firstName); 
  24.                lnameEditText.setText(lastName); 
  25.              } 
  26.          }); 
  27.       } catch (JSONException e) { 
  28.          Log.e(getPackageName(),  
  29.             "Failed to create JSON object from Web view data"); 
  30.       } 
  31.    } 

注意當Javascript調用這個類中的方法的時候,它們并不是在主UI 線程中執行的,如果我們的目的是更新UI部分,則必須創建新的runnable線程并且傳遞給主UI線程執行:

 

  1. runOnUiThread(new Runnable() { 
  2.    public void run() { 
  3.       fnameEditText.setText(firstName); 
  4.       lnameEditText.setText(lastName); 
  5.    } 
  6. }); 

而在原生代碼中調用Javascript則比較簡單。將數據用JSON的形式存放到字符串中然后就可以給Javascript調用,注意要使用的是Javascript:協議:

 

  1. public void sendNamesToWebView() { 
  2.    JSONObject namesJson = new JSONObject(); 
  3.    try { 
  4.       namesJson.put("fname", fnameEditText.getText().toString()); 
  5.       namesJson.put("lname", lnameEditText.getText().toString()); 
  6.       webview.loadUrl( "JavaScript:setNames(" + namesJson.toString() + ")" ); 
  7.    } catch (JSONException e) { 
  8.       Log.e(getPackageName(),  
  9.         "Failed to create JSON object for Web view"); 
  10.    } 

WebView的缺點分析

和軟件開發的大多數情況一樣,我們必須審視WebView的一些缺點,特別是在制作混合移動應用時要注意:

•     不是所有的Web view都渲染成同樣的效果 在過去的兩年中,Web view的功能有明顯提高。然而,你的用戶群可能不會運行Android或iOS的最新和最優秀的版本。 Android平臺在Web view方面已普遍落后于iOS。Google似乎在重新努力來改善這種情況,但那些停留在舊版本的Android(尤其是Ice Cream Sandwich 4.x版本前)的用戶可能會發現Web view的性能尤其是復雜的表格,其效果和在iOS上呈現的并不一樣。開發人員需要在Android 2.2,2.3和4.x上測試HTML5代碼以確保內容呈現的一致。
•    性能問題
Web view的性能通常是比不上原生代碼。然而,在許多情況下,你可能會發現性能是完全可以接受的,特別是在當前的硬件飛速發展的情況下。請記住,原生代碼和代碼Web view部分調用的銜接性能會有一定的影響。但一般來說,不會要求在毫秒級完成調用,對于大多數應用,這是不是一個問題。
•    滾動條問題

用戶可能無法區分Web view的滾動條滾動方式和應用程序原生代碼滾動方式的不同。這其中的一些問題可以通過在新版本的WebKit中使用CSS3的-webkit-overflow-scrolling:touch屬性去解決。你可能也想看看解決方案,如FTScroller 和 iScroll。
•    復雜性

正如前面提到的,如果將內容從Web服務器中下載到移動設備,必須確保擁有必要的基礎設施來支持。因為現在應用程序既有原生代碼,也有Web服務器和潛在的后端服務參與,這會存在更多的故障點。你必須針對早期部署版本的應用程序做好兼容性測試,以確保當有應用更新的時候,現有版本的應用程序仍然運行。
•    遵守開發者協議

開發者的你必須熟悉每一類移動應用商店對web方面內容的限制。正如本文所提到的,特別是在iOS平臺上要額外注意,否則會不允許發布甚至下架。
•    安全性

如前所述對于所有來源于web的內容都應該持有懷疑的態度,尤其是如果將和你的本機原生代碼交互的。確保你只暴露你需要使用的原生代碼部分。如果不這樣做,可能會導致一些Web的惡意代碼能調用本機代碼。必須確保將Web的安全實踐考慮進來以確保Web代碼的安全調用。

鑒于一個混合型移動應用所帶來的靈活性,如果你發現Web view的性能在交互方面不夠優秀,則可以在應用中編寫原生代碼。當然,編寫更多的本機代碼,則在平臺遷移的時候需要更多的重新改寫,這是一種利弊權衡??偟膩碚f,混合型移動應用的解決方案能讓你最大限度掌控應用的性能和靈活性。

小結
移動應用開發中使用HTML5目前是個有點爭議性的話題。然而,重要的是要作為開發者,必須了解每一種解決方案的優缺點,了解什么樣的策略是適合你的應用程序,這些都是由需求決定的。盡管移動HTML5的解決方案在最近幾個月已經有一些負面的宣傳,但本文還是講解了使用HTML5的好處。
無論采用什么方法開發移動應用,必須保持性能的高效和安全性,并且盡早測試,采用多種設備測試。在這篇文章中,我們已經看到,HTML5帶來了許多好處,特別是采用混合HTML 5和原生代碼開發,則給開發者更高的自由度選擇。

責任編輯:張葉青 來源: 51CTO
相關推薦

2011-08-29 17:27:47

HTML 5交互移動應用

2012-02-20 13:45:26

HTML5移動開發程序

2014-03-18 09:20:17

HTML5移動開發

2015-10-13 11:49:06

移動·開發技術周刊

2015-07-03 11:07:39

HTML5移動Web

2015-01-12 09:52:08

移動應用原生混合

2015-01-12 12:11:10

移動應用原生混合

2011-08-10 13:44:22

HTML 5

2011-12-12 10:08:39

jQuery MobiHTML5

2011-12-28 15:32:46

HTML5移動App

2015-10-23 13:44:14

巴巴獵

2011-12-08 11:01:45

HTML 5

2011-05-25 09:34:30

HTML5cssjavascript

2014-12-22 15:02:48

HTML5移動應用開發

2012-02-23 10:28:43

AppCanHTML5移動應用

2013-06-27 14:33:00

2014-10-21 17:34:11

HTML5移動設計

2015-03-26 14:04:57

2011-07-11 10:43:48

2014-11-12 16:00:12

火舞游戲
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 麻豆毛片| 成人在线观看免费视频 | 欧美精品一区二区蜜桃 | 精品国产一区久久 | 综合精品 | 成人小视频在线 | 久久精品中文 | 日本福利在线观看 | 国产xxxx岁13xxxxhd | 日韩在线一区二区三区 | 国产一区二区毛片 | 国产精品乱码一区二区三区 | 99精品一区二区三区 | 欧美日韩成人网 | 午夜电影网站 | 日本激情视频在线播放 | 亚洲视频二区 | 国产一区二区毛片 | 五月天激情综合网 | 免费视频久久 | 91在线| 日批免费看 | 久久一本| 一区免费观看 | 亚洲精品一二三 | 成人国产精品久久 | 精品一区国产 | 欧美日在线 | 亚洲精品一区久久久久久 | 欧美综合一区 | 成人免费观看男女羞羞视频 | 亚洲精品成人免费 | 91看片免费版 | 国产欧美日韩精品一区二区三区 | 亚洲一区久久 | 亚洲成人综合在线 | 欧美精品一区二区免费 | av中文字幕在线观看 | 涩涩导航 | 亚洲视频一区在线观看 | 色必久久 |