הפיכת קוד הניווט למודולרי

בדף הזה מוסבר איך להפוך את קוד הניווט למודולרי. הוא נועד להשלים את ההנחיות הכלליות בנושא מודולריזציה של אפליקציות.

סקירה כללית

הפיכת קוד הניווט למודולרי היא תהליך של הפרדת מקשי ניווט קשורים והתוכן שהם מייצגים למודולים נפרדים. כך יש הפרדה ברורה בין האחריות שלכם לבין האחריות שלנו, ואתם יכולים לנווט בין התכונות השונות באפליקציה.

כדי להפוך את קוד הניווט למודולרי:

  • יוצרים שני מודולים משניים: api ו-impl לכל תכונה באפליקציה
  • ממקמים את מקשי הניווט של כל תכונה בapiמודול שלה
  • ממקמים את entryProviders ואת התוכן שניתן לנווט בו לכל תכונה במודול impl המתאים.
  • מספקים את entryProviders למודולים העיקריים של האפליקציה, ישירות או באמצעות הזרקת תלות

הפרדה של התכונות למודולי משנה של API והטמעה

לכל תכונה באפליקציה, יוצרים שני מודולי משנה בשמות api ו-impl (קיצור של implementation, הטמעה). הטבלה הבאה תעזור לכם להחליט איפה למקם את קוד הניווט.

שם המודול

מכיל

api

מקשי ניווט

impl

תוכן לתכונה הזו, כולל הגדרות של NavEntry ושל entryProvider. מידע נוסף על פתרון בעיות שקשורות למפתחות לתוכן

הגישה הזו מאפשרת לתכונה אחת לנווט לתכונה אחרת על ידי כך שהתוכן שלה, שנמצא במודול impl שלה, תלוי במקשי הניווט של מודול אחר, שנמצא במודול api של אותו מודול.

תרשים של תלות במודול תכונות שמראה איך מודולים מסוג impl יכולים להיות תלויים במודולים מסוג api.
איור 1. תרשים של תלות במודול תכונות שמראה איך מודולים להטמעה יכולים להיות תלויים במודולים של API.

הפרדה בין רכיבי הניווט באמצעות פונקציות של תוספים

בניווט 3, תוכן שאפשר לנווט בו מוגדר באמצעות רשומות ניווט. כדי להפריד בין הרשומות האלה למודולים נפרדים, צריך ליצור פונקציות של תוסף ב-EntryProviderScope ולהעביר אותן למודול impl של התכונה. הם נקראים כלי ליצירת רשומות.

בדוגמת הקוד הבאה מוצג כלי ליצירת רשומות שיוצר שתי רשומות ניווט.

// import androidx.navigation3.runtime.EntryProviderScope
// import androidx.navigation3.runtime.NavKey

fun EntryProviderScope<NavKey>.featureAEntryBuilder() {
    entry<KeyA> {
        ContentRed("Screen A") {
            // Content for screen A
        }
    }
    entry<KeyA2> {
        ContentGreen("Screen A2") {
            // Content for screen A2
        }
    }
}

מגדירים את entryProvider במודול הראשי של האפליקציה ומפעילים את הפונקציה באמצעות entryProvider DSL.

// import androidx.navigation3.runtime.entryProvider
// import androidx.navigation3.ui.NavDisplay
NavDisplay(
    entryProvider = entryProvider {
        featureAEntryBuilder()
    },
    // ...
)

שימוש בהזרקת תלות כדי להוסיף רשומות לאפליקציה הראשית

בדוגמת הקוד הקודמת, כל כלי ליצירת רשומות נקרא ישירות על ידי האפליקציה הראשית באמצעות ה-DSL‏ entryProvider. אם לאפליקציה יש הרבה מסכים או מודולים של תכונות, יכול להיות שהשיטה הזו לא תהיה יעילה.

כדי לפתור את הבעיה הזו, צריך להגדיר שכל מודול תכונה יתרום את בוני הרכיבים שלו לפעילות של האפליקציה באמצעות הזרקת תלות.

לדוגמה, הקוד הבא משתמש בDagger multibindings, ובאופן ספציפי ב-@IntoSet, כדי להחדיר את ה-entry builders ל-Set שבבעלות MainActivity. לאחר מכן, הפונקציות האלה מופעלות באופן איטרטיבי בתוך entryProvider, וכך אין צורך להפעיל במפורש פונקציות רבות של יצירת רשומות.

מודול תכונות

// import dagger.Module
// import dagger.Provides
// import dagger.hilt.InstallIn
// import dagger.hilt.android.components.ActivityRetainedComponent
// import dagger.multibindings.IntoSet

@Module
@InstallIn(ActivityRetainedComponent::class)
object FeatureAModule {

    @IntoSet
    @Provides
    fun provideFeatureAEntryBuilder() : EntryProviderScope<NavKey>.() -> Unit = {
        featureAEntryBuilder()
    }
}

מודול אפליקציה

// import android.os.Bundle
// import androidx.activity.ComponentActivity
// import androidx.activity.compose.setContent
// import androidx.navigation3.runtime.EntryProviderScope
// import androidx.navigation3.runtime.NavKey
// import androidx.navigation3.runtime.entryProvider
// import androidx.navigation3.ui.NavDisplay
// import javax.inject.Inject

class MainActivity : ComponentActivity() {

    @Inject
    lateinit var entryBuilders: Set<@JvmSuppressWildcards EntryProviderScope<NavKey>.() -> Unit>

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            NavDisplay(
                entryProvider = entryProvider {
                    entryBuilders.forEach { builder -> this.builder() }
                },
                // ...
            )
        }
    }
}

אם רכיבי הניווט צריכים לנווט – למשל, אם הם מכילים רכיבי ממשק משתמש שמנווטים למסכים חדשים – צריך להחדיר אובייקט שיכול לשנות את מצב הניווט של האפליקציה לכל פונקציית Builder.

משאבים

דוגמאות קוד שמראות איך להפוך את הקוד של Navigation 3 למודולרי: