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

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

סקירה כללית

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

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

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

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

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

שם המודול

מכיל

api

מקשי ניווט

impl

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

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

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

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

ב-Navigation 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 DSL כשמגדירים את entryProvider במודול הראשי של האפליקציה.

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

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

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

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

לדוגמה, הקוד הבא משתמש בקישורים מרובים של Dagger, ובאופן ספציפי ב-@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 אובייקט שיכול לשנות את מצב הניווט של האפליקציה.

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

כדי לאסוף את כל ההתאמות מכל חלקי האפליקציה, אתם יכולים להשתמש ב-multibindings של הזרקת תלויות. לדוגמה, כל מודול של תכונות יכול לתרום את ההתאמות שלו ל-multibinding של Dagger @IntoSet:

מודול של תכונות

לאחר מכן, במודול הראשי של האפליקציה, כמו MainActivity, אפשר להוסיף את קבוצת ההתאמות ולהשתמש בהן כדי להתאים בקשות נכנסות:

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

מידע נוסף על הגדרה וטיפול בקישורי עומק זמין במאמר תמיכה בקישורי עומק.

משאבים

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