تتحدث هذه المقالة عن استخدام
مكونات 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;
نموذج العرضتطلب 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)
ملاحظة
لتخزين 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 Coroutinesmultithreading و kotlin