Two-Way Binding — ikitərəfli məlumat bağlaması, modeli və təqdimatı mobil tətbiqlərdə avtomatik sinxronlaşdıran mexanizm haqqında məlumat əldə edin. findViewById vasitəsilə əl ilə UI yeniləməsindən fərqli olaraq, bağlama mexanizmi həm istifadəçi daxiletməsi dəyişdikdə modeli, həm də məlumat dəyişdikdə təqdimatı yeniləyir. Google I/O 2024 məlumatlarına görə, bağlama Android və iOS layihələrində şablon UI kodunu 30–50% azaldır. Bu yanaşma Jetpack Compose və SwiftUI-dən Flutter və React Native-ə qədər freymvorklarda tətbiq olunur.
Əsas məqamlar
Two-Way Binding (ikitərəfli məlumat bağlaması) — memarlıq mexanizmi olub, məlumat modelindəki dəyişikliklər avtomatik olaraq istifadəçi interfeysində əks olunur və UI-dəki dəyişikliklər dərhal modeli yeniləyir. Məlumat axınının yalnız modeldən təqdimata getdiyi birtərəfli bağlamadan fərqli olaraq, ikitərəfli bağlama hər yeniləməni əl ilə kodlaşdırmadan qapalı sinxronizasiya dövrəsi yaradır.
Android Developers Blog (2023) məlumatlarına görə, 2015-ci ildə təqdim edilmiş DataBinding kitabxanası kommersiya Android tətbiqlərinin 42%-də istifadə olunur. Mexanizm xüsusilə giriş formalarında — mətn sahələri, açarlar, sürgülər və qeyd qutuları — tələb olunur, burada istifadəçi daxiletməsi dərhal modeldə, proqram dəyişiklikləri isə UI-də əks olunmalıdır. Bütün bu ssenarilərdə tərtibatçı bir bağlama yazır, «dinləyici + setter» cütü əvəzinə.
IT Sectr-də 2017-ci ildən layihələrdə ikitərəfli bağlamadan istifadə edirik və onu şüurlu şəkildə tətbiq etməyi tövsiyə edirik: sadə giriş sahələri üçün, lakin asılılıqları olan mürəkkəb vəziyyətlər üçün yox.
Two-Way Binding mexanizmi üç əsas elementə əsaslanır: müşahidə olunan sahə (observable), dəyişiklik dinləyicisi (listener) və əks sinxronizasiya mexanizmi. İstifadəçi EditText sahəsinə mətn daxil etdikdə, sistem TextWatcher hadisəsini ələ keçirir, yeni dəyəri bağlı dəyişənə yazır və dəyişən koddan dəyişibsə, UI-ni yenidən çəkmək zərurəti barədə xəbərdar edir.
DataBinding kitabxanası Android-də kompilyasiya mərhələsində bütün bağlama məntiqini ehtiva edən Binding sinfi yaradır. @={variable} atributu olan hər View üçün inaktivasiya ilə setter + getter cütü yaradılır. SwiftUI-də eyni işi Combine mexanizmi vasitəsilə dəyəri sinxronlaşdıran @Binding propertyWrapper görür. SwiftUI dəyişiklikləri @Published xüsusiyyətləri vasitəsilə izləyir və bağlı dəyişənin hər dəyişməsində View-ni avtomatik yenidən çəkir.
WWDC Session 10033 (2023) məlumatlarına görə, SwiftUI-də @Binding mexanizmi giriş sahələrinin sinxronizasiyasında saniyədə 60 kadr emal edir ki, bu da onu gecikməsiz interaktiv formalar üçün yararlı edir. Hər iki freymvorkda Two-Way Binding Observer nümunəsi üzərində sintaktik şəkərdir, abunəliyi və bildirişi avtomatlaşdırır.
Android-də ikitərəfli bağlama iki variantda mövcuddur: klassik XML-DataBinding @={} atributu vasitəsilə və Jetpack Compose ikitərəfli state-istinadları vasitəsilə. Hər iki yanaşma eyni problemi — UI və modelin sinxronizasiyasını həll edir, lakin sintaksis və tətbiq sahəsinə görə fərqlənir.
XML işarələməsində ikitərəfli bağlama @={variable.property} sintaksisi ilə işarələnir — qıvrım mötərizələr daxilindəki bərabər işarəsi onu birtərəfli @{variable}-dən fərqləndirir. Xüsusi View-lər üçün inverse atributunu göstərən @BindingAdapter annotasiyası tələb olunur.
<layout>
<data>
<variable name="viewModel" type="com.example.LoginViewModel" />
</data>
<EditText
android:text="@{viewModel.email}" />
<CheckBox
android:checked="@{viewModel.agreeToTerms}" />
</layout>Nümunə email və qeyd qutusu olan ən sadə formanı göstərir — hər iki sahə ikitərəfli bağlamadan istifadə edir ki, bu da Activity kodunda TextWatcher və OnCheckedChangeListener yazmaq ehtiyacını aradan qaldırır. İstifadəçi tərəfindən mətn dəyişdirildikdə viewModel.email sahəsi avtomatik yenilənir.
@BindingAdapter("app:rating")
fun RatingBar.setRating(rating: Float) {
if (rating != this.rating) {
this.rating = rating
}
}
@InverseBindingAdapter("app:rating")
fun RatingBar.getRating(): Float = this.rating
@BindingAdapter("app:ratingAttrChanged")
fun RatingBar.setListeners(
listener: InverseBindingListener?
) {
this.onRatingBarChangeListener =
RatingBar.OnRatingBarChangeListener { _, _, _ -> listener?.onChange() }
}RatingBar üçün xüsusi BindingAdapter bir cüt annotasiyadan istifadə edir — @BindingAdapter və @InverseBindingAdapter — DataBinding kitabxanasının View-dən dəyəri necə oxuyacağını (əks əlaqə) və View-ə necə yazacağını (birbaşa əlaqə) bilməsi üçün. AttrChanged suffiksi olan üçüncü adapter sistemi istifadəçi tərəfindən dəyərin dəyişməsi barədə xəbərdar edir.
Jetpack Compose @={} sintaksisini dəstəkləmir, lakin mutableStateOf və setter funksiyasının aşkar ötürülməsi vasitəsilə oxşar mexanizm təmin edir. Compose-da ikitərəfli bağlama State və callback funksiyasının (value, onValueChange) uşaq komponentlərə ötürülməsi əsasında qurulur.
@Composable
fun LoginScreen() {
var email by remember { mutableStateOf("") }
OutlinedTextField(
value = email,
onValueChange = { email = it },
label = { Text("Email") }
)
}
@Composable
fun CustomRatingBar(
rating: Float,
onRatingChange: (Float) -> Unit
) {
Slider(
value = rating,
onValueChange = onRatingChange,
valueRange = 0f..5f
)
}Compose-da ikitərəfli əlaqə state + callback cütü vasitəsilə imitasiya olunur — valideyn cari dəyəri və onun yenilənmə funksiyasını ötürür, uşaq komponent istifadəçi qarşılıqlı əlaqəsi zamanı callback-i çağırır. Bu yanaşma məlumat axınının istiqamətini aşkar şəkildə göstərir ki, bu da DataBinding-in gizli sinxronizasiyası ilə müqayisədə debug etməni asanlaşdırır.
SwiftUI-də ikitərəfli bağlama valideyn View-ə aid məlumat mənbəyinə read-write istinad yaradan @Binding propertyWrapper vasitəsilə həyata keçirilir. @Binding dəyəri müstəqil saxlamır — valideynin @State və ya @StateObject vasitəsilə oxuyur və yazır.
struct LoginView: View {
@State private var email = ""
@State private var agreeToTerms = false
var body: some View {
Form {
TextField("Email", text: $email)
Toggle("Şərtlərlə razıyam", isOn: $agreeToTerms)
ChildRatingView(rating: $rating)
}
}
}
struct ChildRatingView: View {
@Binding var rating: Double
var body: some View {
Slider(value: $rating, in: 0...5)
}
}Dəyişən adı qarşısındakı $ simvolu Binding istinadı yaradır: $email Binding<String> tipinə malikdir, String yox. SwiftUI TextField-də mətn dəyişməsini Combine mexanizmi vasitəsilə email xüsusiyyətinin yenilənməsi ilə avtomatik əlaqələndirir. Valideyn View uşaq komponentə öz @State-nə Binding ötürür ki, bu da deleqatlar və ya callback-lər olmadan iyerarxiyanın istənilən səviyyəsindən vəziyyəti dəyişməyə imkan verir.
Apple WWDC 2023 məlumatlarına görə, SwiftUI yenidənçəkmələri minimuma endirmək üçün diffing alqoritmindən istifadə edir: əgər @Binding dəyəri dəyişibsə, lakin View bu dəyərdən asılı deyilsə, yenidənçəkmə baş vermir. Bu, UIKit ilə müqayisə olunan performans təmin edir (ProMotion displeylərində 120 FPS-ə qədər).
İkitərəfli bağlama və birtərəfli məlumat axını (UDF) arasında seçim mobil inkişafda əsas memarlıq qərarlarından biridir. Two-Way Binding forma lokal vəziyyətləri üçün optimaldır, burada istifadəçinin hər addımı əlavə kod olmadan dərhal modeldə əks olunmalıdır. UDF tətbiqin qlobal vəziyyəti üçün üstünlük təşkil edir, burada dəyişikliklərin proqnozlaşdırıla bilməsi inkişaf sürətindən daha vacibdir.
| Meyar | Two-Way Binding | UDF |
|---|---|---|
| Kodun həcmi formada | 1 sətir (@={} atributu) | 5–7 sətir (State, Intent, Reducer) |
| Məlumat axınının debug-u | Çətin (kim dəyişdi — UI yoxsa kod?) | Asan (bütün dəyişikliklər Intent vasitəsilə) |
| Performans | Yüksək (native sinxronizasiya) | Orta (Reducer + Redux təbəqəsi) |
| Ölçeklenebilirlik | Mürəkkəb formalarda validasiya ilə azalır | Ekran sayı ilə artır |
| Vəziyyətlərin proqnozlaşdırılması | Aşağı (dövrlərdən yan təsirlər) | Yüksək (reduiser — yeganə həqiqət mənbəyi) |
Tövsiyə: sadə giriş sahələri (mətn, qeyd qutuları, açarlar) üçün 3–5 sahəli formalarda mürəkkəb validasiya olmadan Two-Way Binding istifadə edin. Qlobal vəziyyəti, şəbəkə sorğuları və asılı sahələri olan ekranlar üçün birtərəfli axın və aşkar hadisə emalı ilə UDF tətbiq edin. IT Sectr-də hər iki yanaşmanı birləşdiririk: Two-Way Binding forma daxilində, UDF naviqasiya və biznes məntiqi üçün.
Sonsuz yeniləmə dövrü — Two-Way Binding istifadə edərkən ən çox rast gəlinən problem. Dövr modelin dəyişməsi UI yeniləməsinə səbəb olduqda yaranır ki, bu da modeli yenidən dəyişir. DataBinding-də bu, @InverseBindingAdapter-də getter setter çağırışından dərhal sonra yeni dəyər qaytardıqda baş verir. Həll — əks yazmadan əvvəl dəyərin dəyişib-dəyişmədiyini yoxlamaq (guard şərti).
İkinci geniş yayılmış səhv — hesablanmış sahələrin bağlanması. Əgər sahə başqa sahədən asılıdırsa (məsələn, ümumi dəyər = qiymət × miqdar), ikitərəfli bağlama uyğunsuz vəziyyətə səbəb ola bilər. Məsələn, istifadəçi miqdarı dəyişir, dəyərin yenidən hesablanması tetiklenir, bu da miqdarı yenidən dəyişir. Hesablanmış sahələr üçün Flow və ya Combine ilə birtərəfli bağlamadan istifadə edin.
Üçüncü səhv — LifecycleOwner olmadan Observable sahələrin bağlanması. Android DataBinding-də bindiqə LifecycleOwner ötürülməsi tələb olunur, əks halda müşahidəçilər Activity məhv edildikdə təmizlənməyəcək, bu da yaddaş sızmasına səbəb olur. Həmişə fraqmentlərdə viewLifecycleOwner, Activity-də isə this ötürün.
Google Issue Tracker (2024) məlumatlarına görə, DataBinding üzrə səhv hesabatlarının təxminən 15%-i tsiklik yeniləmələrlə bağlıdır. Diaqnostika üçün Android Studio Layout Inspector istifadə edin — o, ekrandakı bütün bağlamaların cari dəyərlərini göstərir ki, bu da sonsuz dövrənin mənbəyini tapmağı asanlaşdırır.
Tez-tez verilən suallar
Birtərəfli bağlama (One-Way Binding) məlumatları yalnız modeldən təqdimata ötürür — model dəyişdikdə UI yenilənir, lakin istifadəçi daxiletməsi modeli birbaşa dəyişmir. Two-Way Binding məlumatları hər iki istiqamətdə sinxronlaşdırır: UI-də dəyişiklik avtomatik olaraq modeli yeniləyir və əksinə. DataBinding sintaksisində fərq @{} (One-Way) və @={} (Two-Way) simvolları ilə göstərilir.
Asılı sahələri, hesablanmış dəyərləri və ya xüsusi validasiyası olan mürəkkəb formalar üçün ikitərəfli bağlamadan istifadə etməyin — bu ssenarilərdə məlumat axını proqnozlaşdırıla bilməz. Həmçinin hər elementin bindiq olduğu çox sayda elementli RecyclerView siyahılarında ondan çəkinin: çoxsaylı müşahidəçilər səbəbindən performans aşağı düşür. Hadisələrin Intent emalı ilə birtərəfli axını olan UDF daha yaxşı miqyaslanır.
Jetpack Compose daxili @={} sintaksisinə malik deyil, lakin ikitərəfli sinxronizasiya State + callback (onValueChange) cütü vasitəsilə həyata keçirilir. Valideyn cari dəyəri (State) və yeniləmə funksiyasını ötürür, uşaq komponent dəyişiklik zamanı callback-i çağırır. Bu aşkar bağlamadır, gizli deyil — məlumat axını görünən və izlənilə bilən qalır.
DataBinding-də dövrələri debug etmək üçün Android Studio Layout Inspector istifadə edin — o, ekrandakı bütün bağlı dəyişənlərin cari dəyərlərini göstərir. @InverseBindingAdapter-də loglama əlavə edin və getter-in yenicə yazılmış dəyərdən fərqli dəyər qaytarmadığını yoxlayın. Standart həll — əks yazmadan əvvəl guard şərti: if (newValue != currentValue).
Flutter-da daxili ikitərəfli bağlama yoxdur, lakin TextEditingController və onChanged callback-i vasitəsilə emulyasiya olunur. StatefulWidget üçün tərtibatçı əl ilə nəzarətçinin dəyişikliklərinə abunə olur və modeli yeniləyir. Provider və Riverpod-da ikitərəfli sinxronizasiya Selector vasitəsilə qurulur, o, model dəyişdikdə vidjeti yenidən qurur və istifadəçi daxiletməsi zamanı callback-i çağırır.
Nəticə
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun