Android LiveData di Kotlin menggunakan Retrofit dan coroutine

Artikel ini berbicara tentang penggunaan Komponen Android ViewModel, LifeCycle, dan LiveData. Komponen-komponen ini memungkinkan Anda untuk tidak peduli dengan siklus hidup Aktivitas.

Contoh penggunaan Coroutine modern dalam hubungannya dengan repositori Retrofit juga dipertimbangkan.

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) } } 

Perpanjangan retrofit coroutines

kotlin-coroutines-retrofit
Perpanjangan untuk Retrofit di Kotlin. Ini hanya dua file. Saya baru saja menambahkannya ke proyek. Anda dapat menghubungkan mereka melalui Dependency in Gradle. Ada contoh penggunaan di Github.
Kami juga menghubungkan Adapter addCallAdapterFactory (CoroutineCallAdapterFactory ()) .
ServerAPI dan Repositori berada di file yang sama

API SISA

Implementasi REST API di Kotlin. Dia tidak memiliki perubahan spesifik.

ServerAPI
 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

Selanjutnya, pertimbangkan Repositori. Ini adalah layanan utama untuk menerima LiveData. Kami menginisialisasi LiveData dengan status muat: Resource.loading (null) . Selanjutnya, tunggu sampai akhir permintaan waitResult () Panggilan ini harus di blok Coroutin async (UI)

Di akhir permintaan, kami dapat menangani hasilnya. Jika semuanya baik-baik saja, hasilnya akan disimpan dalam mutableLiveData.value = Resource.success (result.value) Poin penting adalah bahwa harus ada tautan ke instance baru, jika tidak, pengamat LiveData tidak akan berfungsi. lihat: Sumber Daya baru <> (SUKSES, data, null);

Repositori
  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 } } 


Data pembungkus

Untuk penanganan kesalahan dan transfer status ke Fragment, Wrapper - Resource <T> digunakan .

Ini menyimpan tiga negara:

 public enum Status { SUCCESS, ERROR, LOADING } 

Data itu sendiri:

 @Nullable public final T data; 

Sumberdaya <T>
Kelas generik yang berisi data dan status tentang memuat data ini

 // A generic class that contains data and status about loading this data. public class Resource<T> { @NonNull public final Status status; @Nullable public final T data; @Nullable public final String message; private Resource(@NonNull Status status, @Nullable T data, @Nullable String message) { this.status = status; this.data = data; this.message = message; } public static <T> Resource<T> success(@NonNull T data) { return new Resource<>(Status.SUCCESS, data, null); } public static <T> Resource<T> error(String msg, @Nullable T data) { return new Resource<>(Status.ERROR, data, msg); } public static <T> Resource<T> loading(@Nullable T data) { return new Resource<>(Status.LOADING, data, null); } public enum Status { SUCCESS, ERROR, LOADING } } 


ViewModel

StoresViewModel meminta data dari repositori dan menyimpannya dalam variabel toko

 val api = Repository() stores = api.getStores(token) 

ViewModel
 class StoresViewModel (context: Context, token: String) : ViewModel() { val stores: MutableLiveData<Resource<Array<Store>>> init { val api = Repository() stores = api.getStores(token) } } 


ViewModelProviders

Untuk meneruskan parameter ke ViewModel, kami memperluas ViewModelProviders standar
Misalnya, untuk meneruskan ke LoginViewModel Anda memerlukan dua parameter (Login, Kata Sandi). Untuk mentransfer token ke StoresViewModel, satu (Token) digunakan

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) } } } 


Fragmen

Mendapatkan StoresViewModel:

 viewModel = ViewModelProviders.of(this, AppViewModelFactory(requireActivity(), tokenHolder.token)).get(StoresViewModel::class.java) 

Menggunakan Observer Observer untuk Perubahan Data:

  // Observe data on the ViewModel, exposed as a LiveData viewModel.stores.observe(this, Observer<Resource<Array<Store>>> { storesResource -> 

Fragmen
  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) // Observe data on the ViewModel, exposed as a LiveData viewModel.stores.observe(this, Observer<Resource<Array<Store>>> { storesResource -> val stores = storesResource?.data stores?.let { viewAdapter = StoresAdapter(stores!!) recyclerView.adapter = viewAdapter } if (storesResource?.status == Resource.LOADING){ log("Loading...") } if (storesResource?.status == Resource.ERROR){ log("Error : " + storesResource?.message) } }) } 


PS

Untuk menyimpan Token dan menggunakannya di seluruh aplikasi, saya menggunakan perpustakaan / ekstensi dari Fabio Collini. Aplikasi ini dijelaskan dengan baik dalam artikelnya. Tautan ada di halaman di Github atau di bawah dalam artikel ini.

delegasi prefs oleh 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++ } } 

Gradle

  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' 

Tautan

Semua dalam satu contoh.

Sampel Komponen Arsitektur Android

Ikhtisar LiveData

Kode Async menggunakan Kotlin Coroutines

Multithreading dan kotlin

Source: https://habr.com/ru/post/id427475/


All Articles