חדשות על מוצרים

מה חדש בגרסת Jetpack Compose מדצמבר 2025

משך הקריאה: 6 דקות
Nick Butcher
ניהול מוצרים

היום, הגרסה של Jetpack Compose מדצמבר 2025 יציבה. הגרסה הזו כוללת את גרסה 1.10 של מודולי הליבה של Compose ואת גרסה 1.4 של Material 3 (אפשר לעיין במיפוי המלא של BOM), ונוספו בה תכונות חדשות ושיפורים משמעותיים בביצועים.

כדי להשתמש בגרסה של היום, צריך לשדרג את גרסת ה-BOM של Compose ל-2025.12.00:

implementation(platform("androidx.compose:compose-bom:2025.12.00"))

שיפורים בביצועים

אנחנו יודעים שביצועי זמן הריצה של האפליקציה חשובים מאוד לך ולמשתמשים שלך, ולכן הביצועים היו בראש סדר העדיפויות של צוות Compose. הגרסה הזו כוללת מספר שיפורים – וכל מה שצריך לעשות כדי ליהנות מהם הוא לשדרג לגרסה העדכנית. השוואות הביצועים הפנימיות שלנו מראות שהביצועים של Compose זהים לביצועים שהיו מתקבלים אם הייתם משתמשים ב-Views:

janky.png

השוואה בין Views לבין Jetpack Compose בגרסאות שונות של Compose, לצורך השוואה בין ביצועי הגלילה

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

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

בעבר, אחרי שהתחלתם ליצור מוזיקה, הייתם צריכים לחכות עד שהיצירה תסתיים. אם הקומפוזיציה מורכבת, היא עלולה לחסום את ה-thread הראשי למשך זמן ארוך יותר מפריים אחד, ולגרום לממשק המשתמש לקפוא. עם האפשרות להשהות את ההרכבה, זמן הריצה יכול עכשיו 'להשהות' את העבודה אם נגמר לו הזמן, ולחדש את העבודה בפריים הבא. השיטה הזו יעילה במיוחד כשמשתמשים בה עם טעינה מראש של פריסות עצלות כדי להכין מסגרות מראש. ממשקי ה-API של CacheWindow בפריסת Lazy, שהוצגו ב-Compose 1.9, הם דרך מצוינת לאחזור מראש של תוכן נוסף וליהנות מהשהיה של הקומפוזיציה כדי לשפר את הביצועים של ממשק המשתמש.

pausable.gif

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

בנוסף, שיפרנו את הביצועים במקומות אחרים, כולל Modifier.onPlaced, Modifier.onVisibilityChanged ויישומים אחרים של משנים. נמשיך להשקיע בשיפור הביצועים של התכונה 'כתיבה בעזרת AI'.

תכונות חדשות

שימור

ל-Compose יש מספר ממשקי API לשמירה ולניהול של מצב במחזורי חיים שונים. לדוגמה, remember שומר את המצב בין קומפוזיציות, ו-rememberSavable/rememberSerializable שומר את המצב בין פעילויות או בין יצירות מחדש של תהליכים. ‫retain הוא API חדש שפועל בין ממשקי ה-API האלה, ומאפשר לכם לשמור ערכים אחרי שינויים בהגדרות בלי שהם יעברו סריאליזציה, אבל לא אחרי סיום התהליך. ‫retain לא מבצע סריאליזציה של המצב, ולכן אפשר לשמור אובייקטים כמו ביטויי למדה, זרימות ואובייקטים גדולים כמו מפות סיביות, שלא ניתן לבצע להם סריאליזציה בקלות. לדוגמה, אפשר להשתמש ב- retain כדי לנהל נגן מדיה (כמו ExoPlayer) ולוודא שהפעלת המדיה לא מופרעת בגלל שינוי בהגדרה.

@Composable

