3atv精品不卡视频,97人人超碰国产精品最新,中文字幕av一区二区三区人妻少妇,久久久精品波多野结衣,日韩一区二区三区精品

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

如何使用Retrofit,OkHttp,Gson,Glide和Coroutines处理RESTful Web服务

發布時間:2023/11/29 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何使用Retrofit,OkHttp,Gson,Glide和Coroutines处理RESTful Web服务 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Kriptofolio應用程序系列-第5部分 (Kriptofolio app series — Part 5)

These days almost every Android app connects to internet to get/send data. You should definitely learn how to handle RESTful Web Services, as their correct implementation is the core knowledge while creating modern apps.

如今,幾乎每個Android應用程序都可以連接到互聯網以獲取/發送數據。 您絕對應該學習如何處理RESTful Web服務,因為正確的實現是創建現代應用程序時的核心知識。

This part is going to be complicated. We are going to combine multiple libraries at once to get a working result. I am not going to talk about the native Android way to handle internet requests, because in the real world nobody uses it. Every good app does not try to reinvent the wheel but instead uses the most popular third party libraries to solve common problems. It would be too complicated to recreate the functionality that these well-made libraries have to offer.

這部分將變得復雜。 我們將一次合并多個庫以獲得工作結果。 我不會討論處理互聯網請求的原生Android方法,因為在現實世界中沒有人使用它。 每個優秀的應用程序都不會嘗試重新發明輪子,而是使用最受歡迎的第三方庫來解決常見問題。 重新創建這些精巧庫必須提供的功能將太復雜。

系列內容 (Series content)

  • Introduction: A roadmap to build a modern Android app in 2018–2019

    簡介:2018-2019年構建現代Android應用程序的路線圖

  • Part 1: An introduction to the SOLID principles

    第1部分:SOLID原理簡介

  • Part 2: How to start building your Android app: creating Mockups, UI, and XML layouts

    第2部分:如何開始構建Android應用:創建樣機,UI和XML布局

  • Part 3: All about that Architecture: exploring different architecture patterns and how to use them in your app

    第3部分:有關該架構的全部內容:探索不同的架構模式以及如何在您的應用程序中使用它們

  • Part 4: How to implement Dependency Injection in your app with Dagger 2

    第4部分:如何使用Dagger 2在您的應用程序中實現依賴注入

  • Part 5: Handle RESTful Web Services using Retrofit, OkHttp, Gson, Glide and Coroutines (you’re here)

    第5部分:使用Retrofit,OkHttp,Gson,Glide和Coroutines處理RESTful Web服務(在這里)

什么是Retrofit,OkHttp和Gson? (What is Retrofit, OkHttp and Gson?)

Retrofit is a REST Client for Java and Android. This library, in my opinion, is the most important one to learn, as it will do the main job. It makes it relatively easy to retrieve and upload JSON (or other structured data) via a REST based webservice.

Retrofit是用于Java和Android的REST客戶端。 我認為,該庫是最重要的學習庫,因為它將完成主要工作。 它使通過基于REST的Web服務檢索和上傳JSON(或其他結構化數據)相對容易。

In Retrofit you configure which converter is used for the data serialization. Typically to serialize and deserialize objects to and from JSON you use an open-source Java library — Gson. Also if you need, you can add custom converters to Retrofit to process XML or other protocols.

在翻新中,可以配置哪個轉換器用于數據序列化。 通常,要使用JSON對對象進行序列化和反序列化,請使用開源Java庫-Gson。 另外,如果需要,您可以將自定義轉換器添加到Retrofit以處理XML或其他協議。

For making HTTP requests Retrofit uses the OkHttp library. OkHttp is a pure HTTP/SPDY client responsible for any low-level network operations, caching, requests and responses manipulation. In contrast, Retrofit is a high-level REST abstraction build on top of OkHttp. Retrofit is strongly coupled with OkHttp and makes intensive use of it.

為了發出HTTP請求,Retrofit使用OkHttp庫。 OkHttp是一個純HTTP / SPDY客戶端,負責任何低級網絡操作,緩存,請求和響應操作。 相反,Retrofit是在OkHttp之上的高級REST抽象構建。 改型與OkHttp緊密結合,并大量使用它。

Now that you know that everything is closely related, we are going to use all these 3 libraries at once. Our first goal is to get all the cryptocurrencies list using Retrofit from the Internet. We will use a special OkHttp interceptor class for CoinMarketCap API authentication when making a call to the server. We will get back a JSON data result and then convert it using the Gson library.

既然您知道所有內容都息息相關,那么我們將立即使用這三個庫。 我們的首要目標是使用Internet上的Retrofit獲取所有加密貨幣列表。 調用服務器時,我們將使用特殊的OkHttp攔截器類進行CoinMarketCap API身份驗證。 我們將獲取JSON數據結果,然后使用Gson庫對其進行轉換。

快速安裝Retrofit 2,請先嘗試 (Quick setup for Retrofit 2 just to try it first)

When learning something new, I like to try it out in practice as soon as I can. We will apply a similar approach with Retrofit 2 for you to understand it better more quickly. Don’t worry right now about code quality or any programming principles or optimizations — we’ll just write some code to make Retrofit 2 work in our project and discuss what it does.

學習新知識時,我希望盡快在實踐中進行嘗試。 我們將對Retrofit 2應用類似的方法,以使您更快地更好地理解它。 現在不必擔心代碼質量或任何編程原則或優化-我們將只編寫一些代碼以使Retrofit 2在我們的項目中工作并討論其功能。

Follow these steps to set up Retrofit 2 on My Crypto Coins app project:

請按照以下步驟在My Crypto Coins應用程序項目上設置Retrofit 2:

首先,授予該應用程序的INTERNET權限 (First, give INTERNET permission for the app)

We are going to execute HTTP requests on a server accessible via the Internet. Give this permission by adding these lines to your Manifest file:

我們將在可通過Internet訪問的服務器上執行HTTP請求。 通過將以下行添加到清單文件中來授予此權限:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.baruckis.mycryptocoins"><uses-permission android:name="android.permission.INTERNET" />... </manifest>

然后,您應該添加庫依賴項 (Then you should add library dependencies)

Find the latest Retrofit version. Also you should know that Retrofit doesn’t ship with an integrated JSON converter. Since we will get responses in JSON format, we need to include the converter manually in the dependencies too. We are going to use latest Google’s JSON converter Gson version. Let’s add these lines to your gradle file:

查找最新的Retrofit版本 。 另外,您應該知道Retrofit并未附帶集成的JSON轉換器。 由于我們將獲得JSON格式的響應,因此我們也需要手動將轉換器包含在依賴項中。 我們將使用最新的Google JSON轉換器Gson版本 。 讓我們將這些行添加到gradle文件中:

// 3rd party // HTTP client - Retrofit with OkHttp implementation "com.squareup.retrofit2:retrofit:$versions.retrofit" // JSON converter Gson for JSON to Java object mapping implementation "com.squareup.retrofit2:converter-gson:$versions.retrofit"

As you noticed from my comment, the OkHttp dependency is already shipped with the Retrofit 2 dependency. Versions is just a separate gradle file for convenience:

正如您從我的評論中注意到的那樣,Retrofit 2依賴項已經附帶了OkHttp依賴項。 為了方便起見,版本只是一個單獨的gradle文件:

def versions = [:]versions.retrofit = "2.4.0"ext.versions = versions

下一步設置改造界面 (Next set up the Retrofit interface)

It’s an interface that declares our requests and their types. Here we define the API on the client side.

這是一個聲明我們的請求及其類型的接口。 在這里,我們在客戶端定義API。

/*** REST API access points.*/ interface ApiService {// The @GET annotation tells retrofit that this request is a get type request.// The string value tells retrofit that the path of this request is// baseUrl + v1/cryptocurrency/listings/latest + query parameter.@GET("v1/cryptocurrency/listings/latest")// Annotation @Query is used to define query parameter for request. Finally the request url will// look like that https://sandbox-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?convert=EUR.fun getAllCryptocurrencies(@Query("convert") currency: String): Call<CryptocurrenciesLatest>// The return type for this function is Call with its type CryptocurrenciesLatest. }

并設置數據類 (And set up the data class)

Data classes are POJOs (Plain Old Java Objects) that represent the responses of the API calls we’re going to make.

數據類是POJO(普通的舊Java對象),代表我們將要進行的API調用的響應。

/*** Data class to handle the response from the server.*/ data class CryptocurrenciesLatest(val status: Status,val data: List<Data> ) {data class Data(val id: Int,val name: String,val symbol: String,val slug: String,// The annotation to a model property lets you pass the serialized and deserialized// name as a string. This is useful if you don't want your model class and the JSON// to have identical naming.@SerializedName("circulating_supply")val circulatingSupply: Double,@SerializedName("total_supply")val totalSupply: Double,@SerializedName("max_supply")val maxSupply: Double,@SerializedName("date_added")val dateAdded: String,@SerializedName("num_market_pairs")val numMarketPairs: Int,@SerializedName("cmc_rank")val cmcRank: Int,@SerializedName("last_updated")val lastUpdated: String,val quote: Quote) {data class Quote(// For additional option during deserialization you can specify value or alternative// values. Gson will check the JSON for all names we specify and try to find one to// map it to the annotated property.@SerializedName(value = "USD", alternate = ["AUD", "BRL", "CAD", "CHF", "CLP","CNY", "CZK", "DKK", "EUR", "GBP", "HKD", "HUF", "IDR", "ILS", "INR", "JPY","KRW", "MXN", "MYR", "NOK", "NZD", "PHP", "PKR", "PLN", "RUB", "SEK", "SGD","THB", "TRY", "TWD", "ZAR"])val currency: Currency) {data class Currency(val price: Double,@SerializedName("volume_24h")val volume24h: Double,@SerializedName("percent_change_1h")val percentChange1h: Double,@SerializedName("percent_change_24h")val percentChange24h: Double,@SerializedName("percent_change_7d")val percentChange7d: Double,@SerializedName("market_cap")val marketCap: Double,@SerializedName("last_updated")val lastUpdated: String)}}data class Status(val timestamp: String,@SerializedName("error_code")val errorCode: Int,@SerializedName("error_message")val errorMessage: String,val elapsed: Int,@SerializedName("credit_count")val creditCount: Int) }

調用服務器時,創建用于身份驗證的特殊攔截器類 (Create a special interceptor class for authentication when making a call to the server)

This is the case particular for any API that requires authentication to get a successful response. Interceptors are a powerful way to customize your requests. We are going to intercept the actual request and to add individual request headers, which will validate the call with an API Key provided by CoinMarketCap Professional API Developer Portal. To get yours, you need to register there.

對于任何需要身份驗證才能獲得成功響應的API而言,情況尤其如此。 攔截器是自定義您的請求的強大方法。 我們將截取實際的請求并添加單個請求標頭,這將使用CoinMarketCap專業API開發人員門戶提供的API密鑰來驗證調用。 要獲得您的證書,您需要在此注冊。

/*** Interceptor used to intercept the actual request and* to supply your API Key in REST API calls via a custom header.*/ class AuthenticationInterceptor : Interceptor {override fun intercept(chain: Interceptor.Chain): Response {val newRequest = chain.request().newBuilder()// TODO: Use your API Key provided by CoinMarketCap Professional API Developer Portal..addHeader("X-CMC_PRO_API_KEY", "CMC_PRO_API_KEY").build()return chain.proceed(newRequest)} }

最后,將此代碼添加到我們的活動中以查看翻新工作 (Finally, add this code to our activity to see Retrofit working)

I wanted to get your hands dirty as soon as possible, so I put everything in one place. This is not the correct way, but it’s the fastest instead just to see a visual result quickly.

我想盡快弄臟你的手,所以我將所有東西都放在一個地方。 這不是正確的方法,而是最快的方法,只是快速查看視覺結果。

class AddSearchActivity : AppCompatActivity(), Injectable {private lateinit var listView: ListViewprivate lateinit var listAdapter: AddSearchListAdapter...override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)...// Later we will setup Retrofit correctly, but for now we do all in one place just for quick start.setupRetrofitTemporarily()}...private fun setupRetrofitTemporarily() {// We need to prepare a custom OkHttp client because need to use our custom call interceptor.// to be able to authenticate our requests.val builder = OkHttpClient.Builder()// We add the interceptor to OkHttpClient.// It will add authentication headers to every call we make.builder.interceptors().add(AuthenticationInterceptor())val client = builder.build()val api = Retrofit.Builder() // Create retrofit builder..baseUrl("https://sandbox-api.coinmarketcap.com/") // Base url for the api has to end with a slash..addConverterFactory(GsonConverterFactory.create()) // Use GSON converter for JSON to POJO object mapping..client(client) // Here we set the custom OkHttp client we just created..build().create(ApiService::class.java) // We create an API using the interface we defined.val adapterData: MutableList<Cryptocurrency> = ArrayList<Cryptocurrency>()val currentFiatCurrencyCode = "EUR"// Let's make asynchronous network request to get all latest cryptocurrencies from the server.// For query parameter we pass "EUR" as we want to get prices in euros.val call = api.getAllCryptocurrencies("EUR")val result = call.enqueue(object : Callback<CryptocurrenciesLatest> {// You will always get a response even if something wrong went from the server.override fun onFailure(call: Call<CryptocurrenciesLatest>, t: Throwable) {Snackbar.make(findViewById(android.R.id.content),// Throwable will let us find the error if the call failed."Call failed! " + t.localizedMessage,Snackbar.LENGTH_INDEFINITE).show()}override fun onResponse(call: Call<CryptocurrenciesLatest>, response: Response<CryptocurrenciesLatest>) {// Check if the response is successful, which means the request was successfully// received, understood, accepted and returned code in range [200..300).if (response.isSuccessful) {// If everything is OK, let the user know that.Toast.makeText(this@AddSearchActivity, "Call OK.", Toast.LENGTH_LONG).show();// Than quickly map server response data to the ListView adapter.val cryptocurrenciesLatest: CryptocurrenciesLatest? = response.body()cryptocurrenciesLatest!!.data.forEach {val cryptocurrency = Cryptocurrency(it.name, it.cmcRank.toShort(),0.0, it.symbol, currentFiatCurrencyCode, it.quote.currency.price,0.0, it.quote.currency.percentChange1h,it.quote.currency.percentChange7d, it.quote.currency.percentChange24h,0.0)adapterData.add(cryptocurrency)}listView.visibility = View.VISIBLElistAdapter.setData(adapterData)}// Else if the response is unsuccessful it will be defined by some special HTTP// error code, which we can show for the user.else Snackbar.make(findViewById(android.R.id.content),"Call error with HTTP status code " + response.code() + "!",Snackbar.LENGTH_INDEFINITE).show()}})}... }

You can explore the code here. Remember this is only an initial simplified implementation version for you to get the idea better.

您可以在此處探索代碼。 請記住,這只是一個初始的簡化實現版本,可以幫助您更好地理解。

使用OkHttp 3和Gson的Retrofit 2的最終正確設置 (Final correct setup for Retrofit 2 with OkHttp 3 and Gson)

Ok after a quick experiment, it is time to bring this Retrofit implementation to the next level. We already got the data successfully but not correctly. We are missing the states like loading, error and success. Our code is mixed without separation of concerns. It’s a common mistake to write all your code in an activity or a fragment. Our activity class is UI based and should only contain logic that handles UI and operating system interactions.

經過快速的實驗之后,現在可以將Retrofit實施提升到一個新的水平。 我們已經成功獲取了數據,但不正確。 我們缺少諸如加載,錯誤和成功之類的狀態。 我們的代碼混合在一起,沒有關注點的分離。 在活動或片段中編寫所有代碼是一個常見的錯誤。 我們的活動類基于UI,并且應僅包含處理UI和操作系統交互的邏輯。

Actually, after this quick setup, I worked a lot and made many changes. There is no point to put all the code that was changed in the article. Better instead you should browse the final Part 5 code repo here. I have commented everything very well and my code should be clear for you to understand. But I am going to talk about most important things I have done and why I did them.

實際上,在完成此快速設置之后,我做了很多工作并進行了許多更改。 沒有必要在文章中放置所有已更改的代碼。 更好的是,您應該在此處瀏覽最終的第5部分代碼存儲庫。 我對所有內容的評論都非常好,我的代碼應該清晰易懂。 但是我將談論我所做的最重要的事情以及為什么要做這些。

The first step to improve was to start using Dependency Injection. Remember from the previous part we already have Dagger 2 implemented inside the project correctly. So I used it for the Retrofit setup.

改進的第一步是開始使用依賴注入。 請記住,在上一部分中,我們已經在項目內部正確實現了Dagger 2。 因此,我將其用于翻新設置。

/*** AppModule will provide app-wide dependencies for a part of the application.* It should initialize objects used across our application, such as Room database, Retrofit, Shared Preference, etc.*/ @Module(includes = [ViewModelsModule::class]) class AppModule() {...@Provides@Singletonfun provideHttpClient(): OkHttpClient {// We need to prepare a custom OkHttp client because need to use our custom call interceptor.// to be able to authenticate our requests.val builder = OkHttpClient.Builder()// We add the interceptor to OkHttpClient.// It will add authentication headers to every call we make.builder.interceptors().add(AuthenticationInterceptor())// Configure this client not to retry when a connectivity problem is encountered.builder.retryOnConnectionFailure(false)// Log requests and responses.// Add logging as the last interceptor, because this will also log the information which// you added or manipulated with previous interceptors to your request.builder.interceptors().add(HttpLoggingInterceptor().apply {// For production environment to enhance apps performance we will be skipping any// logging operation. We will show logs just for debug builds.level = if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE})return builder.build()}@Provides@Singletonfun provideApiService(httpClient: OkHttpClient): ApiService {return Retrofit.Builder() // Create retrofit builder..baseUrl(API_SERVICE_BASE_URL) // Base url for the api has to end with a slash..addConverterFactory(GsonConverterFactory.create()) // Use GSON converter for JSON to POJO object mapping..addCallAdapterFactory(LiveDataCallAdapterFactory()).client(httpClient) // Here we set the custom OkHttp client we just created..build().create(ApiService::class.java) // We create an API using the interface we defined.}... }

Now as you see, Retrofit is separated from the activity class as it should be. It will be initialized only once and used app-wide.

現在您可以看到,Retrofit與活動類已經分開了。 它將僅初始化一次,并在整個應用范圍內使用。

As you may have noticed while creating the Retrofit builder instance, we added a special Retrofit calls adapter using addCallAdapterFactory. By default, Retrofit returns a Call<T>, but for our project we require it to return a LiveData<T> type. In order to do that we need to add LiveDataCallAdapter by using LiveDataCallAdapterFactory.

正如您在創建Retrofit構建器實例時可能已經注意到的那樣,我們使用addCallAdapterFactory添加了一個特殊的Retrofit調用適配器。 默認情況下,Retrofit返回Call<T> ,但是對于我們的項目,我們要求它返回LiveData<T>類型。 為了做到這一點,我們需要添加LiveDataCallAdapter使用LiveDataCallAdapterFactory 。

/*** A Retrofit adapter that converts the Call into a LiveData of ApiResponse.* @param <R> </R> */ class LiveDataCallAdapter<R>(private val responseType: Type) :CallAdapter<R, LiveData<ApiResponse<R>>> {override fun responseType() = responseTypeoverride fun adapt(call: Call<R>): LiveData<ApiResponse<R>> {return object : LiveData<ApiResponse<R>>() {private var started = AtomicBoolean(false)override fun onActive() {super.onActive()if (started.compareAndSet(false, true)) {call.enqueue(object : Callback<R> {override fun onResponse(call: Call<R>, response: Response<R>) {postValue(ApiResponse.create(response))}override fun onFailure(call: Call<R>, throwable: Throwable) {postValue(ApiResponse.create(throwable))}})}}}} }class LiveDataCallAdapterFactory : CallAdapter.Factory() {override fun get(returnType: Type,annotations: Array<Annotation>,retrofit: Retrofit): CallAdapter<*, *>? {if (CallAdapter.Factory.getRawType(returnType) != LiveData::class.java) {return null}val observableType = CallAdapter.Factory.getParameterUpperBound(0, returnType as ParameterizedType)val rawObservableType = CallAdapter.Factory.getRawType(observableType)if (rawObservableType != ApiResponse::class.java) {throw IllegalArgumentException("type must be a resource")}if (observableType !is ParameterizedType) {throw IllegalArgumentException("resource must be parameterized")}val bodyType = CallAdapter.Factory.getParameterUpperBound(0, observableType)return LiveDataCallAdapter<Any>(bodyType)} }

Now we will get LiveData<T> instead of Call<T> as the return type from Retrofit service methods defined in the ApiService interface.

現在,我們將從ApiService接口中定義的Retrofit服務方法中獲得LiveData<T>而不是Call<T>作為返回類型。

Another important step to make is to start using the Repository pattern. I have talked about it in Part 3. Check out our MVVM architecture schema from that post to remember where it goes.

要做的另一個重要步驟是開始使用存儲庫模式。 我已經在第3部分中討論過它。 從那篇文章中查看我們的MVVM體系結構架構,以記住它的去向。

As you see in the picture, Repository is a separate layer for the data. It’s our single source of contact for getting or sending data. When we use Repository, we are following the separation of concerns principle. We can have different data sources (like in our case persistent data from an SQLite database and data from web services), but Repository is always going to be single source of truth for all app data.

如您在圖片中看到的,存儲庫是數據的單獨層。 這是我們獲取或發送數據的唯一聯系方式。 當使用存儲庫時,我們遵循關注點分離原則。 我們可以有不同的數據源(例如我們SQLite數據庫中的持久性數據和Web服務中的數據),但是存儲庫始終將是所有應用程序數據的唯一真實來源。

Instead of communicating with our Retrofit implementation directly, we are going to use Repository for that. For each kind of entity, we are going to have a separate Repository.

與其直接與我們的Retrofit實現進行通信,不如使用存儲庫。 對于每種實體,我們將有一個單獨的存儲庫。

/*** The class for managing multiple data sources.*/ @Singleton class CryptocurrencyRepository @Inject constructor(private val context: Context,private val appExecutors: AppExecutors,private val myCryptocurrencyDao: MyCryptocurrencyDao,private val cryptocurrencyDao: CryptocurrencyDao,private val api: ApiService,private val sharedPreferences: SharedPreferences ) {// Just a simple helper variable to store selected fiat currency code during app lifecycle.// It is needed for main screen currency spinner. We set it to be same as in shared preferences.var selectedFiatCurrencyCode: String = getCurrentFiatCurrencyCode()...// The Resource wrapping of LiveData is useful to update the UI based upon the state.fun getAllCryptocurrencyLiveDataResourceList(fiatCurrencyCode: String, shouldFetch: Boolean = false, callDelay: Long = 0): LiveData<Resource<List<Cryptocurrency>>> {return object : NetworkBoundResource<List<Cryptocurrency>, CoinMarketCap<List<CryptocurrencyLatest>>>(appExecutors) {// Here we save the data fetched from web-service.override fun saveCallResult(item: CoinMarketCap<List<CryptocurrencyLatest>>) {val list = getCryptocurrencyListFromResponse(fiatCurrencyCode, item.data, item.status?.timestamp)cryptocurrencyDao.reloadCryptocurrencyList(list)myCryptocurrencyDao.reloadMyCryptocurrencyList(list)}// Returns boolean indicating if to fetch data from web or not, true means fetch the data from web.override fun shouldFetch(data: List<Cryptocurrency>?): Boolean {return data == null || shouldFetch}override fun fetchDelayMillis(): Long {return callDelay}// Contains the logic to get data from the Room database.override fun loadFromDb(): LiveData<List<Cryptocurrency>> {return Transformations.switchMap(cryptocurrencyDao.getAllCryptocurrencyLiveDataList()) { data ->if (data.isEmpty()) {AbsentLiveData.create()} else {cryptocurrencyDao.getAllCryptocurrencyLiveDataList()}}}// Contains the logic to get data from web-service using Retrofit.override fun createCall(): LiveData<ApiResponse<CoinMarketCap<List<CryptocurrencyLatest>>>> = api.getAllCryptocurrencies(fiatCurrencyCode)}.asLiveData()}...fun getCurrentFiatCurrencyCode(): String {return sharedPreferences.getString(context.resources.getString(R.string.pref_fiat_currency_key), context.resources.getString(R.string.pref_default_fiat_currency_value))?: context.resources.getString(R.string.pref_default_fiat_currency_value)}...private fun getCryptocurrencyListFromResponse(fiatCurrencyCode: String, responseList: List<CryptocurrencyLatest>?, timestamp: Date?): ArrayList<Cryptocurrency> {val cryptocurrencyList: MutableList<Cryptocurrency> = ArrayList()responseList?.forEach {val cryptocurrency = Cryptocurrency(it.id, it.name, it.cmcRank.toShort(),it.symbol, fiatCurrencyCode, it.quote.currency.price,it.quote.currency.percentChange1h,it.quote.currency.percentChange7d, it.quote.currency.percentChange24h, timestamp)cryptocurrencyList.add(cryptocurrency)}return cryptocurrencyList as ArrayList<Cryptocurrency>}}

As you notice in the CryptocurrencyRepository class code, I am using the NetworkBoundResource abstract class. What is it and why do we need it?

正如您在CryptocurrencyRepository類代碼中注意到的那樣,我正在使用NetworkBoundResource抽象類。 這是什么,為什么我們需要它?

NetworkBoundResource is a small but very important helper class that will allow us to maintain a synchronization between the local database and the web service. Our goal is to build a modern application that will work smoothly even when our device is offline. Also with the help of this class we will be able to present different network states like errors or loading for the user visually.

NetworkBoundResource是一個很小但非常重要的幫助程序類,它將使我們能夠維護本地數據庫和Web服務之間的同步。 我們的目標是構建一個即使在設備離線時也能平穩運行的現代應用程序。 同樣,在此類的幫助下,我們將能夠直觀地呈現不同的網絡狀態,例如錯誤或負載。

NetworkBoundResource starts by observing the database for the resource. When the entry is loaded from the database for the first time, it checks whether the result is good enough to be dispatched or if it should be re-fetched from the network. Note that both of these situations can happen at the same time, given that you probably want to show cached data while updating it from the network.

NetworkBoundResource從觀察數據庫中的資源開始。 首次從數據庫中加載條目時,它將檢查結果是否足夠好以進行分派,或者是否應從網絡中重新獲取。 請注意,這兩種情況可能同時發生,因為您可能想在從網絡更新數據時顯示緩存的數據。

If the network call completes successfully, it saves the response into the database and re-initializes the stream. If the network request fails, the NetworkBoundResource dispatches a failure directly.

如果網絡調用成功完成,它將響應保存到數據庫中并重新初始化流。 如果網絡請求失敗,則NetworkBoundResource直接調度失敗。

/*** A generic class that can provide a resource backed by both the sqlite database and the network.*** You can read more about it in the [Architecture* Guide](https://developer.android.com/arch).* @param <ResultType> - Type for the Resource data.* @param <RequestType> - Type for the API response. </RequestType></ResultType> */// It defines two type parameters, ResultType and RequestType, // because the data type returned from the API might not match the data type used locally. abstract class NetworkBoundResource<ResultType, RequestType> @MainThread constructor(private val appExecutors: AppExecutors) {// The final result LiveData.private val result = MediatorLiveData<Resource<ResultType>>()init {// Send loading state to UI.result.value = Resource.loading(null)@Suppress("LeakingThis")val dbSource = loadFromDb()result.addSource(dbSource) { data ->result.removeSource(dbSource)if (shouldFetch(data)) {fetchFromNetwork(dbSource)} else {result.addSource(dbSource) { newData ->setValue(Resource.successDb(newData))}}}}@MainThreadprivate fun setValue(newValue: Resource<ResultType>) {if (result.value != newValue) {result.value = newValue}}// Fetch the data from network and persist into DB and then send it back to UI.private fun fetchFromNetwork(dbSource: LiveData<ResultType>) {val apiResponse = createCall()// We re-attach dbSource as a new source, it will dispatch its latest value quickly.result.addSource(dbSource) { newData ->setValue(Resource.loading(newData))}// Create inner function as we want to delay it.fun fetch() {result.addSource(apiResponse) { response ->result.removeSource(apiResponse)result.removeSource(dbSource)when (response) {is ApiSuccessResponse -> {appExecutors.diskIO().execute {saveCallResult(processResponse(response))appExecutors.mainThread().execute {// We specially request a new live data,// otherwise we will get immediately last cached value,// which may not be updated with latest results received from network.result.addSource(loadFromDb()) { newData ->setValue(Resource.successNetwork(newData))}}}}is ApiEmptyResponse -> {appExecutors.mainThread().execute {// reload from disk whatever we hadresult.addSource(loadFromDb()) { newData ->setValue(Resource.successDb(newData))}}}is ApiErrorResponse -> {onFetchFailed()result.addSource(dbSource) { newData ->setValue(Resource.error(response.errorMessage, newData))}}}}}// Add delay before call if needed.val delay = fetchDelayMillis()if (delay > 0) {Handler().postDelayed({ fetch() }, delay)} else fetch()}// Called when the fetch fails. The child class may want to reset components// like rate limiter.protected open fun onFetchFailed() {}// Returns a LiveData object that represents the resource that's implemented// in the base class.fun asLiveData() = result as LiveData<Resource<ResultType>>@WorkerThreadprotected open fun processResponse(response: ApiSuccessResponse<RequestType>) = response.body// Called to save the result of the API response into the database.@WorkerThreadprotected abstract fun saveCallResult(item: RequestType)// Called with the data in the database to decide whether to fetch// potentially updated data from the network.@MainThreadprotected abstract fun shouldFetch(data: ResultType?): Boolean// Make a call to the server after some delay for better user experience.protected open fun fetchDelayMillis(): Long = 0// Called to get the cached data from the database.@MainThreadprotected abstract fun loadFromDb(): LiveData<ResultType>// Called to create the API call.@MainThreadprotected abstract fun createCall(): LiveData<ApiResponse<RequestType>> }

Under the hood, the NetworkBoundResource class is made by using MediatorLiveData and its ability to observe multiple LiveData sources at once. Here we have two LiveData sources: the database and the network call response. Both of those LiveData are wrapped into one MediatorLiveData which is exposed by NetworkBoundResource.

在幕后, NetworkBoundResource類是通過使用MediatorLiveData及其一次觀察多個LiveData源的功能而制成的。 在這里,我們有兩個LiveData源:數據庫和網絡呼叫響應。 這兩個LiveData都包裝到一個MediatorLiveData中,該MediatorLiveData由NetworkBoundResource公開。

Let’s take a closer look how the NetworkBoundResource will work in our app. Imagine the user will launch the app and click on a floating action button on the bottom right corner. The app will launch the add crypto coins screen. Now we can analyze NetworkBoundResource's usage inside it.

讓我們仔細看看NetworkBoundResource將如何在我們的應用程序中工作。 假設用戶將啟動該應用程序,然后單擊右下角的浮動操作按鈕。 該應用程序將啟動添加加密硬幣屏幕。 現在我們可以分析NetworkBoundResource在其中的用法。

If the app is freshly installed and it is its first launch, then there will not be any data stored inside the local database. Because there is no data to show, a loading progress bar UI will be shown. Meanwhile the app is going to make a request call to the server via a web service to get all the cryptocurrencies list.

如果該應用是全新安裝的并且是首次啟動,則本地數據庫內部將不會存儲任何數據。 因為沒有要顯示的數據,所以將顯示加載進度欄UI。 同時,該應用將通過網絡服務向服務器發出請求調用,以獲取所有加密貨幣列表。

If the response is unsuccessful then the error message UI will be shown with the ability to retry a call by pressing a button. When a request call is successful at last, then the response data will be saved to a local SQLite database.

如果響應不成功,則會顯示錯誤消息UI,并具有通過按按鈕重試呼叫的功能。 最后一次請求調用成功后,響應數據將保存到本地SQLite數據庫中。

If we come back to the same screen the next time, the app will load data from the database instead of making a call to the internet again. But the user can ask for a new data update by implementing pull-to-refresh functionality. Old data information will be shown whilst the network call is happening. All this is done with the help of NetworkBoundResource.

如果我們下次再次返回同一屏幕,則該應用程序將從數據庫中加載數據,而不是再次撥打互聯網。 但是用戶可以通過實現“按需刷新”功能來請求新的數據更新。 在進行網絡呼叫時,將顯示舊的數據信息。 所有這些都是在NetworkBoundResource的幫助下完成的。

Another class used in our Repository and LiveDataCallAdapter where all the "magic" happens is ApiResponse. Actually ApiResponse is just a simple common wrapper around the Retrofit2.Response class that converts each response to an instance of LiveData.

在我們的信息庫和使用另一類LiveDataCallAdapter ,所有的“神奇”的情況是ApiResponse 。 實際上, ApiResponse只是Retrofit2.Response類的簡單通用包裝,它將每個響應轉換為LiveData的實例。

/*** Common class used by API responses. ApiResponse is a simple wrapper around the Retrofit2.Call* class that convert responses to instances of LiveData.* @param <CoinMarketCapType> the type of the response object </T> */ @Suppress("unused") // T is used in extending classes sealed class ApiResponse<CoinMarketCapType> {companion object {fun <CoinMarketCapType> create(error: Throwable): ApiErrorResponse<CoinMarketCapType> {return ApiErrorResponse(error.message ?: "Unknown error.")}fun <CoinMarketCapType> create(response: Response<CoinMarketCapType>): ApiResponse<CoinMarketCapType> {return if (response.isSuccessful) {val body = response.body()if (body == null || response.code() == 204) {ApiEmptyResponse()} else {ApiSuccessResponse(body = body)}} else {// Convert error response to JSON object.val gson = Gson()val type = object : TypeToken<CoinMarketCap<CoinMarketCapType>>() {}.typeval errorResponse: CoinMarketCap<CoinMarketCapType> = gson.fromJson(response.errorBody()!!.charStream(), type)val msg = errorResponse.status?.errorMessage ?: errorResponse.messageval errorMsg = if (msg.isNullOrEmpty()) {response.message()} else {msg}ApiErrorResponse(errorMsg ?: "Unknown error.")}}} }/*** Separate class for HTTP 204 resposes so that we can make ApiSuccessResponse's body non-null.*/ class ApiEmptyResponse<CoinMarketCapType> : ApiResponse<CoinMarketCapType>()data class ApiSuccessResponse<CoinMarketCapType>(val body: CoinMarketCapType) : ApiResponse<CoinMarketCapType>()data class ApiErrorResponse<CoinMarketCapType>(val errorMessage: String) : ApiResponse<CoinMarketCapType>()

Inside this wrapper class, if our response has an error, we use the Gson library to convert the error to a JSON object. However, if the response was successful, then the Gson converter for JSON to POJO object mapping is used. We already added it when creating the retrofit builder instance with GsonConverterFactory inside the Dagger AppModule function provideApiService.

在此包裝器類內部,如果響應中有錯誤,則使用Gson庫將錯誤轉換為JSON對象。 但是,如果響應成功,則使用Gson轉換器將JSON轉換為POJO對象。 在Dagger AppModule函數provideApiService使用GsonConverterFactory創建改造生成器實例時,我們已經添加了它。

滑動以加載圖像 (Glide for image loading)

What is Glide? From the docs:

什么是Glide ? 從文檔:

Glide is a fast and efficient open source media management and image loading framework for Android that wraps media decoding, memory and disk caching, and resource pooling into a simple and easy to use interface.Glide是適用于Android的快速高效的開源媒體管理和圖像加載框架,它將媒體解碼,內存和磁盤緩存以及資源池包裝到一個簡單易用的界面中。 Glide’s primary focus is on making scrolling any kind of a list of images as smooth and fast as possible, but it is also effective for almost any case where you need to fetch, resize, and display a remote image.Glide的主要重點是使盡可能平滑和快速地滾動任何種類的圖像列表,但是對于幾乎所有需要獲取,調整大小和顯示遠程圖像的情況,它也都有效。

Sounds like a complicated library which offers many useful features that you would not want to develop all by yourself. In My Crypto Coins app, we have several list screens where we need to show multiple cryptocurrency logos — pictures taken from the internet all at once — and still ensure a smooth scrolling experience for the user. So this library fits our needs perfectly. Also this library is very popular among Android developers.

聽起來像一個復雜的庫,其中提供了許多您不想自己開發的有用功能。 在“我的加密貨幣”應用程序中,我們有幾個列表屏幕,在這些屏幕中,我們需要顯示多個加密貨幣徽標(一次從互聯網上拍攝的圖片),并且仍然可以確保用戶流暢的滾動體驗。 因此,該庫完全符合我們的需求。 同樣,該庫在Android開發人員中非常受歡迎。

Steps to setup Glide on My Crypto Coins app project:

在“我的加密貨幣”應用程序項目上設置Glide的步驟:

聲明依賴 (Declare dependencies)

Get the latest Glide version. Again versions is a separate file versions.gradle for the project.

獲取最新的Glide版本 。 同樣,版本是項目的單獨文件versions.gradle 。

// Glide implementation "com.github.bumptech.glide:glide:$versions.glide" kapt "com.github.bumptech.glide:compiler:$versions.glide" // Glide's OkHttp3 integration. implementation "com.github.bumptech.glide:okhttp3-integration:$versions.glide"+"@aar"

Because we want to use the networking library OkHttp in our project for all network operations, we need to include the specific Glide integration for it instead of the default one. Also since Glide is going to perform a network request to load images via the internet, we need to include the permission INTERNET in our AndroidManifest.xml file — but we already did that with the Retrofit setup.

因為我們要在項目中使用網絡庫OkHttp進行所有網絡操作,所以我們需要為其包含特定的Glide集成,而不是默認的集成。 同樣,由于Glide將執行網絡請求以通過Internet加載圖像,因此我們需要在我們的AndroidManifest.xml文件中包括INTERNET權限-但是我們已經在Retrofit設置中做到了這一點。

創建AppGlideModule (Create AppGlideModule)

Glide v4, which we will be using, offers a generated API for Applications. It will use an annotation processor to generate an API that allows applications to extend Glide’s API and include components provided by integration libraries. For any app to access the generated Glide API we need to include an appropriately annotated AppGlideModule implementation. There can be only a single implementation of the generated API and only one AppGlideModule per application.

我們將使用的Glide v4為應用程序提供了生成的API。 它將使用注釋處理器生成一個API,該API允許應用程序擴展Glide的API并包括集成庫提供的組件。 對于任何要訪問生成的Glide API的應用程序,我們都需要包含一個帶注釋的AppGlideModule實現。 生成的API只能有一個實現,每個應用程序只能有一個AppGlideModule 。

Let’s create a class extending AppGlideModule somewhere in your app project:

讓我們在您的應用程序項目中的某個地方創建一個擴展AppGlideModule的類:

/*** Glide v4 uses an annotation processor to generate an API that allows applications to access all* options in RequestBuilder, RequestOptions and any included integration libraries in a single* fluent API.** The generated API serves two purposes:* Integration libraries can extend Glide’s API with custom options.* Applications can extend Glide’s API by adding methods that bundle commonly used options.** Although both of these tasks can be accomplished by hand by writing custom subclasses of* RequestOptions, doing so is challenging and produces a less fluent API.*/ @GlideModule class AppGlideModule : AppGlideModule()

Even if our application is not changing any additional settings or implementing any methods in AppGlideModule, we still need to have its implementation to use Glide. You're not required to implement any of the methods in AppGlideModule for the API to be generated. You can leave the class blank as long as it extends AppGlideModule and is annotated with @GlideModule.

即使我們的應用程序沒有更改任何其他設置或在AppGlideModule實現任何方法,我們仍然需要使其實現才能使用Glide。 您無需為要生成的API實施AppGlideModule中的任何方法。 您可以將類保留為空白,只要它擴展了AppGlideModule并使用@GlideModule注釋@GlideModule 。

使用Glide生成的API (Use Glide-generated API)

When using AppGlideModule, applications can use the API by starting all loads with GlideApp.with(). This is the code that shows how I have used Glide to load and show cryptocurrency logos in the add crypto coins screen all cryptocurrencies list.

使用AppGlideModule ,應用程序可以通過從GlideApp.with()開始所有加載來使用API??。 這是顯示我如何使用Glide在添加加密硬幣屏幕的所有加密貨幣列表中加載和顯示加密貨幣徽標的代碼。

class AddSearchListAdapter(val context: Context, private val cryptocurrencyClickCallback: ((Cryptocurrency) -> Unit)?) : BaseAdapter() {...override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {...val itemBinding: ActivityAddSearchListItemBinding...// We make an Uri of image that we need to load. Every image unique name is its id.val imageUri = Uri.parse(CRYPTOCURRENCY_IMAGE_URL).buildUpon().appendPath(CRYPTOCURRENCY_IMAGE_SIZE_PX).appendPath(cryptocurrency.id.toString() + CRYPTOCURRENCY_IMAGE_FILE).build()// Glide generated API from AppGlideModule.GlideApp// We need to provide context to make a call..with(itemBinding.root)// Here you specify which image should be loaded by providing Uri..load(imageUri)// The way you combine and execute multiple transformations.// WhiteBackground is our own implemented custom transformation.// CircleCrop is default transformation that Glide ships with..transform(MultiTransformation(WhiteBackground(), CircleCrop()))// The target ImageView your image is supposed to get displayed in..into(itemBinding.itemImageIcon.imageview_front)...return itemBinding.root}...}

As you see, you can start using Glide with just few lines of code and let it do all the hard work for you. It is pretty straightforward.

如您所見,您只需幾行代碼就可以開始使用Glide,并讓它為您完成所有艱苦的工作。 這很簡單。

Kotlin協程 (Kotlin Coroutines)

While building this app, we are going to face situations when we will run time consuming tasks such as writing data to a database or reading from it, fetching data from the network and other. All these common tasks take longer to complete than allowed by the Android framework’s main thread.

在構建此應用程序時,我們將面臨一些情況,例如,運行耗時的任務,例如將數據寫入數據庫或從數據庫中讀取數據,從網絡中獲取數據等。 完成所有這些常見任務所需的時間要比Android框架主線程所允許的時間長。

The main thread is a single thread that handles all updates to the UI. Developers are required not to block it to avoid the app freezing or even crashing with an Application Not Responding dialog. Kotlin coroutines is going to solve this problem for us by introducing main thread safety. It is the last missing piece that we want to add for My Crypto Coins app.

主線程是處理UI的所有更新的單個線程。 要求開發人員不要阻止它,以避免應用程序凍結甚至因“應用程序無響應”對話框而崩潰。 Kotlin協程將通過引入主線程安全性為我們解決此問題。 這是我們要為“我的加密貨幣”應用添加的最后丟失的部分。

Coroutines are a Kotlin feature that convert async callbacks for long-running tasks, such as database or network access, into sequential code. With coroutines, you can write asynchronous code, which was traditionally written using the Callback pattern, using a synchronous style. The return value of a function will provide the result of the asynchronous call. Code written sequentially is typically easier to read, and can even use language features such as exceptions.

協程是Kotlin的一項功能,可將長時間運行的任務(如數據庫或網絡訪問)的異步回調轉換為順序代碼。 使用協程,您可以使用異步樣式編寫異步代碼,該代碼通常是使用Callback模式編寫的。 函數的返回值將提供異步調用的結果。 順序編寫的代碼通常更易于閱讀,甚至可以使用諸如異常之類的語言功能。

So we are going to use coroutines everywhere in this app where we need to wait until a result is available from a long-running task and than continue execution. Let’s see one exact implementation for our ViewModel where we will retry getting the latest data from the server for our cryptocurrencies presented on the main screen.

因此,我們將在此應用中的所有地方使用協程,我們需要等到長時間運行的任務獲得結果并繼續執行。 讓我們看一下ViewModel的一個確切實現,在該模型中,我們將嘗試從服務器獲取主屏幕上顯示的加密貨幣的最新數據。

First add coroutines to the project:

首先將協程添加到項目中:

// Coroutines support libraries for Kotlin.// Dependencies for coroutines. implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$versions.coroutines"// Dependency is for the special UI context that can be passed to coroutine builders that use // the main thread dispatcher to dispatch events on the main thread. implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$versions.coroutines"

Then we will create abstract class which will become the base class to be used for any ViewModel that needs to have common functionality like coroutines in our case:

然后,我們將創建抽象類,該抽象類將成為所有需要具有通用功能(例如協程)的ViewModel使用的基類:

abstract class BaseViewModel : ViewModel() {// In Kotlin, all coroutines run inside a CoroutineScope.// A scope controls the lifetime of coroutines through its job.private val viewModelJob = Job()// Since uiScope has a default dispatcher of Dispatchers.Main, this coroutine will be launched// in the main thread.val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)// onCleared is called when the ViewModel is no longer used and will be destroyed.// This typically happens when the user navigates away from the Activity or Fragment that was// using the ViewModel.override fun onCleared() {super.onCleared()// When you cancel the job of a scope, it cancels all coroutines started in that scope.// It's important to cancel any coroutines that are no longer required to avoid unnecessary// work and memory leaks.viewModelJob.cancel()} }

Here we create specific coroutine scope, which will control the lifetime of coroutines through its job. As you see, scope allows you to specify a default dispatcher that controls which thread runs a coroutine. When the ViewModel is no longer used, we cancel viewModelJob and with that every coroutine started by uiScope will be cancelled as well.

在這里,我們創建了特定的協程范圍,它將通過其工作來控制協程的壽命。 如您所見,作用域使您可以指定一個默認調度程序,該調度程序控制哪個線程運行協程。 當不再使用ViewModel時,我們將取消viewModelJob并且uiScope啟動的每個協程uiScope將被取消。

Finally, implement the retry functionality:

最后,實現重試功能:

/*** The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way.* The ViewModel class allows data to survive configuration changes such as screen rotations.*/// ViewModel will require a CryptocurrencyRepository so we add @Inject code into ViewModel constructor. class MainViewModel @Inject constructor(val context: Context, val cryptocurrencyRepository: CryptocurrencyRepository) : BaseViewModel() {...val mediatorLiveDataMyCryptocurrencyResourceList = MediatorLiveData<Resource<List<MyCryptocurrency>>>()private var liveDataMyCryptocurrencyResourceList: LiveData<Resource<List<MyCryptocurrency>>>private val liveDataMyCryptocurrencyList: LiveData<List<MyCryptocurrency>>...// This is additional helper variable to deal correctly with currency spinner and preference.// It is kept inside viewmodel not to be lost because of fragment/activity recreation.var newSelectedFiatCurrencyCode: String? = null// Helper variable to store state of swipe refresh layout.var isSwipeRefreshing: Boolean = falseinit {...// Set a resource value for a list of cryptocurrencies that user owns.liveDataMyCryptocurrencyResourceList = cryptocurrencyRepository.getMyCryptocurrencyLiveDataResourceList(cryptocurrencyRepository.getCurrentFiatCurrencyCode())// Declare additional variable to be able to reload data on demand.mediatorLiveDataMyCryptocurrencyResourceList.addSource(liveDataMyCryptocurrencyResourceList) {mediatorLiveDataMyCryptocurrencyResourceList.value = it}...}.../*** On retry we need to run sequential code. First we need to get owned crypto coins ids from* local database, wait for response and only after it use these ids to make a call with* retrofit to get updated owned crypto values. This can be done using Kotlin Coroutines.*/fun retry(newFiatCurrencyCode: String? = null) {// Here we store new selected currency as additional variable or reset it.// Later if call to server is unsuccessful we will reuse it for retry functionality.newSelectedFiatCurrencyCode = newFiatCurrencyCode// Launch a coroutine in uiScope.uiScope.launch {// Make a call to the server after some delay for better user experience.updateMyCryptocurrencyList(newFiatCurrencyCode, SERVER_CALL_DELAY_MILLISECONDS)}}// Refresh the data from local database.fun refreshMyCryptocurrencyResourceList() {refreshMyCryptocurrencyResourceList(cryptocurrencyRepository.getMyCryptocurrencyLiveDataResourceList(cryptocurrencyRepository.getCurrentFiatCurrencyCode()))}// To implement a manual refresh without modifying your existing LiveData logic.private fun refreshMyCryptocurrencyResourceList(liveData: LiveData<Resource<List<MyCryptocurrency>>>) {mediatorLiveDataMyCryptocurrencyResourceList.removeSource(liveDataMyCryptocurrencyResourceList)liveDataMyCryptocurrencyResourceList = liveDatamediatorLiveDataMyCryptocurrencyResourceList.addSource(liveDataMyCryptocurrencyResourceList){ mediatorLiveDataMyCryptocurrencyResourceList.value = it }}private suspend fun updateMyCryptocurrencyList(newFiatCurrencyCode: String? = null, callDelay: Long = 0) {val fiatCurrencyCode: String = newFiatCurrencyCode?: cryptocurrencyRepository.getCurrentFiatCurrencyCode()isSwipeRefreshing = true// The function withContext is a suspend function. The withContext immediately shifts// execution of the block into different thread inside the block, and back when it// completes. IO dispatcher is suitable for execution the network requests in IO thread.val myCryptocurrencyIds = withContext(Dispatchers.IO) {// Suspend until getMyCryptocurrencyIds() returns a result.cryptocurrencyRepository.getMyCryptocurrencyIds()}// Here we come back to main worker thread. As soon as myCryptocurrencyIds has a result// and main looper is available, coroutine resumes on main thread, and// [getMyCryptocurrencyLiveDataResourceList] is called.// We wait for background operations to complete, without blocking the original thread.refreshMyCryptocurrencyResourceList(cryptocurrencyRepository.getMyCryptocurrencyLiveDataResourceList(fiatCurrencyCode, true, myCryptocurrencyIds, callDelay))}... }

Here we call a function marked with a special Kotlin keyword suspend for coroutines. This means that the function suspends execution until the result is ready, then it resumes where it left off with the result. While it is suspended waiting for a result, it unblocks the thread that it is running on.

在這里,我們調用標有特殊Kotlin關鍵字的函數來suspend協程。 這意味著該函數將暫停執行直到結果準備就緒,然后再從結果中停止執行。 在掛起等待結果時,它會解除阻塞正在運行的線程。

Also, in one suspend function we can call another suspend function. As you see we do that by calling new suspend function marked withContext that is executed on different thread.

同樣,在一個暫停函數中,我們可以調用另一個暫停函數。 如您所見,我們通過調用標記為withContext新的掛起函數來執行withContext ,該函數在不同的線程上執行。

The idea of all this code is that we can combine multiple calls to form nice-looking sequential code. First we request to get the ids of the cryptocurrencies we own from the local database and wait for the response. Only after we get it do we use the response ids to make a new call with Retrofit to get those updated cryptocurrency values. That is our retry functionality.

所有這些代碼的想法是,我們可以組合多個調用以形成美觀的順序代碼。 首先,我們要求從本地數據庫獲取我們擁有的加密貨幣的ID,然后等待響應。 只有在得到它之后,我們才使用響應ID使用Retrofit進行新的調用以獲取那些更新的加密貨幣值。 那就是我們的重試功能。

我們做到了! 最終想法,存儲庫,應用程序和演示文稿 (We made it! Final thoughts, repository, app & presentation)

Congratulations, I am happy if you managed to reach to the end. All the most significant points for creating this app have been covered. There was plenty of new stuff done in this part and a lot of that is not covered by this article, but I commented my code everywhere very well so you should not get lost in it. Check out final code for this part 5 here on GitHub:

恭喜,如果您設法做到最后,我很高興。 涵蓋了創建此應用程序的所有最重要的要點。 在這一部分中有很多新的東西要做,但是本文沒有涉及很多,但是我在所有地方都很好地評論了我的代碼,因此您不要迷路。 在GitHub上查看第5部分的最終代碼:

View Source On GitHub.

在GitHub上查看源代碼 。

The biggest challenge for me personally was not to learn new technologies, not to develop the app, but to write all these articles. Actually I am very happy with myself that I completed this challenge. Learning and developing is easy compared to teaching others, but that is where you can understand the topic even better. My advice if you are looking for the best way to learn new things is to start creating something yourself immediately. I promise you will learn a lot and quickly.

我個人面臨的最大挑戰不是學習新技術,開發應用程序,而是寫所有這些文章。 實際上,我對完成這項挑戰感到非常滿意。 與教別人相比,學習和發展很容易,但是在這里您可以更好地理解該主題。 如果您正在尋找學習新事物的最佳方法,我的建議是立即開始自己創建一些東西。 我保證您會學到很多東西并且很快。

All these articles are based on version 1.0.0 of “Kriptofolio” (previously “My Crypto Coins”) app which you can download as a separate APK file here. But I will be very happy if you install and rate the latest app version from the store directly:

所有這些文章均基于“ Kriptofolio”(以前稱為“我的加密貨幣”)應用1.0.0版,您可以在此處將其下載為單獨的APK文件。 但是,如果您直接從商店中安裝最新應用程序版本并對其進行評分,我將非常高興:

在Google Play上獲取 (Get It On Google Play)

Also please feel free to visit this simple presentation website that I made for this project:

另外,請隨時訪問我為此項目制作的這個簡單的演示網站:

Kriptofolio.app (Kriptofolio.app)



A?iū! Thanks for reading! I originally published this post for my personal blog www.baruckis.com on May 11, 2019.

阿奇! 謝謝閱讀! 我最初于2019年5月11日在我的個人博客www.baruckis.com上發布了這篇文章。

翻譯自: https://www.freecodecamp.org/news/kriptofolio-app-series-part-5/

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的如何使用Retrofit,OkHttp,Gson,Glide和Coroutines处理RESTful Web服务的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

久久精品视频在线看15 | 少妇一晚三次一区二区三区 | 激情五月综合色婷婷一区二区 | 精品国产麻豆免费人成网站 | 天堂无码人妻精品一区二区三区 | 国产精品a成v人在线播放 | 久久亚洲国产成人精品性色 | 亚洲精品无码人妻无码 | 国产69精品久久久久app下载 | 男人的天堂av网站 | 亚洲日本一区二区三区在线 | 欧美真人作爱免费视频 | 国产人妻久久精品二区三区老狼 | 欧美三级不卡在线观看 | 亚欧洲精品在线视频免费观看 | 亚洲自偷自偷在线制服 | 天天躁夜夜躁狠狠是什么心态 | 国产乱人伦偷精品视频 | 国产精品无码成人午夜电影 | 国产精品va在线播放 | 欧美午夜特黄aaaaaa片 | 成年女人永久免费看片 | 人妻天天爽夜夜爽一区二区 | 国产成人精品久久亚洲高清不卡 | 欧美成人午夜精品久久久 | 亚洲第一网站男人都懂 | 丰满少妇高潮惨叫视频 | 国产午夜精品一区二区三区嫩草 | 国产sm调教视频在线观看 | 亚洲日韩av一区二区三区四区 | 国产熟妇另类久久久久 | 亚洲一区av无码专区在线观看 | 国产精品嫩草久久久久 | 国产做国产爱免费视频 | 亚洲欧洲日本无在线码 | аⅴ资源天堂资源库在线 | 免费国产成人高清在线观看网站 | 亚洲精品一区三区三区在线观看 | 欧洲美熟女乱又伦 | 精品国产一区二区三区四区在线看 | 亚洲成在人网站无码天堂 | 人人爽人人澡人人人妻 | 东京热无码av男人的天堂 | 国产免费观看黄av片 | 国产熟妇另类久久久久 | 成人免费视频一区二区 | 国内少妇偷人精品视频 | 亚洲日韩av片在线观看 | 亚洲啪av永久无码精品放毛片 | 国产亚洲欧美日韩亚洲中文色 | 熟女俱乐部五十路六十路av | 久久国产精品偷任你爽任你 | 国产激情艳情在线看视频 | 波多野结衣乳巨码无在线观看 | 久久精品无码一区二区三区 | 蜜桃av抽搐高潮一区二区 | 亚洲国产精华液网站w | 免费无码av一区二区 | 国产精品人人爽人人做我的可爱 | 日本一区二区三区免费高清 | 久久无码专区国产精品s | 精品国偷自产在线 | 正在播放老肥熟妇露脸 | 中文字幕 亚洲精品 第1页 | 国产成人亚洲综合无码 | 一本色道久久综合亚洲精品不卡 | 国产亚洲精品久久久久久国模美 | 国内综合精品午夜久久资源 | 欧美第一黄网免费网站 | 久久久久免费看成人影片 | 成人av无码一区二区三区 | 久久久久久久女国产乱让韩 | 亚洲成av人在线观看网址 | 精品水蜜桃久久久久久久 | 丝袜 中出 制服 人妻 美腿 | 国产黄在线观看免费观看不卡 | 久久久久久久人妻无码中文字幕爆 | 亚洲精品一区三区三区在线观看 | 国产乱子伦视频在线播放 | 乌克兰少妇xxxx做受 | 无码人妻丰满熟妇区五十路百度 | 亚洲精品国偷拍自产在线观看蜜桃 | 免费中文字幕日韩欧美 | 国产精品久久久一区二区三区 | 未满成年国产在线观看 | 少妇一晚三次一区二区三区 | 熟妇人妻激情偷爽文 | 亚洲午夜福利在线观看 | aⅴ亚洲 日韩 色 图网站 播放 | 美女极度色诱视频国产 | 全球成人中文在线 | 澳门永久av免费网站 | 久久精品丝袜高跟鞋 | 国产精品18久久久久久麻辣 | 国产又粗又硬又大爽黄老大爷视 | 无码帝国www无码专区色综合 | aⅴ亚洲 日韩 色 图网站 播放 | 性生交大片免费看l | 久久熟妇人妻午夜寂寞影院 | 国产熟妇另类久久久久 | 国产精品久久久一区二区三区 | 国产成人精品视频ⅴa片软件竹菊 | 欧美精品一区二区精品久久 | 国产精品亚洲а∨无码播放麻豆 | 强开小婷嫩苞又嫩又紧视频 | 欧美性生交xxxxx久久久 | 成人无码精品1区2区3区免费看 | 少妇人妻大乳在线视频 | 免费无码一区二区三区蜜桃大 | 亚洲午夜福利在线观看 | 国产97在线 | 亚洲 | 免费人成在线观看网站 | 又湿又紧又大又爽a视频国产 | 国产情侣作爱视频免费观看 | 日韩人妻无码中文字幕视频 | 日韩欧美成人免费观看 | 亚洲精品午夜无码电影网 | 亚洲 欧美 激情 小说 另类 | 久久精品丝袜高跟鞋 | 欧美日韩亚洲国产精品 | 久久国产精品精品国产色婷婷 | 久久午夜无码鲁丝片秋霞 | 欧美熟妇另类久久久久久不卡 | 亚洲精品国产精品乱码视色 | 亚洲娇小与黑人巨大交 | 精品日本一区二区三区在线观看 | 无码精品国产va在线观看dvd | 亚洲色偷偷男人的天堂 | 人妻天天爽夜夜爽一区二区 | 高潮毛片无遮挡高清免费 | 亚洲一区二区三区四区 | 日日天日日夜日日摸 | 欧美老妇与禽交 | 大地资源网第二页免费观看 | 日日噜噜噜噜夜夜爽亚洲精品 | 无码av中文字幕免费放 | 最新国产乱人伦偷精品免费网站 | 日本www一道久久久免费榴莲 | 对白脏话肉麻粗话av | 国色天香社区在线视频 | 久热国产vs视频在线观看 | 内射欧美老妇wbb | 人妻体内射精一区二区三四 | 免费无码肉片在线观看 | 鲁一鲁av2019在线 | 国产精品资源一区二区 | 久久99精品国产麻豆蜜芽 | 亚洲热妇无码av在线播放 | 在线 国产 欧美 亚洲 天堂 | 熟女少妇人妻中文字幕 | 水蜜桃亚洲一二三四在线 | 日本高清一区免费中文视频 | 中文字幕无码av波多野吉衣 | 东京热男人av天堂 | 黑人玩弄人妻中文在线 | 牲欲强的熟妇农村老妇女视频 | 无码av中文字幕免费放 | 亚洲七七久久桃花影院 | 荫蒂添的好舒服视频囗交 | 久久伊人色av天堂九九小黄鸭 | 樱花草在线播放免费中文 | 日本一卡二卡不卡视频查询 | 狠狠色噜噜狠狠狠7777奇米 | 亚洲精品一区二区三区在线观看 | 欧美日本日韩 | 久久精品国产一区二区三区肥胖 | 噜噜噜亚洲色成人网站 | 4hu四虎永久在线观看 | 牛和人交xxxx欧美 | 日韩无套无码精品 | 狂野欧美激情性xxxx | 国产福利视频一区二区 | 国产免费无码一区二区视频 | 少妇一晚三次一区二区三区 | 成熟人妻av无码专区 | 高潮毛片无遮挡高清免费视频 | 国产香蕉97碰碰久久人人 | 欧洲vodafone精品性 | 中文字幕人妻丝袜二区 | 久久zyz资源站无码中文动漫 | 岛国片人妻三上悠亚 | 免费人成在线观看网站 | 2020久久香蕉国产线看观看 | 亚洲小说春色综合另类 | 任你躁在线精品免费 | 国产网红无码精品视频 | 色偷偷av老熟女 久久精品人妻少妇一区二区三区 | 无码午夜成人1000部免费视频 | 爱做久久久久久 | 中文字幕日韩精品一区二区三区 | 亚洲日本va午夜在线电影 | 国产精品亚洲а∨无码播放麻豆 | 99久久精品国产一区二区蜜芽 | 四虎国产精品一区二区 | 久久国产精品精品国产色婷婷 | 精品无码一区二区三区的天堂 | аⅴ资源天堂资源库在线 | 国产sm调教视频在线观看 | 综合激情五月综合激情五月激情1 | 国产猛烈高潮尖叫视频免费 | 狠狠色丁香久久婷婷综合五月 | 亚洲精品国偷拍自产在线观看蜜桃 | 亚洲综合另类小说色区 | 国产莉萝无码av在线播放 | 久久久久久av无码免费看大片 | 久久 国产 尿 小便 嘘嘘 | 亚洲日韩一区二区三区 | 成人免费无码大片a毛片 | 国产精品久久久久久久9999 | 国产精品美女久久久网av | 无遮挡国产高潮视频免费观看 | 内射巨臀欧美在线视频 | 欧美日韩视频无码一区二区三 | 欧美日韩亚洲国产精品 | 久久久无码中文字幕久... | 日欧一片内射va在线影院 | 欧美刺激性大交 | 又湿又紧又大又爽a视频国产 | 亚洲中文字幕在线观看 | 久久精品人妻少妇一区二区三区 | 2019nv天堂香蕉在线观看 | a片在线免费观看 | 久久无码中文字幕免费影院蜜桃 | 日本乱人伦片中文三区 | 又紧又大又爽精品一区二区 | 国产97人人超碰caoprom | 国产精品二区一区二区aⅴ污介绍 | 日本大香伊一区二区三区 | 欧美亚洲日韩国产人成在线播放 | 中文字幕av伊人av无码av | 性色欲情网站iwww九文堂 | 久久精品国产一区二区三区肥胖 | 国产办公室秘书无码精品99 | 久在线观看福利视频 | 精品欧美一区二区三区久久久 | 99久久精品午夜一区二区 | 国产精品毛片一区二区 | 无码av免费一区二区三区试看 | 人妻人人添人妻人人爱 | 麻豆国产人妻欲求不满 | 性色欲情网站iwww九文堂 | 中文字幕日韩精品一区二区三区 | 在线a亚洲视频播放在线观看 | 久久久久久av无码免费看大片 | 国产艳妇av在线观看果冻传媒 | 免费无码的av片在线观看 | 成人欧美一区二区三区黑人免费 | 桃花色综合影院 | 欧美性猛交内射兽交老熟妇 | 欧美国产日韩亚洲中文 | 亚洲一区二区观看播放 | 日本又色又爽又黄的a片18禁 | 亚洲综合在线一区二区三区 | 波多野结衣av一区二区全免费观看 | 亚洲乱码中文字幕在线 | 中文字幕av伊人av无码av | 日本爽爽爽爽爽爽在线观看免 | 无码吃奶揉捏奶头高潮视频 | 国产内射爽爽大片视频社区在线 | 欧美人与善在线com | 欧美日本日韩 | 中文字幕av伊人av无码av | 午夜男女很黄的视频 | 免费男性肉肉影院 | 亚洲中文字幕av在天堂 | 成人免费视频一区二区 | 老太婆性杂交欧美肥老太 | 精品国偷自产在线 | 免费国产黄网站在线观看 | 国产无av码在线观看 | 国产欧美精品一区二区三区 | 精品国精品国产自在久国产87 | 国产亚洲人成在线播放 | 色情久久久av熟女人妻网站 | 久久午夜无码鲁丝片午夜精品 | 国产熟妇另类久久久久 | 亚洲va中文字幕无码久久不卡 | 国产婷婷色一区二区三区在线 | 老子影院午夜精品无码 | 亚洲a无码综合a国产av中文 | 精品无码av一区二区三区 | 亚洲人亚洲人成电影网站色 | 国内揄拍国内精品人妻 | 精品国产青草久久久久福利 | 成人av无码一区二区三区 | 欧美一区二区三区视频在线观看 | 久久精品视频在线看15 | 中文字幕无码乱人伦 | 精品久久8x国产免费观看 | 国产农村妇女高潮大叫 | 亚洲成a人一区二区三区 | 欧美放荡的少妇 | 国产在线无码精品电影网 | 国产精品第一区揄拍无码 | 欧美性猛交xxxx富婆 | 久久久中文字幕日本无吗 | 丰满诱人的人妻3 | 西西人体www44rt大胆高清 | 久久久精品成人免费观看 | 在线播放免费人成毛片乱码 | 天天摸天天透天天添 | 久久亚洲a片com人成 | 国产后入清纯学生妹 | 国产极品视觉盛宴 | 亚洲国产精品成人久久蜜臀 | 色综合久久中文娱乐网 | 国产精品亚洲lv粉色 | 99久久精品午夜一区二区 | 亚洲一区二区三区国产精华液 | 亚洲人成人无码网www国产 | 在教室伦流澡到高潮hnp视频 | 九月婷婷人人澡人人添人人爽 | 一区二区三区高清视频一 | 奇米影视888欧美在线观看 | 色偷偷av老熟女 久久精品人妻少妇一区二区三区 | 人妻少妇精品视频专区 | 亚洲日本一区二区三区在线 | 亚洲国产精华液网站w | 无码人妻出轨黑人中文字幕 | 午夜精品一区二区三区在线观看 | 九九综合va免费看 | 久久99精品国产.久久久久 | www国产亚洲精品久久久日本 | 77777熟女视频在线观看 а天堂中文在线官网 | 丰满人妻精品国产99aⅴ | www一区二区www免费 | 亚洲日韩精品欧美一区二区 | 人人超人人超碰超国产 | 亚洲精品美女久久久久久久 | 国产做国产爱免费视频 | 正在播放东北夫妻内射 | 成人无码精品1区2区3区免费看 | 国产一区二区不卡老阿姨 | 婷婷五月综合缴情在线视频 | 98国产精品综合一区二区三区 | 人人爽人人爽人人片av亚洲 | 日本一区二区三区免费高清 | 日韩无套无码精品 | 婷婷丁香五月天综合东京热 | 黑人大群体交免费视频 | 久久综合给合久久狠狠狠97色 | 久久久精品456亚洲影院 | 亚洲午夜无码久久 | 久久精品国产一区二区三区肥胖 | 精品国产aⅴ无码一区二区 | 久久久中文字幕日本无吗 | 成人aaa片一区国产精品 | 欧美丰满熟妇xxxx性ppx人交 | 精品成人av一区二区三区 | 欧美xxxx黑人又粗又长 | 亚洲欧美色中文字幕在线 | 色综合久久久久综合一本到桃花网 | 国产成人精品久久亚洲高清不卡 | 99麻豆久久久国产精品免费 | 国产成人无码av在线影院 | 无码免费一区二区三区 | 天天做天天爱天天爽综合网 | 女人高潮内射99精品 | 亚无码乱人伦一区二区 | 未满小14洗澡无码视频网站 | 未满成年国产在线观看 | 国产成人无码一二三区视频 | 丝袜人妻一区二区三区 | 丰满少妇熟乱xxxxx视频 | 天天躁日日躁狠狠躁免费麻豆 | 国产一精品一av一免费 | 久久99精品国产麻豆 | 国产猛烈高潮尖叫视频免费 | 极品尤物被啪到呻吟喷水 | 亚洲乱码中文字幕在线 | 亚洲精品中文字幕 | 亚洲色成人中文字幕网站 | 粗大的内捧猛烈进出视频 | 99久久人妻精品免费二区 | 一本色道久久综合亚洲精品不卡 | 国产又爽又黄又刺激的视频 | 成人精品视频一区二区三区尤物 | 国产热a欧美热a在线视频 | 牲欲强的熟妇农村老妇女视频 | 亚洲国产精品久久人人爱 | 精品水蜜桃久久久久久久 | 在教室伦流澡到高潮hnp视频 | 国产亚洲精品久久久久久大师 | 亚洲色大成网站www国产 | 人人妻人人澡人人爽欧美一区九九 | 久久成人a毛片免费观看网站 | 国产午夜福利100集发布 | 露脸叫床粗话东北少妇 | 国产在线精品一区二区高清不卡 | 久久国产36精品色熟妇 | 午夜肉伦伦影院 | 亚洲成av人片在线观看无码不卡 | 人妻无码αv中文字幕久久琪琪布 | 中文字幕人妻无码一区二区三区 | 麻豆果冻传媒2021精品传媒一区下载 | 国产精品毛多多水多 | 久久精品丝袜高跟鞋 | 精品国产乱码久久久久乱码 | 国产精品久免费的黄网站 | 天天躁日日躁狠狠躁免费麻豆 | 久久久久久久女国产乱让韩 | 欧洲熟妇精品视频 | 精品无码国产一区二区三区av | 美女毛片一区二区三区四区 | 欧美三级a做爰在线观看 | 中文字幕+乱码+中文字幕一区 | 国内揄拍国内精品少妇国语 | 少妇愉情理伦片bd | 免费观看激色视频网站 | 亚洲人亚洲人成电影网站色 | 国产无av码在线观看 | 夜精品a片一区二区三区无码白浆 | 正在播放老肥熟妇露脸 | 日本一卡2卡3卡四卡精品网站 | 亚洲男人av天堂午夜在 | 奇米影视888欧美在线观看 | 亚洲熟女一区二区三区 | 久久久久免费精品国产 | 亚洲精品中文字幕 | 中文字幕色婷婷在线视频 | 无码国内精品人妻少妇 | 男人的天堂2018无码 | 最近中文2019字幕第二页 | 国产亚洲精品久久久久久国模美 | 国产精品久久久 | 久久久久99精品成人片 | 狂野欧美性猛交免费视频 | 国产成人精品一区二区在线小狼 | 国产成人无码av一区二区 | 日本大香伊一区二区三区 | 久久精品中文闷骚内射 | 中文字幕 亚洲精品 第1页 | 精品日本一区二区三区在线观看 | 又大又硬又爽免费视频 | 巨爆乳无码视频在线观看 | 国产特级毛片aaaaaa高潮流水 | 狠狠亚洲超碰狼人久久 | 97色伦图片97综合影院 | 日本护士毛茸茸高潮 | 学生妹亚洲一区二区 | 久久亚洲中文字幕精品一区 | 18禁黄网站男男禁片免费观看 | 少妇人妻偷人精品无码视频 | 一本久道久久综合婷婷五月 | 97资源共享在线视频 | 一区二区三区高清视频一 | 亚洲成av人在线观看网址 | 草草网站影院白丝内射 | 55夜色66夜色国产精品视频 | 国产亚洲tv在线观看 | 欧美日韩一区二区免费视频 | 国产9 9在线 | 中文 | 国产免费无码一区二区视频 | 国产亚洲精品久久久ai换 | 捆绑白丝粉色jk震动捧喷白浆 | 亚洲国产精品成人久久蜜臀 | 国产精品高潮呻吟av久久 | 东京无码熟妇人妻av在线网址 | 亚洲成a人片在线观看无码3d | 奇米影视888欧美在线观看 | 俺去俺来也www色官网 | 国产人妻精品一区二区三区 | 天天av天天av天天透 | 日本护士xxxxhd少妇 | 色情久久久av熟女人妻网站 | 欧美猛少妇色xxxxx | 99久久精品日本一区二区免费 | 国产在线精品一区二区高清不卡 | 人妻体内射精一区二区三四 | 国产精品久久国产三级国 | 精品无码av一区二区三区 | 亚洲 另类 在线 欧美 制服 | 久久99精品久久久久婷婷 | 成年女人永久免费看片 | 综合激情五月综合激情五月激情1 | 国产精品免费大片 | 曰韩无码二三区中文字幕 | 啦啦啦www在线观看免费视频 | 国产激情无码一区二区app | 国产成人av免费观看 | 国产午夜手机精彩视频 | 亚洲欧美综合区丁香五月小说 | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 性做久久久久久久免费看 | 4hu四虎永久在线观看 | 高清国产亚洲精品自在久久 | 精品一区二区三区无码免费视频 | 日日摸天天摸爽爽狠狠97 | 久久综合网欧美色妞网 | 中文字幕 亚洲精品 第1页 | 撕开奶罩揉吮奶头视频 | 国产亚洲精品久久久久久久久动漫 | 97久久精品无码一区二区 | 国产激情精品一区二区三区 | 国产人妻精品一区二区三区 | 久久久精品成人免费观看 | 日日麻批免费40分钟无码 | 欧美第一黄网免费网站 | 欧美真人作爱免费视频 | 国产精品人人爽人人做我的可爱 | 国产激情精品一区二区三区 | 成人一区二区免费视频 | 亚洲色欲色欲欲www在线 | 亚洲 a v无 码免 费 成 人 a v | 久久精品99久久香蕉国产色戒 | 高潮毛片无遮挡高清免费视频 | 午夜免费福利小电影 | 狠狠cao日日穞夜夜穞av | 国产av一区二区精品久久凹凸 | 波多野结衣高清一区二区三区 | 一本加勒比波多野结衣 | 国产又爽又猛又粗的视频a片 | 三上悠亚人妻中文字幕在线 | 国产无套粉嫩白浆在线 | 激情爆乳一区二区三区 | 白嫩日本少妇做爰 | 天海翼激烈高潮到腰振不止 | 日本熟妇人妻xxxxx人hd | 四虎国产精品免费久久 | 男人的天堂av网站 | 国产区女主播在线观看 | 久久久久久久女国产乱让韩 | 欧美日韩一区二区三区自拍 | 国产小呦泬泬99精品 | 对白脏话肉麻粗话av | 亚洲男人av天堂午夜在 | 初尝人妻少妇中文字幕 | 国内丰满熟女出轨videos | 国产精品久免费的黄网站 | 成人精品一区二区三区中文字幕 | 男女下面进入的视频免费午夜 | 亚洲日本va午夜在线电影 | 久久久国产一区二区三区 | 婷婷六月久久综合丁香 | 日本大乳高潮视频在线观看 | 2020久久香蕉国产线看观看 | 高中生自慰www网站 | 亚洲第一网站男人都懂 | 精品久久久久久亚洲精品 | 九九综合va免费看 | 日韩无码专区 | 国产成人无码区免费内射一片色欲 | 精品一区二区三区波多野结衣 | 日韩在线不卡免费视频一区 | 图片小说视频一区二区 | 亚洲の无码国产の无码影院 | 中文字幕无码免费久久9一区9 | 国产亚洲精品久久久ai换 | 中文字幕+乱码+中文字幕一区 | 99久久精品日本一区二区免费 | 久久亚洲精品中文字幕无男同 | 国产人妻精品一区二区三区不卡 | 久久综合久久自在自线精品自 | 装睡被陌生人摸出水好爽 | 中文字幕 人妻熟女 | 成人性做爰aaa片免费看 | yw尤物av无码国产在线观看 | 国产偷抇久久精品a片69 | 亚洲精品久久久久中文第一幕 | 夫妻免费无码v看片 | 国产亚洲视频中文字幕97精品 | 亚洲国产成人a精品不卡在线 | 人人妻在人人 | 撕开奶罩揉吮奶头视频 | 国产疯狂伦交大片 | 国产凸凹视频一区二区 | 综合激情五月综合激情五月激情1 | 日本一区二区三区免费播放 | 国产成人午夜福利在线播放 | 青青草原综合久久大伊人精品 | 人妻与老人中文字幕 | 久久亚洲精品中文字幕无男同 | 娇妻被黑人粗大高潮白浆 | 亚洲 激情 小说 另类 欧美 | 亚洲精品成a人在线观看 | 亚洲中文字幕乱码av波多ji | 日本www一道久久久免费榴莲 | 亚洲国产午夜精品理论片 | 国产极品美女高潮无套在线观看 | 欧美三级不卡在线观看 | 老子影院午夜精品无码 | 特黄特色大片免费播放器图片 | 亚洲成色在线综合网站 | 亚洲中文字幕成人无码 | 俺去俺来也在线www色官网 | 国产精品.xx视频.xxtv | 久久99精品国产麻豆 | 久久久久久a亚洲欧洲av冫 | 精品无人区无码乱码毛片国产 | 三上悠亚人妻中文字幕在线 | 日韩人妻系列无码专区 | 台湾无码一区二区 | v一区无码内射国产 | 亲嘴扒胸摸屁股激烈网站 | 扒开双腿疯狂进出爽爽爽视频 | 奇米影视888欧美在线观看 | 国内精品人妻无码久久久影院 | 老熟妇乱子伦牲交视频 | 国产三级久久久精品麻豆三级 | 人人澡人人透人人爽 | 亚洲第一网站男人都懂 | 伊人久久大香线焦av综合影院 | 四虎国产精品一区二区 | 色噜噜亚洲男人的天堂 | 国产精品办公室沙发 | 亚洲国产av精品一区二区蜜芽 | 婷婷色婷婷开心五月四房播播 | 国产精品国产自线拍免费软件 | 色五月五月丁香亚洲综合网 | 国产精品国产自线拍免费软件 | 牲欲强的熟妇农村老妇女视频 | 中文字幕久久久久人妻 | 欧美 日韩 人妻 高清 中文 | 国产av一区二区精品久久凹凸 | 天天做天天爱天天爽综合网 | 亚洲人成网站色7799 | 亚洲の无码国产の无码步美 | 无码吃奶揉捏奶头高潮视频 | 国产精品久久久久9999小说 | 天天做天天爱天天爽综合网 | 久久午夜无码鲁丝片秋霞 | 少妇的肉体aa片免费 | 精品午夜福利在线观看 | 性做久久久久久久免费看 | 人妻天天爽夜夜爽一区二区 | 人妻少妇精品视频专区 | 亚洲精品成a人在线观看 | 4hu四虎永久在线观看 | 日本爽爽爽爽爽爽在线观看免 | 蜜桃av抽搐高潮一区二区 | 亚洲а∨天堂久久精品2021 | 国产精品人人妻人人爽 | 亚洲 高清 成人 动漫 | av在线亚洲欧洲日产一区二区 | 丁香花在线影院观看在线播放 | 女人高潮内射99精品 | 伊人久久大香线焦av综合影院 | 美女毛片一区二区三区四区 | 欧洲美熟女乱又伦 | 成人精品天堂一区二区三区 | 高中生自慰www网站 | 在线观看国产午夜福利片 | 国产婷婷色一区二区三区在线 | 亚洲精品欧美二区三区中文字幕 | 亚洲精品国产精品乱码视色 | 撕开奶罩揉吮奶头视频 | 麻豆精产国品 | 亚洲性无码av中文字幕 | 人人妻人人藻人人爽欧美一区 | 精品欧美一区二区三区久久久 | 女人被爽到呻吟gif动态图视看 | 欧美人与禽zoz0性伦交 | 成人毛片一区二区 | 在线观看欧美一区二区三区 | 乱码av麻豆丝袜熟女系列 | 人妻少妇被猛烈进入中文字幕 | 天天躁夜夜躁狠狠是什么心态 | 鲁鲁鲁爽爽爽在线视频观看 | 最新国产乱人伦偷精品免费网站 | 玩弄人妻少妇500系列视频 | 国产香蕉尹人视频在线 | 色一情一乱一伦一区二区三欧美 | 伊人久久大香线蕉亚洲 | 久久人妻内射无码一区三区 | 久久99精品国产麻豆蜜芽 | 成人免费无码大片a毛片 | 久久国内精品自在自线 | 国产成人综合色在线观看网站 | 98国产精品综合一区二区三区 | 精品 日韩 国产 欧美 视频 | 领导边摸边吃奶边做爽在线观看 | 亚洲成a人片在线观看无码3d | 奇米影视7777久久精品 | 中文字幕av伊人av无码av | 精品一区二区三区波多野结衣 | 日日摸夜夜摸狠狠摸婷婷 | 中文字幕+乱码+中文字幕一区 | 国产亚洲日韩欧美另类第八页 | 国产精品亚洲一区二区三区喷水 | 欧美三级不卡在线观看 | 久久久久人妻一区精品色欧美 | 在线天堂新版最新版在线8 | 国产精品毛片一区二区 | 国产在线无码精品电影网 | 乱码午夜-极国产极内射 | 成熟人妻av无码专区 | 国产九九九九九九九a片 | 中文字幕人妻无码一夲道 | 精品国产乱码久久久久乱码 | 国内精品久久毛片一区二区 | 亚洲精品一区三区三区在线观看 | 国产特级毛片aaaaaaa高清 | 亚洲欧洲日本无在线码 | 亚洲高清偷拍一区二区三区 | 波多野结衣乳巨码无在线观看 | 又色又爽又黄的美女裸体网站 | 欧美国产日韩亚洲中文 | 四虎国产精品免费久久 | 午夜丰满少妇性开放视频 | 97资源共享在线视频 | 天堂无码人妻精品一区二区三区 | 无码纯肉视频在线观看 | 国产 浪潮av性色四虎 | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 亚洲欧美中文字幕5发布 | 成人av无码一区二区三区 | 成人免费视频一区二区 | 精品久久8x国产免费观看 | 荫蒂添的好舒服视频囗交 | 国产人妻精品一区二区三区不卡 | 强伦人妻一区二区三区视频18 | 欧美人妻一区二区三区 | 亚洲成av人影院在线观看 | 日韩欧美中文字幕在线三区 | 国产又爽又黄又刺激的视频 | 免费播放一区二区三区 | 国模大胆一区二区三区 | 波多野结衣乳巨码无在线观看 | 真人与拘做受免费视频一 | 亚洲欧美日韩成人高清在线一区 | 亚洲va欧美va天堂v国产综合 | 人妻人人添人妻人人爱 | 国产精品无码久久av | 日韩人妻无码一区二区三区久久99 | 女人被男人爽到呻吟的视频 | 亚洲精品一区国产 | 国产精品久久久久久久影院 | 成人一在线视频日韩国产 | 自拍偷自拍亚洲精品被多人伦好爽 | 国产精品爱久久久久久久 | 国产综合久久久久鬼色 | 天堂亚洲2017在线观看 | 亚洲精品一区二区三区在线观看 | 欧美熟妇另类久久久久久不卡 | 午夜熟女插插xx免费视频 | 国产精品国产三级国产专播 | 无码人妻出轨黑人中文字幕 | 欧美人与禽zoz0性伦交 | 小鲜肉自慰网站xnxx | 扒开双腿吃奶呻吟做受视频 | 国产色视频一区二区三区 | 丰满护士巨好爽好大乳 | 亚洲成a人片在线观看无码3d | 麻豆国产人妻欲求不满谁演的 | 男女超爽视频免费播放 | 十八禁真人啪啪免费网站 | 国产一区二区三区四区五区加勒比 | 少妇一晚三次一区二区三区 | 免费无码肉片在线观看 | 亚洲综合另类小说色区 | 黑人粗大猛烈进出高潮视频 | 4hu四虎永久在线观看 | 成在人线av无码免观看麻豆 | 中文字幕 亚洲精品 第1页 | 精品久久久无码人妻字幂 | 亚洲精品国产品国语在线观看 | 国产无套内射久久久国产 | 国产精品-区区久久久狼 | 中文精品无码中文字幕无码专区 | 国精品人妻无码一区二区三区蜜柚 | 日韩无码专区 | 国内综合精品午夜久久资源 | 自拍偷自拍亚洲精品被多人伦好爽 | 成 人 网 站国产免费观看 | 天天av天天av天天透 | 夜夜躁日日躁狠狠久久av | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 全球成人中文在线 | 亚洲成色www久久网站 | 麻豆国产97在线 | 欧洲 | 日韩欧美中文字幕在线三区 | 国产精品va在线观看无码 | 亚洲熟悉妇女xxx妇女av | 狠狠色欧美亚洲狠狠色www | 成人女人看片免费视频放人 | 真人与拘做受免费视频 | 中文字幕无码热在线视频 | 亚洲国产精品久久人人爱 | 综合激情五月综合激情五月激情1 | 无码精品人妻一区二区三区av | 好屌草这里只有精品 | 99精品视频在线观看免费 | 免费国产成人高清在线观看网站 | 无码国产激情在线观看 | 成人一在线视频日韩国产 | 免费无码av一区二区 | 伊在人天堂亚洲香蕉精品区 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 两性色午夜视频免费播放 | 伊在人天堂亚洲香蕉精品区 | 捆绑白丝粉色jk震动捧喷白浆 | 熟妇人妻无码xxx视频 | 欧美zoozzooz性欧美 | 国产日产欧产精品精品app | 国产又爽又猛又粗的视频a片 | 欧美兽交xxxx×视频 | 欧美成人午夜精品久久久 | 欧美丰满老熟妇xxxxx性 | 国产人妻久久精品二区三区老狼 | 男女爱爱好爽视频免费看 | 国产xxx69麻豆国语对白 | 日本大香伊一区二区三区 | 4hu四虎永久在线观看 | 99久久精品国产一区二区蜜芽 | 国产手机在线αⅴ片无码观看 | 欧美丰满老熟妇xxxxx性 | 曰韩少妇内射免费播放 | 欧美丰满少妇xxxx性 | 国产精品18久久久久久麻辣 | 欧美精品一区二区精品久久 | 日韩成人一区二区三区在线观看 | 久久久久久久久888 | 国产成人综合美国十次 | 在线视频网站www色 | 日本一卡二卡不卡视频查询 | 国产疯狂伦交大片 | aⅴ亚洲 日韩 色 图网站 播放 | 成 人 免费观看网站 | 国产激情艳情在线看视频 | 人妻与老人中文字幕 | 又粗又大又硬毛片免费看 | 精品无人国产偷自产在线 | 国产两女互慰高潮视频在线观看 | 久久无码人妻影院 | 少妇的肉体aa片免费 | 国产精品久久久久久无码 | 亚洲 激情 小说 另类 欧美 | 国产超碰人人爽人人做人人添 | 日韩成人一区二区三区在线观看 | 国产精品毛片一区二区 | 扒开双腿吃奶呻吟做受视频 | 大肉大捧一进一出好爽视频 | 久久综合九色综合欧美狠狠 | 久久精品国产亚洲精品 | 性开放的女人aaa片 | 国产精品18久久久久久麻辣 | 男女猛烈xx00免费视频试看 | 图片区 小说区 区 亚洲五月 | 国产av一区二区精品久久凹凸 | 丰满岳乱妇在线观看中字无码 | 图片小说视频一区二区 | 久久人人97超碰a片精品 | 国内精品久久久久久中文字幕 | 久久熟妇人妻午夜寂寞影院 | 精品偷自拍另类在线观看 | 激情综合激情五月俺也去 | 久久99精品久久久久婷婷 | 国产美女极度色诱视频www | 亚洲人交乣女bbw | 亚洲精品国产品国语在线观看 | 97精品人妻一区二区三区香蕉 | 亚无码乱人伦一区二区 | 亚洲精品www久久久 | 欧美激情综合亚洲一二区 | 日日躁夜夜躁狠狠躁 | 欧美freesex黑人又粗又大 | 亚洲日韩av一区二区三区中文 | 高潮毛片无遮挡高清免费视频 | 亚洲熟女一区二区三区 | 熟女少妇人妻中文字幕 | 久久久无码中文字幕久... | 一二三四在线观看免费视频 | 久久人人爽人人爽人人片av高清 | 熟妇女人妻丰满少妇中文字幕 | 中文字幕久久久久人妻 | 99在线 | 亚洲 | 久久久精品欧美一区二区免费 | 婷婷色婷婷开心五月四房播播 | 性色av无码免费一区二区三区 | 国产精品99爱免费视频 | 国内精品久久毛片一区二区 | 天堂亚洲免费视频 | 久久人妻内射无码一区三区 | 图片小说视频一区二区 | 性欧美大战久久久久久久 | 内射爽无广熟女亚洲 | 妺妺窝人体色www在线小说 | 一二三四在线观看免费视频 | 超碰97人人射妻 | 国产sm调教视频在线观看 | 波多野结衣 黑人 | 欧美freesex黑人又粗又大 | 中国女人内谢69xxxxxa片 | 欧美激情内射喷水高潮 | 国产精品99爱免费视频 | 丰满少妇弄高潮了www | 爆乳一区二区三区无码 | 久久精品女人天堂av免费观看 | 亚洲欧洲无卡二区视頻 | 四虎永久在线精品免费网址 | 两性色午夜视频免费播放 | 亚洲国产av精品一区二区蜜芽 | 亚洲精品无码人妻无码 | 中文字幕日产无线码一区 | 又色又爽又黄的美女裸体网站 | 亚洲午夜无码久久 | 一二三四社区在线中文视频 | 无码一区二区三区在线观看 | 又色又爽又黄的美女裸体网站 | 国产偷抇久久精品a片69 | 久久成人a毛片免费观看网站 | 国产精品久久久午夜夜伦鲁鲁 | 日本护士xxxxhd少妇 | 久久99精品久久久久久动态图 | 永久免费精品精品永久-夜色 | av无码不卡在线观看免费 | 欧美熟妇另类久久久久久多毛 | 久久99精品国产.久久久久 | 国产又粗又硬又大爽黄老大爷视 | 国精产品一品二品国精品69xx | 97夜夜澡人人爽人人喊中国片 | 国产亚洲视频中文字幕97精品 | 亚洲中文字幕在线无码一区二区 | 亚洲成在人网站无码天堂 | 大色综合色综合网站 | 性欧美熟妇videofreesex | 国产精品亚洲lv粉色 | 最近的中文字幕在线看视频 | 九一九色国产 | 东北女人啪啪对白 | 牛和人交xxxx欧美 | 亚洲中文字幕在线无码一区二区 | 无套内射视频囯产 | 亚洲综合伊人久久大杳蕉 | 国产成人午夜福利在线播放 | 性欧美牲交在线视频 | 日本精品人妻无码77777 天堂一区人妻无码 | 东京无码熟妇人妻av在线网址 | 国产9 9在线 | 中文 | 精品欧洲av无码一区二区三区 | 国产av剧情md精品麻豆 | av香港经典三级级 在线 | 国产香蕉97碰碰久久人人 | 国产精品久久国产精品99 | 性啪啪chinese东北女人 | 性啪啪chinese东北女人 | 日欧一片内射va在线影院 | 麻豆果冻传媒2021精品传媒一区下载 | 桃花色综合影院 | 啦啦啦www在线观看免费视频 | 在线欧美精品一区二区三区 | 在线亚洲高清揄拍自拍一品区 | 伦伦影院午夜理论片 | 国产乱人偷精品人妻a片 | 噜噜噜亚洲色成人网站 | 亚洲精品一区二区三区婷婷月 | 久久精品人妻少妇一区二区三区 | av香港经典三级级 在线 | 久久精品女人的天堂av | 成熟女人特级毛片www免费 | 久久精品人妻少妇一区二区三区 | 欧美三级不卡在线观看 | 超碰97人人做人人爱少妇 | 久久综合狠狠综合久久综合88 | 亚洲熟女一区二区三区 | 永久免费观看国产裸体美女 | 99精品视频在线观看免费 | √天堂资源地址中文在线 | 亚洲日韩一区二区 | 国产深夜福利视频在线 | 久久综合狠狠综合久久综合88 | 亚洲小说春色综合另类 | 强开小婷嫩苞又嫩又紧视频 | 男女猛烈xx00免费视频试看 | 欧美日韩视频无码一区二区三 | 狂野欧美性猛xxxx乱大交 | 人妻少妇精品无码专区二区 | 蜜桃臀无码内射一区二区三区 | 国内老熟妇对白xxxxhd | 岛国片人妻三上悠亚 | av人摸人人人澡人人超碰下载 | 久久久中文字幕日本无吗 | 国产九九九九九九九a片 | 又大又黄又粗又爽的免费视频 | 精品一二三区久久aaa片 | 日本www一道久久久免费榴莲 | 少妇性荡欲午夜性开放视频剧场 | 中文精品久久久久人妻不卡 | 免费国产成人高清在线观看网站 | 免费看男女做好爽好硬视频 | 久精品国产欧美亚洲色aⅴ大片 | 国产黄在线观看免费观看不卡 | 高清国产亚洲精品自在久久 | 欧洲美熟女乱又伦 | 亚洲中文字幕va福利 | 久久人人97超碰a片精品 | 成熟女人特级毛片www免费 | 久久精品国产精品国产精品污 | 国产偷自视频区视频 | 黄网在线观看免费网站 | 精品无码一区二区三区爱欲 | 亚洲国产精品成人久久蜜臀 | 初尝人妻少妇中文字幕 | 欧美国产日韩亚洲中文 | 亚洲高清偷拍一区二区三区 | 最新国产麻豆aⅴ精品无码 | 男女猛烈xx00免费视频试看 | 久久久婷婷五月亚洲97号色 | 四虎4hu永久免费 | 中文字幕乱妇无码av在线 | 国产农村妇女高潮大叫 | 欧美 丝袜 自拍 制服 另类 | 国产乡下妇女做爰 | 又大又紧又粉嫩18p少妇 | 成人欧美一区二区三区黑人 | 欧美激情一区二区三区成人 | 亚洲国产精品毛片av不卡在线 | 亚洲成a人片在线观看日本 | 图片区 小说区 区 亚洲五月 | 亚洲の无码国产の无码步美 | 成人毛片一区二区 | 免费乱码人妻系列无码专区 | 蜜臀aⅴ国产精品久久久国产老师 | 日韩精品一区二区av在线 | 欧美国产日韩久久mv | 67194成是人免费无码 | 又粗又大又硬毛片免费看 | 久久久久亚洲精品男人的天堂 | 亚洲狠狠婷婷综合久久 | 国产精品18久久久久久麻辣 | 色偷偷av老熟女 久久精品人妻少妇一区二区三区 | 国内丰满熟女出轨videos | 亚洲爆乳精品无码一区二区三区 | 亚洲国产一区二区三区在线观看 | 精品人人妻人人澡人人爽人人 | 亚洲精品一区二区三区四区五区 | 熟妇女人妻丰满少妇中文字幕 | 国产精品久久久久久久影院 | 亚洲日本va中文字幕 | 天天拍夜夜添久久精品 | 中文亚洲成a人片在线观看 | 两性色午夜视频免费播放 | 在线观看国产午夜福利片 | 国产69精品久久久久app下载 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 最新国产麻豆aⅴ精品无码 | 熟妇人妻无乱码中文字幕 | 亚洲男女内射在线播放 | 国产极品美女高潮无套在线观看 | 少妇邻居内射在线 | 99精品视频在线观看免费 | 性生交片免费无码看人 | 国产真实夫妇视频 | 亚洲国产高清在线观看视频 | 婷婷丁香五月天综合东京热 | 日本又色又爽又黄的a片18禁 | 亚洲人成人无码网www国产 | 狂野欧美激情性xxxx | 久久久久99精品成人片 | 国产成人综合美国十次 | 国产在热线精品视频 | 欧美高清在线精品一区 | 久久久久国色av免费观看性色 | 女高中生第一次破苞av | 国产精品香蕉在线观看 | 国产人妻久久精品二区三区老狼 | 高潮喷水的毛片 | 人人妻人人澡人人爽人人精品浪潮 | 国产网红无码精品视频 | 亚洲国产高清在线观看视频 | 欧美怡红院免费全部视频 | 一本久久a久久精品亚洲 | 久久 国产 尿 小便 嘘嘘 | 波多野结衣aⅴ在线 | 欧美日韩一区二区免费视频 | a在线观看免费网站大全 | 午夜丰满少妇性开放视频 | www国产精品内射老师 | 久久久久免费精品国产 | 亚洲国产精品久久人人爱 | 精品偷拍一区二区三区在线看 | 久久久久国色av免费观看性色 | 玩弄人妻少妇500系列视频 | 成人无码精品1区2区3区免费看 | 丁香花在线影院观看在线播放 | 真人与拘做受免费视频一 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 国产欧美亚洲精品a | 日韩精品无码一本二本三本色 | 成人av无码一区二区三区 | 蜜臀aⅴ国产精品久久久国产老师 | 国产乱子伦视频在线播放 | 欧美35页视频在线观看 | 人妻少妇精品久久 | 奇米影视7777久久精品 | 中文字幕无码乱人伦 | 精品无码一区二区三区的天堂 | 无码人妻精品一区二区三区不卡 | 色婷婷久久一区二区三区麻豆 | 夜夜高潮次次欢爽av女 | 鲁鲁鲁爽爽爽在线视频观看 | 欧美乱妇无乱码大黄a片 | 偷窥村妇洗澡毛毛多 | 无码国产激情在线观看 | 黑人巨大精品欧美黑寡妇 | 久久亚洲精品中文字幕无男同 | 免费视频欧美无人区码 | 成人无码影片精品久久久 | 精品人妻人人做人人爽 | 性色欲情网站iwww九文堂 | 中文精品久久久久人妻不卡 | 成人精品视频一区二区 | 亚洲欧美国产精品专区久久 | 狠狠噜狠狠狠狠丁香五月 | 麻豆成人精品国产免费 | 中文精品久久久久人妻不卡 | 亚洲一区二区三区 | 亚洲成色www久久网站 | 亚洲 欧美 激情 小说 另类 | 国产精品爱久久久久久久 | 久青草影院在线观看国产 | 国产精品久久久久影院嫩草 | 性生交大片免费看l | 人人爽人人爽人人片av亚洲 | 300部国产真实乱 | 人妻无码αv中文字幕久久琪琪布 | 亚洲色成人中文字幕网站 | 丰满肥臀大屁股熟妇激情视频 | 99国产欧美久久久精品 | 波多野结衣av一区二区全免费观看 | 午夜精品久久久内射近拍高清 | 天天拍夜夜添久久精品大 | 日本精品久久久久中文字幕 | 成人亚洲精品久久久久 | 日韩亚洲欧美中文高清在线 | 欧美色就是色 | 97se亚洲精品一区 | 精品午夜福利在线观看 | 亚洲精品国偷拍自产在线观看蜜桃 | 欧美三级a做爰在线观看 | 2020久久香蕉国产线看观看 | 国产午夜亚洲精品不卡 | 日本护士xxxxhd少妇 | 国产精品亚洲lv粉色 | 一本大道久久东京热无码av | 国产一区二区三区四区五区加勒比 | 三上悠亚人妻中文字幕在线 | 久久精品国产99久久6动漫 | 国产香蕉97碰碰久久人人 | 大肉大捧一进一出视频出来呀 | 中文字幕乱码亚洲无线三区 | 成人aaa片一区国产精品 | 亚洲另类伦春色综合小说 | 六十路熟妇乱子伦 | 四虎国产精品一区二区 | 亚洲成a人一区二区三区 | 国产精品亚洲lv粉色 | 在线看片无码永久免费视频 | 欧美丰满老熟妇xxxxx性 | 色 综合 欧美 亚洲 国产 | 久久久久久av无码免费看大片 | 纯爱无遮挡h肉动漫在线播放 | 天堂亚洲2017在线观看 | 丰满少妇人妻久久久久久 | 狠狠色欧美亚洲狠狠色www | 久久综合给久久狠狠97色 | 男女下面进入的视频免费午夜 | 77777熟女视频在线观看 а天堂中文在线官网 | 久久久久久久久888 | 亚洲国产综合无码一区 | 人妻插b视频一区二区三区 | 日本成熟视频免费视频 | 男人扒开女人内裤强吻桶进去 | 国产黄在线观看免费观看不卡 | 国产av一区二区精品久久凹凸 | 最新国产乱人伦偷精品免费网站 | 国产在线一区二区三区四区五区 | 少妇高潮喷潮久久久影院 | 中文字幕av伊人av无码av | 成 人 免费观看网站 | 无码中文字幕色专区 | 国内老熟妇对白xxxxhd | 国模大胆一区二区三区 | 女人高潮内射99精品 | 国产真人无遮挡作爱免费视频 | 美女极度色诱视频国产 | 国产综合在线观看 | 麻豆果冻传媒2021精品传媒一区下载 | 性欧美videos高清精品 | 国内精品久久毛片一区二区 | 欧美一区二区三区视频在线观看 | 日韩在线不卡免费视频一区 | 麻豆md0077饥渴少妇 | а√天堂www在线天堂小说 | 国产三级久久久精品麻豆三级 | 国产精品亚洲lv粉色 | 性欧美疯狂xxxxbbbb | 欧美国产日产一区二区 | www成人国产高清内射 | 国产精品亚洲综合色区韩国 | 亚洲日韩av一区二区三区四区 | 一个人看的视频www在线 | 娇妻被黑人粗大高潮白浆 | 无码成人精品区在线观看 | 色偷偷人人澡人人爽人人模 | 欧美日韩色另类综合 | 亚洲色大成网站www | 香港三级日本三级妇三级 | 一本色道婷婷久久欧美 | 美女极度色诱视频国产 | 亚洲午夜无码久久 | 亚洲成a人一区二区三区 | 欧美国产日韩久久mv | 亚洲无人区一区二区三区 | 强辱丰满人妻hd中文字幕 | 亚洲s码欧洲m码国产av | 亚洲中文字幕va福利 | 无码一区二区三区在线观看 | 日韩精品a片一区二区三区妖精 | 国产av一区二区精品久久凹凸 | 中文字幕 亚洲精品 第1页 | 亚洲理论电影在线观看 | 99国产欧美久久久精品 | 国产热a欧美热a在线视频 | 中国大陆精品视频xxxx | 夜夜躁日日躁狠狠久久av | 欧美日韩色另类综合 | 小sao货水好多真紧h无码视频 | 午夜无码区在线观看 | 国产精品第一国产精品 | 麻豆国产人妻欲求不满谁演的 | aⅴ亚洲 日韩 色 图网站 播放 | 欧美 日韩 亚洲 在线 | 天堂无码人妻精品一区二区三区 | 国产成人无码专区 | 成人毛片一区二区 | 夜精品a片一区二区三区无码白浆 | 一本加勒比波多野结衣 | 国内精品人妻无码久久久影院蜜桃 | 丰满人妻翻云覆雨呻吟视频 | 久久久久亚洲精品中文字幕 | 久久久国产精品无码免费专区 | 成人无码精品一区二区三区 | 成熟女人特级毛片www免费 | 国产免费久久久久久无码 | 国产精品第一国产精品 | 一本精品99久久精品77 | 亚洲自偷精品视频自拍 | 欧美丰满老熟妇xxxxx性 | 中文字幕乱码人妻二区三区 | 无码人妻出轨黑人中文字幕 | 亚洲精品一区国产 | 欧美xxxx黑人又粗又长 | 亚洲人成网站色7799 | 日产精品高潮呻吟av久久 | 久久婷婷五月综合色国产香蕉 | 久久综合九色综合97网 | 少妇久久久久久人妻无码 | 牛和人交xxxx欧美 | 国产av一区二区三区最新精品 | 成熟女人特级毛片www免费 | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 窝窝午夜理论片影院 | 欧美黑人巨大xxxxx | 成人三级无码视频在线观看 | 99久久婷婷国产综合精品青草免费 | 久久综合激激的五月天 | 欧洲vodafone精品性 | 国产精品沙发午睡系列 | 久久人人爽人人爽人人片av高清 | 成人动漫在线观看 | 亚洲精品无码人妻无码 | 天堂а√在线地址中文在线 | 国产麻豆精品一区二区三区v视界 | 又粗又大又硬又长又爽 | 波多野结衣aⅴ在线 | 中文字幕乱妇无码av在线 | 国产av一区二区精品久久凹凸 | 国产精品内射视频免费 | 青青久在线视频免费观看 | 乱码av麻豆丝袜熟女系列 | 色 综合 欧美 亚洲 国产 | 国产人妻精品午夜福利免费 | 亚洲gv猛男gv无码男同 | 77777熟女视频在线观看 а天堂中文在线官网 | 亚洲日本va午夜在线电影 | 亚洲精品一区二区三区在线观看 | 女人被男人躁得好爽免费视频 | 国産精品久久久久久久 | 日日噜噜噜噜夜夜爽亚洲精品 | 高清不卡一区二区三区 | 亚洲国产精品美女久久久久 | 亚洲精品成人福利网站 | 99久久99久久免费精品蜜桃 | 久久综合香蕉国产蜜臀av | 日产精品高潮呻吟av久久 | 久久国产劲爆∧v内射 | 精品aⅴ一区二区三区 | 中文字幕人成乱码熟女app | 国产精品高潮呻吟av久久 | 亚洲中文字幕久久无码 | 成人无码视频免费播放 | 最新国产乱人伦偷精品免费网站 | 国产精品亚洲专区无码不卡 | 玩弄中年熟妇正在播放 | 成人av无码一区二区三区 | 日韩成人一区二区三区在线观看 | 日韩精品a片一区二区三区妖精 | 国产精品久久久一区二区三区 | 欧美性生交活xxxxxdddd | 伊人久久大香线焦av综合影院 | 丰满人妻一区二区三区免费视频 | 色婷婷久久一区二区三区麻豆 | 牲交欧美兽交欧美 | √8天堂资源地址中文在线 | 夜夜夜高潮夜夜爽夜夜爰爰 | 玩弄人妻少妇500系列视频 | 日日摸夜夜摸狠狠摸婷婷 | 中国女人内谢69xxxx | 成人精品视频一区二区 | 国产亚av手机在线观看 | 国产精品无码成人午夜电影 | 无码人妻出轨黑人中文字幕 | 少妇高潮一区二区三区99 | 中文字幕av伊人av无码av | 亚洲熟妇色xxxxx欧美老妇y | 丰满人妻一区二区三区免费视频 | 性啪啪chinese东北女人 | 国产午夜亚洲精品不卡下载 | 内射老妇bbwx0c0ck | 国产熟妇另类久久久久 | 人妻无码αv中文字幕久久琪琪布 | 中文字幕乱码亚洲无线三区 | 精品国产青草久久久久福利 | 人妻少妇被猛烈进入中文字幕 | 欧美亚洲日韩国产人成在线播放 | 亚洲国产欧美在线成人 | 97色伦图片97综合影院 | 亚洲一区二区三区四区 | 亚洲码国产精品高潮在线 | 国产亚洲精品久久久久久 | 亚洲va中文字幕无码久久不卡 | 亚洲色成人中文字幕网站 | 精品日本一区二区三区在线观看 | 成熟人妻av无码专区 | 中文字幕色婷婷在线视频 | 欧美日韩综合一区二区三区 | 午夜无码区在线观看 | 国产无遮挡又黄又爽免费视频 | 国产艳妇av在线观看果冻传媒 | 亚洲精品午夜国产va久久成人 | 亚洲国产精品久久久久久 | 亚洲国产精品久久久天堂 | 精品厕所偷拍各类美女tp嘘嘘 | 88国产精品欧美一区二区三区 | 国产精品久久久久影院嫩草 | 亚洲a无码综合a国产av中文 | 亚洲色www成人永久网址 | 成年美女黄网站色大免费全看 | 妺妺窝人体色www婷婷 | 亚洲欧洲无卡二区视頻 | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 欧美性色19p | 免费无码午夜福利片69 | 少妇被黑人到高潮喷出白浆 | 久久zyz资源站无码中文动漫 | 成人免费视频视频在线观看 免费 | 人妻少妇精品无码专区动漫 | 无码播放一区二区三区 | 四虎国产精品免费久久 | 久久亚洲日韩精品一区二区三区 | 国产成人av免费观看 | 领导边摸边吃奶边做爽在线观看 | 少妇人妻偷人精品无码视频 | 97人妻精品一区二区三区 | av无码电影一区二区三区 | 亚洲中文字幕乱码av波多ji | 图片小说视频一区二区 | 99久久久无码国产精品免费 | 偷窥日本少妇撒尿chinese | 精品国产乱码久久久久乱码 | 性欧美大战久久久久久久 | 亚洲色www成人永久网址 | 无遮挡国产高潮视频免费观看 | 久久人人97超碰a片精品 | 精品欧美一区二区三区久久久 | 国内精品人妻无码久久久影院 | 强开小婷嫩苞又嫩又紧视频 | 牛和人交xxxx欧美 | 特级做a爰片毛片免费69 | 欧美激情综合亚洲一二区 | 久9re热视频这里只有精品 | 男女作爱免费网站 | 亚洲一区av无码专区在线观看 | 亚洲国产精华液网站w | 天海翼激烈高潮到腰振不止 | 国产无遮挡又黄又爽免费视频 | 色诱久久久久综合网ywww | 午夜无码人妻av大片色欲 | 欧美丰满熟妇xxxx性ppx人交 | 人妻少妇精品无码专区动漫 | 丰满人妻精品国产99aⅴ | 国色天香社区在线视频 | 天天做天天爱天天爽综合网 | 美女张开腿让人桶 | 午夜福利试看120秒体验区 | 国产精品亚洲专区无码不卡 | 婷婷丁香五月天综合东京热 | 国产精品无码成人午夜电影 | 夫妻免费无码v看片 | 18无码粉嫩小泬无套在线观看 | 女人高潮内射99精品 | 四虎影视成人永久免费观看视频 | 性生交片免费无码看人 | 人妻少妇被猛烈进入中文字幕 | 精品欧洲av无码一区二区三区 | 在线 国产 欧美 亚洲 天堂 | 日本熟妇浓毛 | 欧美人与禽猛交狂配 | 六月丁香婷婷色狠狠久久 | 色综合久久久无码网中文 | 日本一卡2卡3卡四卡精品网站 | 一个人免费观看的www视频 | 亚洲色欲色欲天天天www | 亚洲另类伦春色综合小说 | 色窝窝无码一区二区三区色欲 | 18黄暴禁片在线观看 | 亚洲小说春色综合另类 | 国产特级毛片aaaaaaa高清 | 亚洲欧美色中文字幕在线 | 国产精品资源一区二区 | 亚洲乱码日产精品bd | 亚洲中文无码av永久不收费 | 1000部啪啪未满十八勿入下载 | 国内精品人妻无码久久久影院蜜桃 | 狠狠色色综合网站 | 亚洲va欧美va天堂v国产综合 | 久久综合网欧美色妞网 | 国产亚洲欧美在线专区 | 巨爆乳无码视频在线观看 | 亚洲色欲久久久综合网东京热 | 强伦人妻一区二区三区视频18 | 国色天香社区在线视频 | 日韩少妇内射免费播放 | 久久五月精品中文字幕 | 成人精品一区二区三区中文字幕 | 日本熟妇大屁股人妻 | 中文字幕日产无线码一区 | 精品欧洲av无码一区二区三区 | 国色天香社区在线视频 | www国产精品内射老师 | 又大又硬又爽免费视频 | 亚洲中文字幕乱码av波多ji | 四虎国产精品免费久久 | 欧美xxxxx精品 | 国产av无码专区亚洲awww | 中文字幕无码免费久久99 | аⅴ资源天堂资源库在线 | 老熟女重囗味hdxx69 | 成人精品视频一区二区三区尤物 | 乱码av麻豆丝袜熟女系列 | 国产精品无码永久免费888 | 成人亚洲精品久久久久软件 | 伊人久久大香线焦av综合影院 | 亚洲第一网站男人都懂 | 亚洲欧美国产精品久久 | 麻豆精品国产精华精华液好用吗 | 久久97精品久久久久久久不卡 | 扒开双腿疯狂进出爽爽爽视频 | 又粗又大又硬毛片免费看 | 骚片av蜜桃精品一区 | 国产精品亚洲专区无码不卡 | 小泽玛莉亚一区二区视频在线 | 18无码粉嫩小泬无套在线观看 | 中文字幕人妻无码一夲道 | 午夜成人1000部免费视频 | √8天堂资源地址中文在线 | 中文字幕精品av一区二区五区 | 国产精品美女久久久 | 少妇无码一区二区二三区 | 国产偷自视频区视频 | 午夜无码人妻av大片色欲 | 精品久久久久久人妻无码中文字幕 | 丰满肥臀大屁股熟妇激情视频 | 亚洲成av人影院在线观看 | 纯爱无遮挡h肉动漫在线播放 | 狂野欧美激情性xxxx | 亚洲欧美精品伊人久久 | 亚洲一区二区三区四区 | 人人妻人人藻人人爽欧美一区 | 国产舌乚八伦偷品w中 | 麻豆成人精品国产免费 | 亚洲国产精品一区二区美利坚 | 麻豆国产丝袜白领秘书在线观看 | 日本又色又爽又黄的a片18禁 | 日欧一片内射va在线影院 | a在线观看免费网站大全 | www国产亚洲精品久久网站 | 人妻互换免费中文字幕 | 一本色道久久综合亚洲精品不卡 | 国产乱码精品一品二品 | 国产 浪潮av性色四虎 | 国产精品久久久 | 丰腴饱满的极品熟妇 | 亚洲色无码一区二区三区 | 国产精品18久久久久久麻辣 | 婷婷五月综合激情中文字幕 | 成人一区二区免费视频 | 国产无遮挡又黄又爽免费视频 | 久久久久久久女国产乱让韩 | 亚洲成在人网站无码天堂 | 亚洲国产精品无码久久久久高潮 | 精品国产一区二区三区四区在线看 | 久久亚洲国产成人精品性色 | 国产精品久久久久久亚洲毛片 | 综合激情五月综合激情五月激情1 | 波多野结衣高清一区二区三区 | 熟妇人妻无码xxx视频 | 中文字幕无码乱人伦 | 波多野结衣aⅴ在线 | 两性色午夜视频免费播放 | 色综合久久久久综合一本到桃花网 | 草草网站影院白丝内射 | 成人av无码一区二区三区 | 精品人妻中文字幕有码在线 | 亚洲va中文字幕无码久久不卡 | 日韩欧美中文字幕在线三区 | 国产手机在线αⅴ片无码观看 | 免费无码一区二区三区蜜桃大 | 日韩人妻无码一区二区三区久久99 | 男女超爽视频免费播放 | 暴力强奷在线播放无码 | 亚洲热妇无码av在线播放 | 蜜桃臀无码内射一区二区三区 | 午夜男女很黄的视频 | 亚洲男女内射在线播放 | 欧美激情综合亚洲一二区 | 18禁止看的免费污网站 | 欧美freesex黑人又粗又大 | 国产真实乱对白精彩久久 | 国产av久久久久精东av | 亚洲中文字幕在线观看 | 精品夜夜澡人妻无码av蜜桃 | 国产精品第一区揄拍无码 | 真人与拘做受免费视频 | 国产另类ts人妖一区二区 | 国产国产精品人在线视 | 久久久精品人妻久久影视 | 亚洲 激情 小说 另类 欧美 | 国产特级毛片aaaaaa高潮流水 | 中文无码精品a∨在线观看不卡 | 欧美野外疯狂做受xxxx高潮 | 日韩少妇内射免费播放 | 99久久人妻精品免费一区 | 亚洲精品一区三区三区在线观看 | 国内综合精品午夜久久资源 | 国内精品人妻无码久久久影院 | 日产精品99久久久久久 | 日日鲁鲁鲁夜夜爽爽狠狠 | 成人精品天堂一区二区三区 | 秋霞特色aa大片 | 亚洲日韩精品欧美一区二区 | 麻豆精品国产精华精华液好用吗 | 欧美一区二区三区视频在线观看 | 国产亚洲精品精品国产亚洲综合 | 国产麻豆精品一区二区三区v视界 | 亚洲 另类 在线 欧美 制服 | 日日天日日夜日日摸 | 正在播放东北夫妻内射 | 丁香啪啪综合成人亚洲 | 欧美肥老太牲交大战 | 国产莉萝无码av在线播放 | 老司机亚洲精品影院 | 牲欲强的熟妇农村老妇女视频 | 欧美性生交活xxxxxdddd | 无码精品国产va在线观看dvd | 偷窥日本少妇撒尿chinese | 国产成人无码av一区二区 | 日日碰狠狠躁久久躁蜜桃 | 成 人影片 免费观看 | yw尤物av无码国产在线观看 | 国产精品办公室沙发 | 高潮毛片无遮挡高清免费 | 久久国产劲爆∧v内射 | 久久人妻内射无码一区三区 | 久久精品成人欧美大片 | 老子影院午夜精品无码 | 国产特级毛片aaaaaaa高清 | 夜先锋av资源网站 | 国产人妻精品午夜福利免费 | 午夜精品久久久久久久 | 亚洲s码欧洲m码国产av | 成人免费视频在线观看 | 久久久精品成人免费观看 | 欧美兽交xxxx×视频 | 国产色xx群视频射精 | 日韩精品无码一本二本三本色 | 久久人人爽人人人人片 | 国产激情艳情在线看视频 | 国产综合色产在线精品 | 77777熟女视频在线观看 а天堂中文在线官网 | 国产精品久久久久9999小说 | 精品国产一区二区三区四区 | 亚洲一区二区三区国产精华液 | 久久精品成人欧美大片 | 亚洲国产精品一区二区第一页 | 欧美性色19p | 国产真实乱对白精彩久久 | 亚洲啪av永久无码精品放毛片 | 天干天干啦夜天干天2017 | 国产无遮挡吃胸膜奶免费看 | 亚洲成在人网站无码天堂 | 国产精品嫩草久久久久 | 国产香蕉尹人综合在线观看 | 精品国精品国产自在久国产87 | 国产情侣作爱视频免费观看 | 国产绳艺sm调教室论坛 | 狠狠色色综合网站 | 乱中年女人伦av三区 | 亚洲中文字幕在线无码一区二区 | 亚洲人成网站在线播放942 | 亚洲成熟女人毛毛耸耸多 | 99久久精品无码一区二区毛片 | 性色欲情网站iwww九文堂 | 亚洲精品国产第一综合99久久 | 一本色道久久综合亚洲精品不卡 | 久久精品国产一区二区三区肥胖 | 欧美日韩一区二区综合 | 欧美日韩视频无码一区二区三 | 亚洲欧美国产精品专区久久 | 国产精品成人av在线观看 | 国产又爽又猛又粗的视频a片 | 日本精品少妇一区二区三区 | 无码一区二区三区在线观看 | 国产人妻人伦精品 | 精品熟女少妇av免费观看 | 国产一区二区三区精品视频 | 欧美刺激性大交 | 天天躁夜夜躁狠狠是什么心态 | 欧美肥老太牲交大战 | 一本久道高清无码视频 | 欧美大屁股xxxxhd黑色 | 日韩精品无码免费一区二区三区 | 成年女人永久免费看片 | 久久成人a毛片免费观看网站 | 亚洲人成影院在线观看 | aa片在线观看视频在线播放 | 国产婷婷色一区二区三区在线 | 成人性做爰aaa片免费看不忠 | 鲁鲁鲁爽爽爽在线视频观看 | 国内老熟妇对白xxxxhd | 午夜福利不卡在线视频 | 日本一卡2卡3卡四卡精品网站 | 国产免费久久久久久无码 | 日欧一片内射va在线影院 | 亚洲爆乳大丰满无码专区 | 欧美丰满熟妇xxxx性ppx人交 | 国产精品美女久久久网av | 图片区 小说区 区 亚洲五月 | 国产精品成人av在线观看 | 国产在线aaa片一区二区99 | av无码久久久久不卡免费网站 | 人妻中文无码久热丝袜 | 宝宝好涨水快流出来免费视频 | 2019nv天堂香蕉在线观看 | 中文字幕av伊人av无码av | 麻豆成人精品国产免费 | 欧美精品无码一区二区三区 | 欧美一区二区三区 | 国产精品美女久久久网av | 99久久久无码国产精品免费 | 欧美激情一区二区三区成人 | 日韩视频 中文字幕 视频一区 | 亚洲阿v天堂在线 | 亚洲精品成a人在线观看 | 俄罗斯老熟妇色xxxx | 内射爽无广熟女亚洲 | 久久精品国产一区二区三区肥胖 | 极品尤物被啪到呻吟喷水 | 国产精品资源一区二区 | 亚洲欧美中文字幕5发布 | 免费无码午夜福利片69 | 无码一区二区三区在线 | 久精品国产欧美亚洲色aⅴ大片 | 初尝人妻少妇中文字幕 | 精品无码一区二区三区的天堂 |