本文讨论有关使用
Android组件 ViewModel,LifeCycle和LiveData的问题。 这些组件使您不必关心“活动”生命周期。
还考虑了将现代
协程与Retrofit存储库结合使用的示例。
fun main(args: Array<String>): Unit = runBlocking { // Wait (suspend) for Result val result: Result<User> = api.getUser("username").awaitResult() // Check result type when (result) { //Successful HTTP result is Result.Ok -> saveToDb(result.value) // Any HTTP error is Result.Error -> log("HTTP error with code ${result.error.code()}", result.error) // Exception while request invocation is Result.Exception -> log("Something broken", e) } }
改造协程扩展Kotlin-协程改造Kotlin上的改造扩展。 这些只是两个文件。 我只是将它们添加到项目中。 您可以通过Gradle中的Dependency连接它们。 Github上有用法示例。
我们还连接了适配器
addCallAdapterFactory(CoroutineCallAdapterFactory()) 。
ServerAPI和存储库在同一文件中
REST API在Kotlin上执行REST API。 她没有任何特定的更改。
服务器API import android.arch.lifecycle.MutableLiveData import android.util.Log import com.jakewharton.retrofit2.adapter.kotlin.coroutines.experimental.CoroutineCallAdapterFactory import kotlinx.coroutines.experimental.android.UI import kotlinx.coroutines.experimental.async import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Call import retrofit2.http.* import ru.gildor.coroutines.retrofit.Result import ru.gildor.coroutines.retrofit.awaitResult object ServerAPI { var API_BASE_URL: String = getNetworkHost(); var httpClient = OkHttpClient.Builder().addInterceptor( HttpLoggingInterceptor().apply { level = if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE }) var builder: Retrofit.Builder = Retrofit.Builder() .baseUrl(API_BASE_URL) .addCallAdapterFactory(CoroutineCallAdapterFactory()) .addConverterFactory(GsonConverterFactory.create()) var retrofit = builder .client(httpClient.build()) .build() var netService = retrofit.create<NetService>( NetService::class.java!!) interface NetService { @GET("api/stores") fun getStoreAll(@Header("Authorization") bearer: String): Call<Array<Store>> } }
实时数据接下来,考虑存储库。 这是接收LiveData的主要服务。 我们将LiveData初始化为加载状态:
Resource.loading(null) 。 接下来,等待请求结束
awaitResult()此调用应在Coroutin
异步(UI)块中
在请求结束时,我们可以处理结果。 如果一切顺利,结果将存储在
mutableLiveData.value = Resource.success(result.value)中。重要的一点是,必须存在指向新实例的链接,否则观察者LiveData将无法正常工作。 请参阅:
新资源<>(成功,数据,空);资料库 class Repository { fun getStores(token: String) : MutableLiveData<Resource<Array<Store>>>{ val mutableLiveData = MutableLiveData<Resource<Array<Store>>>() mutableLiveData.value = Resource.loading(null) val req = PostsAPI.netService.getStoreAll(token) try { async(UI) { val result = req.awaitResult() // Check result type when (result) { //Successful HTTP result is Result.Ok -> { mutableLiveData.value = Resource.success(result.value) } // Any HTTP error is Result.Error -> { mutableLiveData.value = Resource.error("Http Error!", null) } // Exception while request invocation is Result.Exception -> Log.d(TAG, result.exception.message) } } } catch (e: Exception) { Log.d(TAG, e.toString()) } return mutableLiveData } }
包装数据对于错误处理和状态到Fragment的传输,使用Wrapper-
Resource <T> 。
它存储三个状态:
public enum Status { SUCCESS, ERROR, LOADING }
数据本身:
@Nullable public final T data;
视图模型StoresViewModel从存储库请求数据并将其存储在
store变量中
val api = Repository() stores = api.getStores(token)
视图模型 class StoresViewModel (context: Context, token: String) : ViewModel() { val stores: MutableLiveData<Resource<Array<Store>>> init { val api = Repository() stores = api.getStores(token) } }
ViewModelProviders为了将参数传递给ViewModel,我们扩展了标准的
ViewModelProviders例如,要传递给
LoginViewModel,您需要两个参数(Login,Password)。 要将令牌传输到
StoresViewModel ,使用一个(令牌)
AppViewModelFactory class AppViewModelFactory(private val contect: Context, vararg params: Any) : ViewModelProvider.NewInstanceFactory() { private val mParams: Array<out Any> init { mParams = params } override fun <T : ViewModel> create(modelClass: Class<T>): T { return if (modelClass == LoginViewModel::class.java) { LoginViewModel(contect, mParams[0] as String, mParams[1] as String) as T } else if (modelClass == StoresViewModel::class.java) { StoresViewModel(contect, mParams[0] as String) as T } else { super.create(modelClass) } } }
碎片获取StoresViewModel:
viewModel = ViewModelProviders.of(this, AppViewModelFactory(requireActivity(), tokenHolder.token)).get(StoresViewModel::class.java)
使用观察者观察者进行数据更改:
// Observe data on the ViewModel, exposed as a LiveData viewModel.stores.observe(this, Observer<Resource<Array<Store>>> { storesResource ->
碎片 override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val view = inflater.inflate(R.layout.stores_fragment, container, false) val tokenHolder = TokenHolder(PreferenceManager.getDefaultSharedPreferences(requireActivity())) viewModel = ViewModelProviders.of(this, AppViewModelFactory(requireActivity(), tokenHolder.token)).get(StoresViewModel::class.java) recyclerView = view.findViewById<RecyclerView>(R.id.store_list).apply { setHasFixedSize(true) } return view } override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState)
聚苯乙烯
为了存储令牌并在整个应用程序中使用它,我使用了Fabio Collini的库/扩展名。 该应用程序在他的文章中有很好的描述。 该链接位于Github上或本文下方的页面上。
法比奥·科里尼(Fabio Collini)的首选代表团 class TokenHolder(prefs: SharedPreferences) { var token by prefs.string() private set var count by prefs.int() private set fun saveToken(newToken: String) { token = newToken count++ } }
摇篮 implementation 'android.arch.lifecycle:extensions:1.1.1' implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:0.30.0" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.30.0" implementation "com.squareup.retrofit2:retrofit:2.4.0" implementation "com.squareup.retrofit2:converter-gson:2.4.0" implementation "com.jakewharton.retrofit:retrofit2-kotlin-coroutines-experimental-adapter:1.0.0" // If you use Kotlin 1.2 or 1.3 // compile 'ru.gildor.coroutines:kotlin-coroutines-retrofit:0.13.0' // compile 'ru.gildor.coroutines:kotlin-coroutines-retrofit:0.13.0-eap13'
友情链接举一个例子。Android体系结构组件示例LiveData概述使用Kotlin协程的异步代码多线程和Kotlin