تعرف على Dagger Hilt

قبل ما نبدأ نتعرف على Dagger Hilt، لازم تفهم مصطلح dependency وdependent ، هنفترض دلوقتي أنت بتعمل تطبيق بسيط بيتكون Activity واحد، الAcitivty دا بيكون بداخله Fragment جواها الadapter لRecyclerView بسيطة ، فخلينا نجمع اللي قولناه دا في نقتطين:

  • Activity بيعتمد بشكل رئيسي على Fragment ومن غيره مش هيعرف يعمل الشغل اللي أنت قايله يعمله
  • Fragment بيعتمد بشكل رئيسي على Adapter ومن غيره مش هيعرف يظهر list في fragment

في أول مثال الـActivity كدا اسمه dependent بما أنه بيعتمد على Fragment، وفي نفس الحالة كدا الـfragment هيكون Dependency عشان هو المعتمد عليه في أنه يخلي الActivity يعمل كل الوظائف اللي المفروض يعلها

تاني نقطة بقا الـFragment هو بيعتمد على Adapter فكدا اسمه dependent وفي نفس الوقت الAdapter هيكون Dependency عشان الfragment بيعتمد عليه ولو هو مش موجود، الfragment مش هيعرف يعمل الوظيفة بتاعته.

فبكل اختصار أي object أنت بتعرفه جوا أي Class، الobject دا هيكون اسمه dependency، نخش في المصطلح التاني وهو :

Dependency injection:

في الطريقة العادية لو عايزين نعرف SharedPreference، عشان نستخدمها في الActivity فهنعرفها بالطريقة ديه:

 var sharedPreferences = getSharedPreferences("APP_KEY", Context.MODE_PRIVATE)

بعدين تخيل أننا رحنا Activity أو Fragment تانيين وعايزين نجيب قيمة معينة من sharedpreference، فكدا هنضطر نكتب نفس الكود ونعرف sharedpreference جديدة في أي Activity أو Fragment هنرحلها، الموضوع هنا بالنسبة للمثال دا ممكن يكون بسيط هو سطر واحد، ومبيستهلكش موارد من الجهاز كتيرة، لكن تخيل ان البروجكت بتاعك كبر وهيكون عند أكتر من 50 dependencies، هنا هتكون المشكلة بقا هيكون عندك سطور الكود متكررة في كذا مكان، استهلاك عالي لمصادر الجهاز عشان كل dependencies بتتخزن في الذاكرة العشوائية للجهاز، وكمان التطبيق هيكون عرضه للmemory leak بشكل كبير.

فطلع عندنا بقا مصطلح Dependency injection عشان يحل المشكلة ديه، وهي ببساطة أننا مبنعرفش sharedpreference في كل مكان بنحتاجها، لأ أحنا هنعرفها مرة واحدة بس، ولما تيجي تحتاجها هنعملها injection أو حقن، ودا هيحل المشاكل اللي قولنها فوق، فليه نستخدم Dependency injection؟

  • تعريف الObjects مرة واحدة واعادة استخدامها في أكتر من مكان
  • لو عايز تعدل في طريقة تعريف object هتروح تعدل في مكان واحد بس
  • الحماية من Memory leak والضغط على مصادر الجهاز
  • كود أنضف
  • سهولة في الTesting

Dagger 2 :

جوجل عشان توفر على الdevelopers الوقت في Dependency injection عملت library أسمها Dagger -مع العلم أن فيه مكتبات تانية مثل Koin-، المكتبة بتوفر عليك حجات كتير هي بتعملها، وبتسهل عليك أنك تعمل inject لactivity والحجات التانية الخاصة بالفريم ورك بتاع الاندرويد زي الFragment، الحجات ديه النظام هو اللي بيعملها، يعني مينفعش تعمل object من نوع activity

فيه مكتبتين جوجل نزلتهم Dagger وDaggerAndroid، من الاسم واضح ان Dagger Android فيها شوية زيادات المفروض أنها بتسهل عليك تعمل حجات كتير في الفريم وورك وبتقلل الكود، بس في نفس الوقت مشاكلها كبيرة وانها مش مدياك حرية تتحكم بأي حاجة، فعشان كدا أغلب الناس كانت بتستخدم Dagger العادي هتكتب كود أكتر ومكرر بس بتدك حرية.

فكدا زي ما أنت لاحظت أن الاتنين فيهم مميزات وعيوب، غير المشاكل اللي فوق أن فيه مشكلة تانية مش سهلة أنه الاتنين مش شغالين كويس مع android Jetpack ، مينفعش تعمل inject لViewModel أو Workmanager، فيه كام حل ظهروا بس الموضوع لسه صعب وبيضطر تكتب كود كبير أنت في غنى عنه، عشان كدا جوجل عملت اصدار جديد اسمه Dagger Hilt بيحل كل مشاكل النسختين القدام من Dagger، هنبدأ نتعرف عن مميزاته وازاي بتشتغل:

Dagger Hilt:

شوف المميزات بتاعتها عشان نبدأ نعملها Setup ونكتب الكود:

  • كود أسهل وأقل
  • كود ثابت عشان تبدأ تستخدمه في أي تطبيق
  • فصل مكونات التطبيقات عن بعض
  • سهولة الTesting

أعمل بروجكت جديد، وابدأ معانا : أول حاجة عايزين نعملها هنضيف Dagger Hilt للbuild.gradle:

  dependencies {

  implementation "com.google.dagger:hilt-android:{dagger_version}"
  kapt "com.google.dagger:hilt-android-compiler:{dagger_version}"
  
  }
  
  
kapt {
 correctErrorTypes true
}

لو أنت بتستخدم جافا، بدل ما Kapt هتستخدم annotationProcessor، بعدين هنضيف الplugin لbuild.gradle بتاع للمشروع كلوا:

buildscript {
  repositories {
    // other repositories...
    mavenCentral()
  }
  dependencies {
    // other plugins...
    classpath 'com.google.dagger:hilt-android-gradle-plugin:{dagger_version}'
  }
}

وبعد كدا نرجع نضيف Plugin بتاعت dagger في build.gradle اللي جوا app عشان نعرف نستخدمها، ولازم أكيد نكون بنستخدم kapt:

id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'

عشان تعرف أخر نسخة من Dagger عشان تستخدمها من هنا، قبل ما بقا نعرف ايه أهمية plugin اللي ضفناها فوق، لازم تعرف أن الكود اللي بتكتبه وقت الbuild بيتحول لكود تاني بيكون مكتوب bytecode الفريم ورك بيفهمه، فالشغل العادي أنك بتكتب كود زيادة عشان ينفع يتحول لbytecode بسهولة، لكن لو استخدمت الplugin فهي هتوفر عليك الكود الزيادة اللي المفروض أنت تكتبه.

عشان نبدأ نستخدم Dagger Hilt: لازم تعمل class جديد يكون عامل extend لكلاس Application، هنضيف annotation اسمه HiltAndroidApp، عشان نبدأ نعرف التطبيق أنه هنستخدم فيه Dagger Hilt:

@HiltAndroidApp
class MyApplication : Application()  {
}

بعدين لازم نضيف الكلاس لملف manifest

 <application
        android:allowBackup="true"
        android:name=".MyApplication" 

كدا خلصنا أول خطوة، واحنا مستعدين نعرف objects ونستخدمها في أي مكان في الapplication، بس عشان تعرف الفرق الكبير بين Dagger و Dagger شوف الكود الجي مكتوب بDagger :

class MyApplication : MyBaseApplication() {
  @Inject lateinit var classA: ClassA

  override fun onCreate() {
    super.onCreate()

    val myComponent =
        DaggerMyComponent
            .builder()
            .build()

    myComponent.inject(this)
  }
}

كنا عشان نستخدم classA، كان لازم الأول بعد super.onCreate نعمل Inject للComponent -المكان اللي بتعرف فيه أجزاء التطبيق – ، لكن لو استخدمت Hilt فأنت مش هتحتاج الموضوع دا الكود هيكون:

@HiltAndroidApp
class MyApplication : MyBaseApplication() {
   @Inject lateinit var classA: ClassA

  override fun onCreate() {
    super.onCreate() // Injection happens in super.onCreate()
    
  }
}

لاحظ أن أحنا مش محتاجين نعرف الComponent ولا محتاجين نعمله inject عشان نستخد Class A، الInject بيحصل بشكل تلقائي جوا OnCreate.

AndroidEntryPoint:

زي ما قولنا فوق أن فيه حجات في الفريم ورك مش احنا اللي بنعملها، لكن النظام هو اللي بيبدأ وبينهيه بطرقه عن طريق أوامر احنا بندالها، الحجات ديه في Dagger بنعملها Annotation بAndroidEntryPoint، وهما خمس حجات :

  • Activity
  • Fragment
  • View
  • Service
  • BroadcastReceiver

المثال الجي بيورينا ازاي ممكن تعمل inject لsharedPreference من غير ماتعمل object جديد جوا Activity

@AndroidEntryPoint
class MyActivity : MyBaseActivity() {
  @Inject 
  lateinit var sharedPreferences : SharedPreferences; 
  
// Bindings in ApplicationComponent or ActivityComponent
  override fun onCreate() {
    // Injection happens in super.onCreate().
    
  //  we can use sharedPreferences anywhere in the class
   sharedPreferences.edit { putBoolean("Test",false) }

  }
}

لاحظ أول حاجة أننا عملنا annotation بAndroidEntryPoint عشان دا activity وهو نوع من الخمسة اللي قولناهم فوق، بعدين عرفنا sharedpreference عن طريق @Inject، احنا بنكون معرفنها مرة واحدة في حتة اسمها الـComponent هنتكلم عنهم المرة الجاية، وبعدين dagger بيعمل inject للsharedPreferences ويربطها بالتعريف اللي كنا احنا عاملينه في الـComponent وبعدها ممكن نستخدمها عادي جدًا من غير أي تعريف في Activity بمجرد Inject.

المرة الجاية هنتكلم بتوسع أكبر ونشرح الComponents ومعنى وانواع الScope والطريقة الجديدة عشان تعمل inject لـViewModel.

Happy coding

الكاتب: Mohamed Saber

Pharmacist, Android developer and UI/UX enthusiast

(2) تعليقات

اترك ردا