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

Android App的設(shè)計(jì)架構(gòu)MVC MVP MVVM與架構(gòu)經(jīng)驗(yàn)談

移動開發(fā) Android
和MVC框架模式一樣,Model模型處理數(shù)據(jù)代碼不變在Android的App開發(fā)中,很多人經(jīng)常會頭疼于App的架構(gòu)如何設(shè)計(jì)。本文就來帶你分析一下這幾個架構(gòu)的特性,優(yōu)缺點(diǎn),以及App架構(gòu)設(shè)計(jì)中應(yīng)該注意的問題。

和MVC框架模式一樣,Model模型處理數(shù)據(jù)代碼不變在Android的App開發(fā)中,很多人經(jīng)常會頭疼于App的架構(gòu)如何設(shè)計(jì):

  • 我的App需要應(yīng)用這些設(shè)計(jì)架構(gòu)嗎?
  • MVC,MVP等架構(gòu)講的是什么?區(qū)別是什么?

本文就來帶你分析一下這幾個架構(gòu)的特性,優(yōu)缺點(diǎn),以及App架構(gòu)設(shè)計(jì)中應(yīng)該注意的問題。

1.架構(gòu)設(shè)計(jì)的目的

通過設(shè)計(jì)使程序模塊化,做到模塊內(nèi)部的高聚合和模塊之間的低耦合。這樣做的好處是使得程序在開發(fā)的過程中,開發(fā)人員只需要專注于一點(diǎn),提高程序開發(fā)的效率,并且更容易進(jìn)行后續(xù)的測試以及定位問題。但設(shè)計(jì)不能違背目的,對于不同量級的工程,具體架構(gòu)的實(shí)現(xiàn)方式必然是不同的,切忌犯為了設(shè)計(jì)而設(shè)計(jì),為了架構(gòu)而架構(gòu)的毛病。

舉個簡單的例子:

  • 一個Android App如果只有3個Java文件,那只需要做點(diǎn)模塊和層次的劃分就可以,引入框架或者架構(gòu)反而提高了工作量,降低了生產(chǎn)力;

但如果當(dāng)前開發(fā)的App最終代碼量在10W行以上,本地需要進(jìn)行復(fù)雜操作,同時也需要考慮到與其余的Android開發(fā)者以及后臺開發(fā)人員之間的同步配合,那就需要在架構(gòu)上進(jìn)行一些思考!

2.MVC設(shè)計(jì)架構(gòu) 

微信截圖_20151201004323.png

MVC簡介

MVC全名是Model View Controller,如圖,是模型(model)-視圖(view)-控制器(controller)的縮寫,一種軟件設(shè)計(jì)典范,用一種業(yè)務(wù)邏輯、數(shù)據(jù)、界面顯示分離的方法組織代碼,在改進(jìn)和個性化定制界面及用戶交互的同時,不需要重新編寫業(yè)務(wù)邏輯。

其中M層處理數(shù)據(jù),業(yè)務(wù)邏輯等;V層處理界面的顯示結(jié)果;C層起到橋梁的作用,來控制V層和M層通信以此來達(dá)到分離視圖顯示和業(yè)務(wù)邏輯層。

Android中的MVC

Android中界面部分也采用了當(dāng)前比較流行的MVC框架,在Android中:

視圖層(View)

一般采用XML文件進(jìn)行界面的描述,這些XML可以理解為AndroidApp的View。使用的時候可以非常方便的引入。同時便于后期界面的修改。邏輯中與界面對應(yīng)的id不變化則代碼不用修改,大大增強(qiáng)了代碼的可維護(hù)性。

控制層(Controller)

Android的控制層的重任通常落在了眾多的Activity的肩上。這句話也就暗含了不要在Activity中寫代碼,要通過Activity交割Model業(yè)務(wù)邏輯層處理,這樣做的另外一個原因是Android中的Actiivity的響應(yīng)時間是5s,如果耗時的操作放在這里,程序就很容易被回收掉。

模型層(Model)

我們針對業(yè)務(wù)模型,建立的數(shù)據(jù)結(jié)構(gòu)和相關(guān)的類,就可以理解為AndroidApp的Model,Model是與View無關(guān),而與業(yè)務(wù)相關(guān)的(感謝@Xander的講解)。對數(shù)據(jù)庫的操作、對網(wǎng)絡(luò)等的操作都應(yīng)該在Model里面處理,當(dāng)然對業(yè)務(wù)計(jì)算等操作也是必須放在的該層的。就是應(yīng)用程序中二進(jìn)制的數(shù)據(jù)。

MVC代碼實(shí)例

我們來看看MVC在Android開發(fā)中是怎么應(yīng)用的吧!

先上界面圖 

20150606093217165.png

Controller控制器&View 

  1. public class MainActivity extends ActionBarActivity implements OnWeatherListener, View.OnClickListener { 
  2.  
  3.     private WeatherModel weatherModel; 
  4.     private EditText cityNOInput; 
  5.     private TextView city; 
  6.     ... 
  7.  
  8.     @Override 
  9.     protected void onCreate(Bundle savedInstanceState) { 
  10.         super.onCreate(savedInstanceState); 
  11.         setContentView(R.layout.activity_main); 
  12.         weatherModel = new WeatherModelImpl(); 
  13.         initView(); 
  14.     } 
  15.  
  16.     //初始化View 
  17.     private void initView() { 
  18.         cityNOInput = findView(R.id.et_city_no); 
  19.         city = findView(R.id.tv_city); 
  20.         ... 
  21.         findView(R.id.btn_go).setOnClickListener(this); 
  22.     } 
  23.  
  24.     //顯示結(jié)果 
  25.     public void displayResult(Weather weather) { 
  26.         WeatherInfo weatherInfo = weather.getWeatherinfo(); 
  27.         city.setText(weatherInfo.getCity()); 
  28.         ... 
  29.     } 
  30.  
  31.     @Override 
  32.     public void onClick(View v) { 
  33.         switch (v.getId()) { 
  34.             case R.id.btn_go: 
  35.                 weatherModel.getWeather(cityNOInput.getText().toString().trim(), this); 
  36.                 break; 
  37.         } 
  38.     } 
  39.  
  40.     @Override 
  41.     public void onSuccess(Weather weather) { 
  42.         displayResult(weather); 
  43.     } 
  44.  
  45.     @Override 
  46.     public void onError() { 
  47.         Toast.makeText(this, 獲取天氣信息失敗, Toast.LENGTH_SHORT).show(); 
  48.     } 
  49.  
  50.     private T findView(int id) { 
  51.         return (T) findViewById(id); 
  52.     } 

從上面代碼可以看到,Activity持有了WeatherModel模型的對象,當(dāng)用戶有點(diǎn)擊Button交互的時候,Activity作為Controller控制層讀取View視圖層EditTextView的數(shù)據(jù),然后向Model模型發(fā)起數(shù)據(jù)請求,也就是調(diào)用WeatherModel對象的方法 getWeather()方法。當(dāng)Model模型處理數(shù)據(jù)結(jié)束后,通過接口OnWeatherListener通知View視圖層數(shù)據(jù)處理完畢,View視圖層該更新界面UI了。然后View視圖層調(diào)用displayResult()方法更新UI。至此,整個MVC框架流程就在Activity中體現(xiàn)出來了。

Model模型

來看看WeatherModelImpl代碼實(shí)現(xiàn) 

  1. public interface WeatherModel { 
  2.     void getWeather(String cityNumber, OnWeatherListener listener); 
  3.  
  4. ................ 
  5.  
  6. public class WeatherModelImpl implements WeatherModel { 
  7.     /*這部分代碼范例有問題,網(wǎng)絡(luò)訪問不應(yīng)該在Model中,應(yīng)該把網(wǎng)絡(luò)訪問換成從數(shù)據(jù)庫讀取*/ 
  8.     @Override 
  9.     public void getWeather(String cityNumber, final OnWeatherListener listener) { 
  10.  
  11.         /*數(shù)據(jù)層操作*/ 
  12.         VolleyRequest.newInstance().newGsonRequest(http://www.weather.com.cn/data/sk/ + cityNumber + .html, 
  13.                 Weather.class, new Response.Listener<weather>() { 
  14.                     @Override 
  15.                     public void onResponse(Weather weather) { 
  16.                         if (weather != null) { 
  17.                             listener.onSuccess(weather); 
  18.                         } else { 
  19.                             listener.onError(); 
  20.                         } 
  21.                     } 
  22.                 }, new Response.ErrorListener() { 
  23.                     @Override 
  24.                     public void onErrorResponse(VolleyError error) { 
  25.                         listener.onError(); 
  26.                     } 
  27.                 }); 
  28.     } 

以上代碼看出,這里設(shè)計(jì)了一個WeatherModel模型接口,然后實(shí)現(xiàn)了接口WeatherModelImpl類。controller控制器activity調(diào)用WeatherModelImpl類中的方法發(fā)起網(wǎng)絡(luò)請求,然后通過實(shí)現(xiàn)OnWeatherListener接口來獲得網(wǎng)絡(luò)請求的結(jié)果通知View視圖層更新UI 。至此,Activity就將View視圖顯示和Model模型數(shù)據(jù)處理隔離開了。activity擔(dān)當(dāng)contronller完成了model和view之間的協(xié)調(diào)作用。

至于這里為什么不直接設(shè)計(jì)成類里面的一個getWeather()方法直接請求網(wǎng)絡(luò)數(shù)據(jù)?你考慮下這種情況:現(xiàn)在代碼中的網(wǎng)絡(luò)請求是使用Volley框架來實(shí)現(xiàn)的,如果哪天老板非要你使用Afinal框架實(shí)現(xiàn)網(wǎng)絡(luò)請求,你怎么解決問題?難道是修改 getWeather()方法的實(shí)現(xiàn)? no no no,這樣修改不僅破壞了以前的代碼,而且還不利于維護(hù), 考慮到以后代碼的擴(kuò)展和維護(hù)性,我們選擇設(shè)計(jì)接口的方式來解決著一個問題,我們實(shí)現(xiàn)另外一個WeatherModelWithAfinalImpl類,繼承自WeatherModel,重寫里面的方法,這樣不僅保留了以前的WeatherModelImpl類請求網(wǎng)絡(luò)方式,還增加了WeatherModelWithAfinalImpl類的請求方式。Activity調(diào)用代碼無需要任何修改。

3.MVP設(shè)計(jì)架構(gòu)

在App開發(fā)過程中,經(jīng)常出現(xiàn)的問題就是某一部分的代碼量過大,雖然做了模塊劃分和接口隔離,但也很難完全避免。從實(shí)踐中看到,這更多的出現(xiàn)在UI部分,也就是Activity里。想象一下,一個2000+行以上基本不帶注釋的Activity,我的第一反應(yīng)就是想吐。Activity內(nèi)容過多的原因其實(shí)很好解釋,因?yàn)锳ctivity本身需要擔(dān)負(fù)與用戶之間的操作交互,界面的展示,不是單純的Controller或View。而且現(xiàn)在大部分的Activity還對整個App起到類似IOS中的【ViewController】的作用,這又帶入了大量的邏輯代碼,造成Activity的臃腫。為了解決這個問題,讓我們引入MVP框架。

MVC的缺點(diǎn)

在Android開發(fā)中,Activity并不是一個標(biāo)準(zhǔn)的MVC模式中的Controller,它的首要職責(zé)是加載應(yīng)用的布局和初始化用戶 界面,并接受并處理來自用戶的操作請求,進(jìn)而作出響應(yīng)。隨著界面及其邏輯的復(fù)雜度不斷提升,Activity類的職責(zé)不斷增加,以致變得龐大臃腫。

什么是MVP?

MVP從更早的MVC框架演變過來,與MVC有一定的相似性:Controller/Presenter負(fù)責(zé)邏輯的處理,Model提供數(shù)據(jù),View負(fù)責(zé)顯示。 

Clipboard Image.png

MVP框架由3部分組成:View負(fù)責(zé)顯示,Presenter負(fù)責(zé)邏輯處理,Model提供數(shù)據(jù)。在MVP模式里通常包含3個要素(加上View interface是4個):

  • View:負(fù)責(zé)繪制UI元素、與用戶進(jìn)行交互(在Android中體現(xiàn)為Activity)
  • Model:負(fù)責(zé)存儲、檢索、操縱數(shù)據(jù)(有時也實(shí)現(xiàn)一個Model interface用來降低耦合)
  • Presenter:作為View與Model交互的中間紐帶,處理與用戶交互的負(fù)責(zé)邏輯。
  • *View interface:需要View實(shí)現(xiàn)的接口,View通過View interface與Presenter進(jìn)行交互,降低耦合,方便進(jìn)行單元測試

Tips:*View interface的必要性

回想一下你在開發(fā)Android應(yīng)用時是如何對代碼邏輯進(jìn)行單元測試的?是否每次都要將應(yīng)用部署到Android模擬器或真機(jī)上,然后通過模擬用 戶操作進(jìn)行測試?然而由于Android平臺的特性,每次部署都耗費(fèi)了大量的時間,這直接導(dǎo)致開發(fā)效率的降低。而在MVP模式中,處理復(fù)雜邏輯的Presenter是通過interface與View(Activity)進(jìn)行交互的,這說明我們可以通過自定義類實(shí)現(xiàn)這個interface來模擬Activity的行為對Presenter進(jìn)行單元測試,省去了大量的部署及測試的時間。

MVC → MVP

當(dāng)我們將Activity復(fù)雜的邏輯處理移至另外的一個類(Presenter)中時,Activity其實(shí)就是MVP模式中的View,它負(fù)責(zé)UI元素的初始化,建立UI元素與Presenter的關(guān)聯(lián)(Listener之類),同時自己也會處理一些簡單的邏輯(復(fù)雜的邏輯交由 Presenter處理)。

MVP的Presenter是框架的控制者,承擔(dān)了大量的邏輯操作,而MVC的Controller更多時候承擔(dān)一種轉(zhuǎn)發(fā)的作用。因此在App中引入MVP的原因,是為了將此前在Activty中包含的大量邏輯操作放到控制層中,避免Activity的臃腫。

兩種模式的主要區(qū)別:

  • (最主要區(qū)別)View與Model并不直接交互,而是通過與Presenter交互來與Model間接交互。而在MVC中View可以與Model直接交互
  • 通常View與Presenter是一對一的,但復(fù)雜的View可能綁定多個Presenter來處理邏輯。而Controller是基于行為的,并且可以被多個View共享,Controller可以負(fù)責(zé)決定顯示哪個View
  • Presenter與View的交互是通過接口來進(jìn)行的,更有利于添加單元測試。 
MVC與MVP區(qū)別

因此我們可以發(fā)現(xiàn)MVP的優(yōu)點(diǎn)如下:

  1. 模型與視圖完全分離,我們可以修改視圖而不影響模型;
  2. 可以更高效地使用模型,因?yàn)樗械慕换ザ及l(fā)生在一個地方——Presenter內(nèi)部;
  3. 我們可以將一個Presenter用于多個視圖,而不需要改變Presenter的邏輯。這個特性非常的有用,因?yàn)橐晥D的變化總是比模型的變化頻繁;
  4. 如果我們把邏輯放在Presenter中,那么我們就可以脫離用戶接口來測試這些邏輯(單元測試)。

具體到Android App中,一般可以將App根據(jù)程序的結(jié)構(gòu)進(jìn)行縱向劃分,根據(jù)MVP可以將App分別為模型層(M),UI層(V)和邏輯層(P)。

UI層一般包括Activity,F(xiàn)ragment,Adapter等直接和UI相關(guān)的類,UI層的Activity在啟動之后實(shí)例化相應(yīng)的Presenter,App的控制權(quán)后移,由UI轉(zhuǎn)移到Presenter,兩者之間的通信通過BroadCast、Handler或者接口完成,只傳遞事件和結(jié)果。

舉個簡單的例子,UI層通知邏輯層(Presenter)用戶點(diǎn)擊了一個Button,邏輯層(Presenter)自己決定應(yīng)該用什么行為進(jìn)行響應(yīng),該找哪個模型(Model)去做這件事,最后邏輯層(Presenter)將完成的結(jié)果更新到UI層。

**MVP的變種:Passive View

MVP的變種有很多,其中使用最廣泛的是Passive View模式,即被動視圖。在這種模式下,View和Model之間不能直接交互,View通過Presenter與Model打交道。Presenter接受View的UI請求,完成簡單的UI處理邏輯,并調(diào)用Model進(jìn)行業(yè)務(wù)處理,并調(diào)用View將相應(yīng)的結(jié)果反映出來。View直接依賴Presenter,但是Presenter間接依賴View,它直接依賴的是View實(shí)現(xiàn)的接口。 

clip_image002[4]_thumb.jpg

相對于View的被動,那Presenter就是主動的一方。對于Presenter的主動,有如下的理解:

  • Presenter是整個MVP體系的控制中心,而不是單純的處理View請求的人;
  • View僅僅是用戶交互請求的匯報(bào)者,對于響應(yīng)用戶交互相關(guān)的邏輯和流程,View不參與決策,真正的決策者是Presenter;
  • View向Presenter發(fā)送用戶交互請求應(yīng)該采用這樣的口吻:“我現(xiàn)在將用戶交互請求發(fā)送給你,你看著辦,需要我的時候我會協(xié)助你”,不應(yīng)該是這樣:“我現(xiàn)在處理用戶交互請求了,我知道該怎么辦,但是我需要你的支持,因?yàn)閷?shí)現(xiàn)業(yè)務(wù)邏輯的Model只信任你”;
  • 對于綁定到View上的數(shù)據(jù),不應(yīng)該是View從Presenter上“拉”回來的,應(yīng)該是Presenter主動“推”給View的;
  • View盡可能不維護(hù)數(shù)據(jù)狀態(tài),因?yàn)槠浔旧韮H僅實(shí)現(xiàn)單純的、獨(dú)立的UI操作;Presenter才是整個體系的協(xié)調(diào)者,它根據(jù)處理用于交互的邏輯給View和Model安排工作。

MVP架構(gòu)存在的問題與解決辦法

加入模板方法(Template Method)

轉(zhuǎn)移邏輯操作之后可能部分較為復(fù)雜的Activity內(nèi)代碼量還是不少,于是需要在分層的基礎(chǔ)上再加入模板方法(Template Method)。

具體做法是在Activity內(nèi)部分層。其中最頂層為BaseActivity,不做具體顯示,而是提供一些基礎(chǔ)樣式,Dialog,ActionBar在內(nèi)的內(nèi)容,展現(xiàn)給用戶的Activity繼承BaseActivity,重寫B(tài)aseActivity預(yù)留的方法。如有必要再進(jìn)行二次繼承,App中Activity之間的繼承次數(shù)最多不超過3次。

Model內(nèi)部分層

模型層(Model)中的整體代碼量是最大的,一般由大量的Package組成,針對這部分需要做的就是在程序設(shè)計(jì)的過程中,做好模塊的劃分,進(jìn)行接口隔離,在內(nèi)部進(jìn)行分層。

強(qiáng)化Presenter

強(qiáng)化Presenter的作用,將所有邏輯操作都放在Presenter內(nèi)也容易造成Presenter內(nèi)的代碼量過大,對于這點(diǎn),有一個方法是在UI層和Presenter之間設(shè)置中介者M(jìn)ediator,將例如數(shù)據(jù)校驗(yàn)、組裝在內(nèi)的輕量級邏輯操作放在Mediator中;在Presenter和Model之間使用代理Proxy;通過上述兩者分擔(dān)一部分Presenter的邏輯操作,但整體框架的控制權(quán)還是在Presenter手中。Mediator和Proxy不是必須的,只在Presenter負(fù)擔(dān)過大時才建議使用。

最終的架構(gòu)如下圖所示: 

1422253914_8854.jpg

MVP代碼實(shí)例

我們來看看MVP在Android開發(fā)中是怎么應(yīng)用的吧!!

我們用另一個例子來解釋。

先來看包結(jié)構(gòu)圖 

20140928093820322.png

建立Bean 

  1. public class UserBean { 
  2.      private String mFirstName; 
  3.      private String mLastName; 
  4.      public UserBean(String firstName, String lastName) { 
  5.             this. mFirstName = firstName; 
  6.             this. mLastName = lastName; 
  7.      } 
  8.      public String getFirstName() { 
  9.             return mFirstName; 
  10.      } 
  11.      public String getLastName() { 
  12.             return mLastName; 
  13.      } 

建立Model

(處理業(yè)務(wù)邏輯,這里指數(shù)據(jù)讀寫),先寫接口,后寫實(shí)現(xiàn) 

  1. public interface IUserModel { 
  2.      void setID(int id);  
  3.      void setFirstName(String firstName);  
  4.      void setLastName(String lastName);  
  5.      int getID();  
  6.      UserBean load(int id);// 通過id讀取user信息,返回一個UserBean 

實(shí)現(xiàn)不在這里寫了

Presenter控制器

建立presenter(主導(dǎo)器,通過iView和iModel接口操作model和view),activity可以把所有邏輯給presenter處理,這樣java邏輯就從手機(jī)的activity中分離出來。 

  1. public class UserPresenter { 
  2.      private IUserView mUserView; 
  3.      private IUserModel mUserModel; 
  4.  
  5.      public UserPresenter(IUserView view) { 
  6.             mUserView = view
  7.             mUserModel = new UserModel(); 
  8.      } 
  9.  
  10.      public void saveUser( int id, String firstName, String lastName) { 
  11.             mUserModel.setID(id); 
  12.             mUserModel.setFirstName(firstName); 
  13.             mUserModel.setLastName(lastName); 
  14.      } 
  15.  
  16.      public void loadUser( int id) { 
  17.            UserBean user = mUserModel.load(id); 
  18.             mUserView.setFirstName(user.getFirstName()); // 通過調(diào)用IUserView的方法來更新顯示 
  19.             mUserView.setLastName(user.getLastName()); 
  20.      } 

View視圖

建立view(更新ui中的view狀態(tài)),這里列出需要操作當(dāng)前view的方法,也是接口 

  1. public interface IUserView { 
  2.      int getID();  
  3.      String getFristName();  
  4.      String getLastName();  
  5.      void setFirstName(String firstName);  
  6.      void setLastName(String lastName); 

activity中實(shí)現(xiàn)iview接口,在其中操作view,實(shí)例化一個presenter變量。 

  1. public class MainActivity extends Activity implements OnClickListener,IUserView { 
  2.  
  3.      UserPresenter presenter; 
  4.      EditText id,first,last
  5.      @Override 
  6.      protected void onCreate(Bundle savedInstanceState) { 
  7.             super.onCreate(savedInstanceState); 
  8.            setContentView(R.layout. activity_main); 
  9.  
  10.            findViewById(R.id. save).setOnClickListener( this); 
  11.            findViewById(R.id. load).setOnClickListener( this); 
  12.             id = (EditText) findViewById(R.id. id); 
  13.             first = (EditText) findViewById(R.id. first); 
  14.             last = (EditText) findViewById(R.id. last); 
  15.  
  16.             presenter = new UserPresenter( this); 
  17.      } 
  18.  
  19.      @Override 
  20.      public void onClick(View v) { 
  21.             switch (v.getId()) { 
  22.             case R.id. save: 
  23.                  presenter.saveUser(getID(), getFristName(), getLastName()); 
  24.                  break; 
  25.             case R.id. load
  26.                  presenter.loadUser(getID()); 
  27.                  break; 
  28.             default
  29.                  break; 
  30.            } 
  31.      } 
  32.  
  33.      @Override 
  34.      public int getID() { 
  35.             return new Integer( id.getText().toString()); 
  36.      } 
  37.  
  38.      @Override 
  39.      public String getFristName() { 
  40.             return first.getText().toString(); 
  41.      } 
  42.  
  43.      @Override 
  44.      public String getLastName() { 
  45.             return last.getText().toString(); 
  46.      } 
  47.  
  48.      @Override 
  49.      public void setFirstName(String firstName) { 
  50.             first.setText(firstName); 
  51.      } 
  52.  
  53.      @Override 
  54.      public void setLastName(String lastName) { 
  55.             last.setText(lastName); 
  56.      } 
  57.  

因此,Activity及從MVC中的Controller中解放出來了,這會Activity主要做顯示View的作用和用戶交互。每個Activity可以根據(jù)自己顯示View的不同實(shí)現(xiàn)View視圖接口IUserView。

通過對比同一實(shí)例的MVC與MVP的代碼,可以證實(shí)MVP模式的一些優(yōu)點(diǎn):

  • 在MVP中,Activity的代碼不臃腫;
  • 在MVP中,Model(IUserModel的實(shí)現(xiàn)類)的改動不會影響Activity(View),兩者也互不干涉,而在MVC中會;
  • 在MVP中,IUserView這個接口可以實(shí)現(xiàn)方便地對Presenter的測試;
  • 在MVP中,UserPresenter可以用于多個視圖,但是在MVC中的Activity就不行。

4.MVC、MVP與MVVM的關(guān)系

首先介紹下MVVM。

MVVM

MVVM可以算是MVP的升級版,其中的VM是ViewModel的縮寫,ViewModel可以理解成是View的數(shù)據(jù)模型和Presenter的合體,ViewModel和View之間的交互通過Data Binding完成,而Data Binding可以實(shí)現(xiàn)雙向的交互,這就使得視圖和控制層之間的耦合程度進(jìn)一步降低,關(guān)注點(diǎn)分離更為徹底,同時減輕了Activity的壓力。

在比較之前,先從圖上看看三者的異同。 

2f9e4ee7d9616257ab41de204c06ffd5_b.jpg

剛開始理解這些概念的時候認(rèn)為這幾種模式雖然都是要將view和model解耦,但是非此即彼,沒有關(guān)系,一個應(yīng)用只會用一種模式。后來慢慢發(fā)現(xiàn)世界絕對不是只有黑白兩面,中間最大的一塊其實(shí)是灰色地帶,同樣,這幾種模式的邊界并非那么明顯,可能你在自己的應(yīng)用中都會用到。實(shí)際上也根本沒必要去糾結(jié)自己到底用的是MVC、MVP還是MVVP,不管黑貓白貓,捉住老鼠就是好貓。

MVC->MVP->MVVM演進(jìn)過程

MVC -> MVP -> MVVM 這幾個軟件設(shè)計(jì)模式是一步步演化發(fā)展的,MVVM 是從 MVP 的進(jìn)一步發(fā)展與規(guī)范,MVP 隔離了MVC中的 M 與 V 的直接聯(lián)系后,靠 Presenter 來中轉(zhuǎn),所以使用 MVP 時 P 是直接調(diào)用 View 的接口來實(shí)現(xiàn)對視圖的操作的,這個 View 接口的東西一般來說是 showData、showLoading等等。M 與 V已經(jīng)隔離了,方便測試了,但代碼還不夠優(yōu)雅簡潔,所以 MVVM 就彌補(bǔ)了這些缺陷。在 MVVM 中就出現(xiàn)的 Data Binding 這個概念,意思就是 View 接口的 showData 這些實(shí)現(xiàn)方法可以不寫了,通過 Binding 來實(shí)現(xiàn)。

如果把這三者放在一起比較,先說一下三者的共同點(diǎn),也就是Model和View:

  • Model:數(shù)據(jù)對象,同時,提供本應(yīng)用外部對應(yīng)用程序數(shù)據(jù)的操作的接口,也可能在數(shù)據(jù)變化時發(fā)出變更通知。Model不依賴于View的實(shí)現(xiàn),只要外部程序調(diào)用Model的接口就能夠?qū)崿F(xiàn)對數(shù)據(jù)的增刪改查。
  • View:UI層,提供對最終用戶的交互操作功能,包括UI展現(xiàn)代碼及一些相關(guān)的界面邏輯代碼。

三者的差異在于如何粘合View和Model,實(shí)現(xiàn)用戶的交互操作以及變更通知

Controller

Controller接收View的操作事件,根據(jù)事件不同,或者調(diào)用Model的接口進(jìn)行數(shù)據(jù)操作,或者進(jìn)行View的跳轉(zhuǎn),從而也意味著一個Controller可以對應(yīng)多個View。Controller對View的實(shí)現(xiàn)不太關(guān)心,只會被動地接收,Model的數(shù)據(jù)變更不通過Controller直接通知View,通常View采用觀察者模式監(jiān)聽Model的變化。

Presenter

Presenter與Controller一樣,接收View的命令,對Model進(jìn)行操作;與Controller不同的是Presenter會反作用于View,Model的變更通知首先被Presenter獲得,然后Presenter再去更新View。一個Presenter只對應(yīng)于一個View。根據(jù)Presenter和View對邏輯代碼分擔(dān)的程度不同,這種模式又有兩種情況:Passive View和Supervisor Controller。

ViewModel

注意這里的“Model”指的是View的Model,跟MVVM中的一個Model不是一回事。所謂View的Model就是包含View的一些數(shù)據(jù)屬性和操作的這么一個東東,這種模式的關(guān)鍵技術(shù)就是數(shù)據(jù)綁定(data binding),View的變化會直接影響ViewModel,ViewModel的變化或者內(nèi)容也會直接體現(xiàn)在View上。這種模式實(shí)際上是框架替應(yīng)用開發(fā)者做了一些工作,開發(fā)者只需要較少的代碼就能實(shí)現(xiàn)比較復(fù)雜的交互。

一點(diǎn)心得

MVP和MVVM完全隔離了Model和View,但是在有些情況下,數(shù)據(jù)從Model到ViewModel或者Presenter的拷貝開銷很大,可能也會結(jié)合MVC的方式,Model直接通知View進(jìn)行變更。在實(shí)際的應(yīng)用中很有可能你已經(jīng)在不知不覺中將幾種模式融合在一起,但是為了代碼的可擴(kuò)展、可測試性,必須做到模塊的解耦,不相關(guān)的代碼不要放在一起。網(wǎng)上有一個故事講,一個人在一家公司做一個新產(chǎn)品時,一名外包公司的新員工直接在View中做了數(shù)據(jù)庫持久化操作,而且一個hibernate代碼展開后發(fā)現(xiàn)竟然有幾百行的SQL語句,搞得他們驚訝不已,一時成為笑談。

個人理解,在廣義地談?wù)揗VC架構(gòu)時,并非指本文中嚴(yán)格定義的MVC,而是指的MV*,也就是視圖和模型的分離,只要一個框架提供了視圖和模型分離的功能,我們就可以認(rèn)為它是一個MVC框架。在開發(fā)深入之后,可以再體會用到的框架到底是MVC、MVP還是MVVM。

5. 基于AOP的框架設(shè)計(jì)

AOP(Aspect-Oriented Programming, 面向切面編程),誕生于上個世紀(jì)90年代,是對OOP(Object-Oriented Programming, 面向?qū)ο缶幊?的補(bǔ)充和完善。OOP引入封裝、繼承和多態(tài)性等概念來建立一種對象層次結(jié)構(gòu),用以模擬公共行為的一個集合。當(dāng)我們需要為分散的對象引入公共行為的時候,OOP則顯得無能為力。也就是說,OOP允許你定義從上到下的關(guān)系,但并不適合定義從左到右的關(guān)系。例如日志功能。日志代碼往往水平地散布在所有對象層次中,而與它所散布到的對象的核心功能毫無關(guān)系。對于其他類型的代碼,如安全性、異常處理和透明的持續(xù)性也是如此。這種散布在各處的無關(guān)的代碼被稱為橫切(Cross-Cutting)代碼,在OOP設(shè)計(jì)中,它導(dǎo)致了大量代碼的重復(fù),而不利于各個模塊的重用。而AOP技術(shù)則恰恰相反,它利用一種稱為“橫切”的技術(shù),剖解開封裝的對象內(nèi)部,并將那些影響了多個類的公共行為封裝到一個可重用模塊,并將其名為“Aspect”,即方面。所謂“方面”,簡單地說,就是將那些與業(yè)務(wù)無關(guān),卻為業(yè)務(wù)模塊所共同調(diào)用的邏輯或責(zé)任封裝起來,便于減少系統(tǒng)的重復(fù)代碼,降低模塊間的耦合度,并有利于未來的可操作性和可維護(hù)性。 

aop2.1.gif

5.1 AOP在Android中的使用

AOP把軟件系統(tǒng)分為兩個部分:核心關(guān)注點(diǎn)和橫切關(guān)注點(diǎn)。業(yè)務(wù)處理的主要流程是核心關(guān)注點(diǎn),與之關(guān)系不大的部分是橫切關(guān)注點(diǎn)。橫切關(guān)注點(diǎn)的一個特點(diǎn)是,他們經(jīng)常發(fā)生在核心關(guān)注點(diǎn)的多處,而各處都基本相似。AOP的作用在于分離系統(tǒng)中的各種關(guān)注點(diǎn),將核心關(guān)注點(diǎn)和橫切關(guān)注點(diǎn)分離開來。在Android App中,哪些是我們需要的橫切關(guān)注點(diǎn)?個人認(rèn)為主要包括以下幾個方面:Http, SharedPreferences, Json, Xml, File, Device, System, Log, 格式轉(zhuǎn)換等。Android App的需求差別很大,不同的需求橫切關(guān)注點(diǎn)必然是不一樣的。一般的App工程中應(yīng)該有一個Util Package來存放相關(guān)的切面操作,在項(xiàng)目多了之后可以將其中使用較多的Util封裝為一個Jar包供工程調(diào)用。

在使用MVP和AOP對App進(jìn)行縱向和橫向的切割之后,能夠使得App整體的結(jié)構(gòu)更清晰合理,避免局部的代碼臃腫,方便開發(fā)、測試以及后續(xù)的維護(hù)。

6. 干貨:AndroidApp架構(gòu)的設(shè)計(jì)經(jīng)驗(yàn)

首先是作者最最喜歡的一句話,也是對創(chuàng)業(yè)公司特別適用的一句話,也是對不要過度設(shè)計(jì)的一種詮釋:

  • 先實(shí)現(xiàn),再重構(gòu)吧。直接考慮代碼不臃腫得話,不知道什么時候才能寫好了
  • 先實(shí)現(xiàn),再重構(gòu)吧。直接考慮代碼不臃腫得話,不知道什么時候才能寫好了
  • 先實(shí)現(xiàn),再重構(gòu)吧。直接考慮代碼不臃腫得話,不知道什么時候才能寫好了

(重要的事情說三遍)

6.1 整體架構(gòu)

代碼和文檔規(guī)范,根據(jù)需求進(jìn)行模塊劃分,確定交互方式,形成接口文檔,這些較為通用的內(nèi)容不再細(xì)說。做Android App時,一般將App進(jìn)行縱向和橫向的劃分??v向的App由UI層,邏輯層和模型層構(gòu)成,整體結(jié)構(gòu)基于MVP思想(圖片來自網(wǎng)絡(luò))。 

9fe1e5679a642f9f82373d2a32d461b7_b.jpg

UI層內(nèi)部多用模板方法,以Activity為例一般有BaseActivity,提供包括一些基礎(chǔ)樣式,Dialog,ActionBar在內(nèi)的內(nèi)容,展現(xiàn)的Activity都會繼承BaseActivity并實(shí)現(xiàn)預(yù)留的接口,Activity之間的繼承不超過3次;為避免Activity內(nèi)代碼過多,將App的整體控制權(quán)后移,也借鑒了IOC做法,大量的邏輯操作放在邏輯層中,邏輯層和UI層通過接口或者Broadcast等實(shí)現(xiàn)通信,只傳遞結(jié)果。一般Activity里的代碼量都很大,通過這兩種方式一般我寫的單個Activity內(nèi)代碼量不超過400行。

邏輯層實(shí)現(xiàn)的是絕大部分的邏輯操作,由UI層啟動,在內(nèi)部通過接口調(diào)用模型層的方法,在邏輯層內(nèi)大量使用了代理。打個比方,UI層告訴邏輯層我需要做的事,邏輯層去找相應(yīng)的人(模型層)去做,最后只告訴UI這件事做的結(jié)果。

模型層沒什么好說的,這部分一般由大量的Package組成,代碼量是三層中最大的,需要在內(nèi)部進(jìn)行分層。

橫向的分割依據(jù)AOP面向切面的思想,主要是提取出共用方法作為一個單獨(dú)的Util,這些Util會在App整體中穿插使用。很多人的App都會引入自己封裝的Jar包,封裝了包括文件、JSON、SharedPreference等在內(nèi)的常用操作,自己寫的用起來順手,也大幅度降低了重復(fù)作業(yè)。

這樣縱,橫兩次對于App代碼的分割已經(jīng)能使得程序不會過多堆積在一個Java文件里,但靠一次開發(fā)過程就寫出高質(zhì)量的代碼是很困難的,趁著項(xiàng)目的間歇期,對代碼進(jìn)行重構(gòu)很有必要。

6.2 類庫的使用

現(xiàn)在有很多幫助快速開發(fā)的類庫,活用這些類庫也是避免代碼臃腫和混亂的好方法,下面給題主推薦幾個常用類庫。

減少Activity代碼量的依賴注入框架ButterKnife:

  • https://github.com/JakeWharton/butterknife

簡化對于SQlite操作的對象關(guān)系映射框架OrmLite:

  • https://github.com/j256/ormlite-android

圖片緩存類庫Fresco(by facebook):

  • https://github.com/facebook/fresco

能用第三方庫就用第三方庫。別管是否穩(wěn)定,是否被持續(xù)維護(hù),因?yàn)椋魏蔚谌綆斓淖髡?,都能碾壓剛?cè)腴T的菜鳥,你絕對寫不出比別人更好的代碼了。

責(zé)任編輯:未麗燕 來源: 安卓巴士
相關(guān)推薦

2016-01-11 11:20:43

2012-07-13 14:25:59

2023-04-11 07:50:27

軟件架構(gòu)設(shè)計(jì)

2015-09-23 14:01:51

2009-08-03 10:13:13

開發(fā)框架

2017-03-31 20:45:41

MVCMVPMVVM

2017-04-01 08:30:00

MVCMVPMVVM

2011-09-09 09:50:40

Oracle

2010-03-23 08:42:26

ASP.NET MVC

2010-01-14 20:05:43

虛擬化數(shù)據(jù)中心

2009-11-02 11:11:07

VB.NET OOP設(shè)

2009-02-19 10:50:15

DBA性格

2011-08-15 10:27:48

2011-06-21 16:26:19

SEO內(nèi)部優(yōu)化

2015-09-16 10:13:16

游戲性能

2017-05-12 16:50:14

GUI應(yīng)用程序

2024-05-28 07:01:29

2009-09-14 15:04:44

2018-09-18 09:28:05

PCB布線數(shù)字布線經(jīng)驗(yàn)

2013-04-25 09:41:33

網(wǎng)管高級網(wǎng)管網(wǎng)絡(luò)管理
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

97婷婷大伊香蕉精品视频| 欧美一个色资源| 日本一区二区三区四区高清视频| 成人毛片18女人毛片| 美女久久久久| 欧美日韩小视频| 国产情侣第一页| 日本高清中文字幕二区在线| 丝袜亚洲另类欧美综合| 俺去亚洲欧洲欧美日韩| 91精品国产高清91久久久久久| 国产精品偷拍| 欧美国产精品专区| 超碰97网站| 神马久久久久久久| 国产精品www994| 亚洲第一页在线| 校园春色 亚洲色图| 欧美videosex性极品hd| 欧美国产一区在线| 国产日韩久久| 国产伦子伦对白视频| 亚洲视频二区| 久久国产精品久久久久| 超碰97人人干| 亚洲性视频在线| 欧美丝袜丝交足nylons图片| 国产婷婷一区二区三区| 国产理论在线观看| 久久久久久久久99精品| 国产精品免费在线| 一级特黄aaaaaa大片| 亚洲免费婷婷| 久久久久久久久91| 粉嫩av性色av蜜臀av网站| 精品视频亚洲| 精品夜色国产国偷在线| 国产精品无码自拍| 亚洲精品tv| 欧美午夜一区二区三区| 国产精品视频一区二区三区四区五区| 在线中文字幕-区二区三区四区| 久久久久久久综合狠狠综合| 国产精品日韩欧美一区二区三区 | 天天操天天干天天综合网| 中文字幕av久久| 91青青在线视频| 国产亚洲综合色| 欧美裸体网站| 青草久久伊人| 92国产精品观看| 成人黄动漫网站免费| 国产女人18毛片18精品| 日本vs亚洲vs韩国一区三区| 日本国产欧美一区二区三区| 久久露脸国语精品国产91| 欧美另类女人| 欧美激情一级二级| 国产在线视频你懂的| 欧美日韩亚洲一区三区| 色综合色综合久久综合频道88| 久草视频手机在线| 欧美在线亚洲综合一区| 欧美日韩成人在线观看| 国产精品成人免费观看| 国内精品福利| 高清欧美性猛交xxxx| 日本五十熟hd丰满| 国产欧美日韩综合一区在线播放 | 亚洲精品tv久久久久久久久| 国产永久av在线| 国产日韩精品一区| 日韩三级电影| 日本天堂在线观看| 亚洲精品视频一区| 国产精品久久国产| 大香伊人久久| 日韩欧美亚洲成人| 俄罗斯av网站| 国产精品极品美女在线观看| 欧美网站一区二区| 色黄视频免费看| 成人自拍在线| 亚洲欧美日韩天堂| 一级免费黄色录像| 欧美黄色一区| 8090成年在线看片午夜| 国产情侣呻吟对白高潮| 激情偷乱视频一区二区三区| 不卡视频一区二区三区| 蜜桃视频在线入口www| 国产欧美精品一区aⅴ影院 | 91看片在线观看| 亚洲精品亚洲人成人网| 精品久久一二三| 主播大秀视频在线观看一区二区| 欧美精品国产精品| 国产草草浮力影院| 欧美日韩在线播放视频| 欧美成人在线免费| 国产精品男女视频| 韩国v欧美v日本v亚洲v| 激情小说综合区| 免费高清完整在线观看| 精品久久久久久| 亚洲精品在线视频播放| 最新国产精品视频| 美女啪啪无遮挡免费久久网站| 影音先锋在线国产| 国产一二三精品| 欧美人xxxxx| 好看的中文字幕在线播放| 欧美在线观看视频一区二区三区| 亚洲免费观看在线| 日韩久久综合| 欧美与黑人午夜性猛交久久久| 国产美女无遮挡永久免费| 26uuu久久天堂性欧美| 男人j进女人j| 韩国精品主播一区二区在线观看 | 天天av天天翘| 亚洲欧美在线视频观看| 黄www在线观看| 一区二区三区四区高清视频| 中文字幕精品视频| 国产一级18片视频| 99视频精品在线| 香蕉视频免费版| 天堂久久午夜av| 亚洲精品小视频| 日本熟妇成熟毛茸茸| 久久99国产精品成人| 日韩av高清在线播放| 成av人片在线观看www| 日韩亚洲欧美成人一区| 男女全黄做爰文章| 日日摸夜夜添夜夜添精品视频| 国产一区二区不卡视频在线观看| 麻豆传媒在线完整视频| 在线观看www91| 亚洲天堂久久新| 男女av一区三区二区色多| 国产福利不卡| 欧美寡妇性猛交xxx免费| 3751色影院一区二区三区| www..com.cn蕾丝视频在线观看免费版 | 麻豆精品久久久| 日产中文字幕在线精品一区| 亚洲成人不卡| 一区二区欧美激情| 中文字幕永久在线视频| 中文乱码免费一区二区| www.涩涩涩| 99精品在线| 成人精品福利视频| fc2ppv国产精品久久| 91麻豆精品国产自产在线观看一区 | 精品午夜av| 欧美另类极品videosbest最新版本| 国产精品国产三级国产普通话对白 | 无码一区二区三区在线| 视频一区视频二区中文字幕| 欧美日韩免费精品| 国产综合色区在线观看| 国产亚洲精品91在线| 中文字幕在线观看第二页| 国产精品高潮久久久久无| 污污视频在线免费| 亚洲成色精品| 欧美日韩一区在线观看视频| 91精品店在线| www.欧美精品| 亚洲黄色在线观看视频| 精品色蜜蜜精品视频在线观看| 波多野结衣 在线| 免费在线观看成人| 国产精品啪啪啪视频| 老牛精品亚洲成av人片| 国产成人精彩在线视频九色| 3p在线观看| 日韩欧美一二三区| 国产精品男女视频| 国产精品传媒在线| 不许穿内裤随时挨c调教h苏绵| 亚洲少妇在线| 色一情一乱一伦一区二区三欧美| 日韩一区二区三免费高清在线观看| 美女国内精品自产拍在线播放| 你懂的网站在线| 欧美专区在线观看一区| 我家有个日本女人| 久久女同互慰一区二区三区| 伊人网在线综合| 亚洲婷婷免费| 日韩aⅴ视频一区二区三区| 国产 日韩 欧美| 国产做受高潮69| 91在线直播| 日韩欧美国产一二三区| 天堂在线免费观看视频| 日韩美女精品在线| 久久亚洲AV成人无码国产野外| 男女男精品视频| 欧美成人三级在线视频| 色综合咪咪久久网| 免费看成人午夜电影| 美女精品久久| 国产精品久久久久久久久久久不卡| 中文字幕有码在线观看| 亚洲性生活视频| 三级视频在线看| 在线电影欧美成精品| 无码无套少妇毛多18pxxxx| 一区二区高清免费观看影视大全 | 精品国产乱码一区二区三 | www.av中文字幕| 91tv官网精品成人亚洲| 农村寡妇一区二区三区| 999久久精品| 国产一区红桃视频| 卡通欧美亚洲| 国内揄拍国内精品| 中文字幕中文字幕在线十八区 | 亚洲一区二区三区无码久久| 激情另类小说区图片区视频区| 男女高潮又爽又黄又无遮挡| 亚洲欧美亚洲| 制服国产精品| 第一社区sis001原创亚洲| 久久综合狠狠综合久久综青草| 97久久综合区小说区图片区| 91亚洲va在线va天堂va国| 日韩天堂在线| 国产精品99久久久久久www| 9999精品成人免费毛片在线看| 久久国产精品免费视频| 欧美18一19xxx性| 影音先锋欧美精品| 国产香蕉视频在线看| 亚洲乱码国产乱码精品精| 香蕉视频免费看| 亚洲国产精品成人精品| 黄频网站在线观看| 亚洲国产成人一区| 欧美一区二区三区成人片在线| 日韩视频123| 草逼视频免费看| 91精品国产aⅴ一区二区| 91国内精品视频| 欧美日韩黄色影视| 一级片免费网站| 91精品国产入口| 99久久免费国产精精品| 欧美电影一区二区| av片免费播放| 精品免费日韩av| 日本免费网站在线观看| 日韩av在线免费播放| 国模私拍视频在线| 亚洲精品日韩丝袜精品| 黄色免费在线播放| 日日噜噜噜夜夜爽亚洲精品| 黄网站免费在线播放| 久热精品在线视频| 国产盗摄精品一区二区酒店| 久久免费在线观看| 碰碰在线视频| 国产精品香蕉国产| 日韩影片在线观看| 精品久久蜜桃| 精品久久久久中文字幕小说| 亚洲第一精品区| 国内揄拍国内精品久久| 凹凸国产熟女精品视频| 青草国产精品久久久久久| 婷婷激情综合五月天| 成人免费视频网站在线观看| 亚洲乱码国产乱码精品精大量| 国产喂奶挤奶一区二区三区| 欧美h片在线观看| 亚洲国产乱码最新视频| 中文字幕在线播| 欧美一区二区在线视频| 刘亦菲久久免费一区二区| 亚洲欧洲日产国码av系列天堂| 亚洲成人三级| 国模视频一区二区| 国产成人精品一区二区三区免费| 2022国产精品| 神马久久一区二区三区| 熟妇熟女乱妇乱女网站| 国产午夜久久| 又色又爽又黄视频| 久久中文字幕电影| 无码人妻精品一区二区三区夜夜嗨| 午夜日韩在线观看| 在线视频 91| 亚洲精品第一国产综合精品| 在线免费看av| 国产91精品青草社区| 在线成人免费| 欧美一区1区三区3区公司| 欧美成人精品| 在线免费视频一区| 99久久免费视频.com| 亚洲天堂一级片| 色天使色偷偷av一区二区| 99热这里只有精品在线观看| 亚洲欧洲高清在线| av日韩国产| 亚洲自拍偷拍网址| 成人av二区| 男人天堂网视频| 成+人+亚洲+综合天堂| 成人做爰视频网站| 91黄色在线观看| 亚洲AV成人无码一二三区在线| 欧美精品一区二区免费| 四虎国产精品永久在线国在线| 久久久久九九九| 黄色工厂这里只有精品| 一级淫片在线观看| 中文字幕乱码亚洲精品一区| www.国产色| 亚洲精品一区二区在线观看| 在线中文字幕电影| 91在线精品播放| 91亚洲自偷观看高清| 日韩一级片播放| 91看片淫黄大片一级在线观看| 久久久久香蕉视频| 欧美一区二区三区爱爱| 免费黄网站在线播放| 国产成人亚洲综合青青| 丝袜美腿一区二区三区动态图 | 日本成人中文字幕| 亚洲天堂久久新| 欧美小视频在线观看| 亚洲av成人无码久久精品老人| 午夜精品蜜臀一区二区三区免费| 伊人精品综合| 成人精品视频在线播放| 成人做爰69片免费看网站| 欧美精品一区二区成人| 日韩一区二区免费在线观看| h网站久久久| 99久久伊人精品影院| 欧美日韩国产成人精品| 亚洲精品乱码久久久久久9色| 亚洲欧美日本在线| www日本视频| 久久人人爽人人爽人人片av高清| 加勒比色老久久爱综合网| 欧美爱爱视频免费看| 91毛片在线观看| 夜夜爽妓女8888视频免费观看| 亚洲天堂网在线观看| 91看片一区| 亚洲欧洲一区二区福利| 久久国产精品72免费观看| 男人晚上看的视频| 日韩美女视频在线| a在线视频v视频| 免费久久一级欧美特大黄| 人人精品人人爱| 欧美爱爱免费视频| 亚洲精品在线网站| 久久毛片亚洲| 制服诱惑一区| 成人性生交大合| 国产免费一级视频| 日韩三级影视基地| 99re8这里有精品热视频8在线| xxxx18hd亚洲hd捆绑| 久久久一区二区| 国产情侣呻吟对白高潮| 欧美美女18p| 午夜精品福利影院| 亚洲免费999| 午夜久久久久久久久久一区二区| 欧美色综合一区二区三区| 91精品久久久久久久久久 | 国产亚洲视频系列| 国产精品老熟女视频一区二区| 欧美高清无遮挡| 亚洲人成精品久久久| 国产精品igao网网址不卡| 欧美日韩性生活视频| 欧美私人网站| 国产另类第一区| 久久狠狠亚洲综合| 日本熟妇色xxxxx日本免费看| 在线中文字幕日韩| 精品精品国产毛片在线看| 一区二区三区免费播放| 亚洲国产精品久久不卡毛片| 91短视频版在线观看www免费| 国产 高清 精品 在线 a| 美女精品一区二区| 欧美精品亚洲精品日韩精品|