Android LiveData على Kotlin باستخدام التحديث التحديثي و coroutines

تتحدث هذه المقالة عن استخدام مكونات Android ViewModel و LifeCycle و LiveData. تسمح لك هذه المكونات بعدم الاهتمام بدورة حياة النشاط.

مثال على استخدام Coroutines الحديثة بالتزامن مع مستودع التحديثية يعتبر أيضا.

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

تمديد Coroutines التحديثية

kotlin-coroutines-التحديثية
ملحق التحديثية على Kotlin. هذه ليست سوى ملفين. لقد أضفتهم للتو إلى المشروع. يمكنك توصيلها من خلال التبعية في Gradle. هناك أمثلة للاستخدام على Github.
نقوم أيضًا بتوصيل المحول addCallAdapterFactory (CoroutineCallAdapterFactory ()) .
ServerAPI و Repository في نفس الملف

REST API

تنفيذ REST API على Kotlin. ليس لديها أي تغييرات محددة.

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

بعد ذلك ، ضع في اعتبارك المستودع. هذه هي الخدمة الرئيسية لاستقبال LiveData. نقوم بتهيئة LiveData بحالة التحميل: Resource.loading (خالية) . بعد ذلك ، انتظر حتى نهاية الطلب بانتظار النتيجة () يجب أن تكون هذه المكالمة في كتلة Coroutin async (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 } } 


بيانات الغلاف

لمعالجة الأخطاء ونقل الحالة إلى جزء ، يتم استخدام Wrapper - Resource <T> .

يخزن ثلاث ولايات:

 public enum Status { SUCCESS, ERROR, LOADING } 

البيانات نفسها:

 @Nullable public final T data; 

المورد <T>
فئة عامة تحتوي على بيانات وحالة حول تحميل هذه البيانات

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


نموذج العرض

تطلب StoresViewModel البيانات من المستودع وتخزنها في متغير المخازن

 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 ، تحتاج إلى معلمتين (تسجيل الدخول وكلمة المرور). لنقل رمز مميز إلى StoresViewModel ، يتم استخدام رمز واحد (Token)

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


جزء

الحصول على المتاجر

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


ملاحظة

لتخزين Token واستخدامه في جميع أنحاء التطبيق ، استخدمت المكتبة / الملحق من Fabio Collini. التطبيق موصوف بشكل جيد في مقاله. الرابط موجود في صفحة على Github أو أدناه في هذه المقالة.

تفضل المندوبين فابيو كوليني

 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 Architecture

نظرة عامة على LiveData

كود غير متزامن باستخدام Kotlin Coroutines

multithreading و kotlin

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


All Articles