fun MediaPlayer() {

    val applicationContext = LocalContext.current.applicationContext

    val exoPlayer = retain { ExoPlayer.Builder(applicationContext).apply { ... }.build() }

    ...

}

אנחנו רוצים להודות לקהילת AndroidDev (במיוחד לצוות Circuit), שהשפיעה על העיצוב של התכונה הזו ותמכה בו.

חומר 1.4

בגרסה 1.4.0 של ספריית material3 נוספו מספר רכיבים חדשים ושיפורים:

centered-hero-carousel.webp

קרוסלת תמונות מרכזית אופקית

הערה: ממשקי ה-API של Material 3 Expressive ממשיכים להתפתח בגרסאות האלפא של ספריית material3. מידע נוסף זמין בהרצאה הבאה:

תכונות חדשות שקשורות לאנימציות

אנחנו ממשיכים להרחיב את ממשקי ה-API של האנימציות שלנו, כולל עדכונים להתאמה אישית של אנימציות של רכיבים משותפים.

אלמנטים דינמיים משותפים

כברירת מחדל, האנימציות של sharedElement() ושל sharedBounds() מנסות להנפיש

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

כדי לקבוע אם מעבר הרכיב המשותף יתרחש, אפשר עכשיו להתאים אישית את SharedContentConfig שמועבר אל rememberSharedContentState(). המאפיין isEnabled קובע אם הרכיב המשותף פעיל.

