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 coroutineskotlin-coroutines-retrofitPerpanjangan 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 SISAImplementasi 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>> } }
LivedataSelanjutnya, 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 pembungkusUntuk 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;
ViewModelStoresViewModel 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) } }
ViewModelProvidersUntuk 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) } } }
FragmenMendapatkan 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)
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'
TautanSemua dalam satu contoh.Sampel Komponen Arsitektur AndroidIkhtisar LiveDataKode Async menggunakan Kotlin CoroutinesMultithreading dan kotlin