SharedTransitionLayout {

        val transition = updateTransition(currentState)

        transition.AnimatedContent { targetState ->

            // Create the configuration that depends on state changing.

            fun animationConfig() : SharedTransitionScope.SharedContentConfig {

                return object : SharedTransitionScope.SharedContentConfig {

                    override val SharedTransitionScope.SharedContentState.isEnabled: Boolean

                        get() =

                            // determine whether to perform a shared element transition

                }

            }

}

מידע נוסף זמין במאמרי העזרה.

Modifier.skipToLookaheadPosition()

בגרסה הזו נוסף משנה חדש, Modifier.skipToLookaheadPosition(), ששומר על המיקום הסופי של רכיב שאפשר להרכיב כשמבצעים אנימציות של רכיבים משותפים. כך אפשר לבצע מעברים כמו אנימציה מסוג 'חשיפה', כפי שאפשר לראות בדוגמה של Androidify עם החשיפה ההדרגתית של המצלמה. מידע נוסף זמין בסרטון הבא: 

מהירות התחלתית במעברים של רכיבים משותפים

בגרסה הזו הוספנו API חדש למעבר בין רכיבים משותפים, prepareTransitionWithInitialVelocity, שמאפשר להעביר מהירות התחלתית (למשל ממחוות) למעבר בין רכיבים משותפים:

Modifier.fillMaxSize()

    .draggable2D(

        rememberDraggable2DState { offset += it },

        onDragStopped = { velocity ->

            // Set up the initial velocity for the upcoming shared element

            // transition.

            sharedContentStateForDraggableCat

                ?.prepareTransitionWithInitialVelocity(velocity)

            showDetails = false

        },

    )
fling-shared.gif

אנימציה של מעבר בין רכיבים משותפים שמתחילה במהירות התחלתית ממחוות

מעברים מוסתרים

הפונקציות EnterTransition ו-ExitTransition מגדירות איך רכיב ה-AnimatedVisibility/AnimatedContent יופיע או ייעלם. אפשרות ניסיונית חדשה של מסך מאפשרת לכם לציין צבע למסך או לתוכן; לדוגמה, הוספה והסרה של שכבה שחורה חצי אטומה מעל התוכן:

veil_2.gif

תוכן אנימציה מוסתר – שימו לב לווילון (או למסך) החצי אטום מעל תוכן הרשת במהלך האנימציה

AnimatedContent(

    targetState = page,

    modifier = Modifier.fillMaxSize().weight(1f),

    transitionSpec = {

        if (targetState > initialState) {

            (slideInHorizontally { it } togetherWith

                    slideOutHorizontally { -it / 2 } + veilOut(targetColor = veilColor))

        } else {

            slideInHorizontally { -it / 2 } +

                    unveilIn(initialColor = veilColor) togetherWith slideOutHorizontally { it }

        }

    },

) { targetPage ->

    ...

}

השינויים הקרובים

הוצאה משימוש של Modifier.onFirstVisible

ב-Compose 1.9 הושקו Modifier.onVisibilityChanged ו-Modifier.onFirstVisible. בדקנו את המשוב שלך והבנו שלא ניתן לכבד את החוזה של Modifier.onFirstVisible באופן דטרמיניסטי, במיוחד כשפריט first הופך לגלוי. לדוגמה, פריסה מסוג Lazy עשויה להסיר פריטים שיוצאים מהתצוגה, ואז להוסיף אותם מחדש אם הם חוזרים לתצוגה. במקרה כזה, ההתקשרות חזרה onFirstVisible תופעל שוב, כי מדובר בפריט חדש. התנהגות דומה תתרחש גם כשחוזרים למסך שביקרתם בו קודם ומכיל onFirstVisible. לכן החלטנו להוציא משימוש את שינוי ההתנהגות הזה בגרסה הבאה של Compose‏ (1.11) ואנחנו ממליצים לעבור אל onVisibilityChanged. מידע נוסף זמין במאמרי העזרה.

העברת שגרות המשך (coroutine) בבדיקות

אנחנו מתכננים לשנות את השליחה של שגרות המשך (coroutine) בבדיקות כדי לשפר את היציבות של הבדיקות ולזהות יותר בעיות. בשלב הזה, הבדיקות משתמשות ב-UnconfinedTestDispatcher, ששונה מההתנהגות בסביבת הייצור. לדוגמה, יכול להיות שאפקטים יופעלו באופן מיידי במקום להתווסף לתור. בגרסה עתידית, אנחנו מתכננים להשיק API חדש שמשתמש ב- StandardTestDispatcher כברירת מחדל כדי להתאים להתנהגויות בסביבת הייצור. אתם יכולים לנסות את ההתנהגות החדשה עכשיו בגרסה 1.10:

@get:Rule // also createAndroidComposeRule, createEmptyComposeRule

val rule = createComposeRule(effectContext = StandardTestDispatcher())

השימוש ב-StandardTestDispatcher יוסיף משימות לתור, ולכן צריך להשתמש במנגנוני סנכרון כמו composeTestRule.waitForIdle() או composeTestRule.runOnIdle(). אם הבדיקה שלכם משתמשת ב-runTest, אתם צריכים לוודא ש-runTest והכלל Compose שלכם משתפים את אותו מופע של StandardTestDispatcher לצורך סנכרון.

// 1. Create a SINGLE dispatcher instance

val testDispatcher = StandardTestDispatcher()



// 2. Pass it to your Compose rule

@get:Rule

val composeRule = createComposeRule(effectContext = testDispatcher)



@Test

// 3. Pass the *SAME INSTANCE* to runTest

fun myTest() = runTest(testDispatcher) {

    composeRule.setContent { /* ... */ }

}

כלים

ממשקי API מעולים ראויים לכלים מעולים, וב-Android Studio יש כמה תוספות חדשות למפתחי Compose:

כדי לראות את הכלים האלה בפעולה, אפשר לצפות בהדגמה הזו:

כתיבה מהנה

אנחנו ממשיכים להשקיע ב-Jetpack Compose כדי לספק לכם את ממשקי ה-API והכלים שדרושים ליצירת ממשקי משתמש יפים ועשירים. המשוב שלכם חשוב לנו, ולכן נשמח אם תשתפו אותנו בדעתכם על השינויים האלה או על מה שהייתם רוצים לראות בהמשך בכלי למעקב אחר בעיות.

נכתב על ידי:

להמשך הקריאה