מושגים וארכיטקטורה של Remote Compose

‫Remote Compose היא ספריית Jetpack שמאפשרת ליצור ממשק משתמש באמצעות ממשקי API בסגנון Compose באפליקציה, ולהציג אותו בתהליכים של המערכת, כמו ווידג'טים של אפליקציות או ווידג'טים של Wear OS. האפליקציה מתעדת את ממשק המשתמש כמסמך קומפקטי, ונגן בתהליך המארח מעבד אותו ומטפל באינטראקציות, בשינויי מצב ובאנימציות באופן מקומי, בלי לבצע קריאה חוזרת לאפליקציה.

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

תרחישים לדוגמה של Remote Compose

אפשר להשתמש ב-Remote Compose בכל פלטפורמה עם נגן תומך. ב-Android, הוא מפעיל ווידג'טים של אפליקציות וווידג'טים של Wear OS.

איך זה עובד: ארכיטקטורה של שלושה בלוקים

ההבדל הארכיטקטוני הבסיסי בין Remote Compose לבין Jetpack Compose הרגיל הוא ההפרדה בין הגדרת ממשק המשתמש לבין ההפעלה שלו בשלושה בלוקים ברמה גבוהה:

  • יצירת המסמך: האפליקציה מריצה את הפונקציות של @RemoteComposable כדי לתעד מסמך: תיאור קומפקטי וסדרתי של ממשק המשתמש, כולל הפריסה, המצב מרחוק והפעולות שלו. כשהנתונים משתנים, האפליקציה יכולה לתעד ולשלוח מסמך חדש.
  • העברת המסמך למארח המרוחק: האפליקציה מעבירה את המסמך למארח, התהליך שמציג אותו, כמו מרכז האפליקציות או Wear OS.
  • הפעלת המסמך: נגן Remote Compose במארח מעבד את המסמך ומטפל בפריסה, בקלט, בשינויים במצב חוויית המשתמש ובאנימציות באופן מקומי, בלי לבצע קריאה חוזרת לאפליקציה.

הארכיטקטורה הזו מספקת יתרונות עיקריים שמפחיתים את זמן האחזור באופן ישיר:

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

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

תקשורת בין תהליך האפליקציה לתהליך המארח המרוחק ב-Remote Compose
איור 1. שלושת הבלוקים של ארכיטקטורת Remote Compose

רכיבים

‫Remote Compose מספק רכיבי ממשק משתמש ברמה גבוהה כדי ליצור פריסות. השתמשו ברכיבים של Remote Compose וב-RemoteModifier במקום ברכיבים המקבילים הרגילים של Compose. הם כוללים קונטיינרים בסיסיים של פריסה וגם רכיבים מותאמים שתוכננו במיוחד לטיפול בגדלים דינמיים של מסכים:

  • רכיבי פריסה בסיסיים: רכיבי פריסה נפוצים שדומים ל-Compose, כמו RemoteBox,‏ RemoteRow ו-RemoteColumn.
  • רכיבי פריסה מותאמים: רכיבים מיוחדים כמו RemoteFitBox,‏ RemoteCollapsibleRow ו-RemoteCollapsibleColumn, שנועדו להתאים בצורה חלקה למשטחים מרוחקים.
  • עיצוב Material Design: העיצוב פועל באותו אופן בווידג'טים של Wear OS, עם היררכיה של צבעים וטיפוגרפיה שסופקה על ידי RemoteMaterialTheme. כך אפשר להטמיע מערכת מיתוג ועיצוב עקבית באפליקציה ובווידג'טים.

ציור בהתאמה אישית עם RemoteCanvas

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

ההבדלים העיקריים

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

הפניות למצב סמלי

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

  • משתמשים בפונקציות של Remote Compose factory כדי להצהיר על מצב כמו rememberMutableRemoteInt,‏ rememberMutableRemoteFloat או rememberMutableRemoteString.
  • קוד האפליקציה לא יכול לבדוק או לקרוא את הערך של משתנה מצב מרוחק ישירות במהלך הקומפוזיציה.
  • הנגן שומר ומעדכן את הערכים האלה באופן מקומי כשהמשתמש יוצר אינטראקציה עם המסמך.

שמירת המצב ביעד מרוחק לעומת תהליך בקרה ב-Kotlin

קוד Kotlin פועל רק כשהאפליקציה יוצרת את המסמך. הצהרות Kotlin כמו if,‏ when ו-for יכולות לעצב את המסמך על סמך ערכים שידועים באותו זמן, כמו חזרה על שורה לכל פריט ברשימה, אבל הן לא יכולות להגיב למצב מרוחק שמשתנה מאוחר יותר. כשעובדים ב-Remote Compose, חשוב לזכור את הדברים הבאים:

  • לוגיקת הסתעפות: החלפת ממשק המשתמש הדינמי מתבצעת באמצעות RemoteStateLayout על ידי מיפוי המצב למשבצות RemoteComposable שונות.
  • הערכת טקסט: הימנעו מאינטרפולציה רגילה של מחרוזות Kotlin ("$remoteValue"), שמתבצעת רק כשהמסמך נוצר. במקום זאת, מעבירים את RemoteString אל RemoteText.

עיצוב דינמי באמצעות RemoteModifier

עיצוב דינמי במארח לא מפעיל מחדש את הקומפוזיציה. במקום זאת, משתני מצב מרוחקים מועברים ישירות ל-modifiers, כמו RemoteModifier.background(remoteColor).

הקשר של מערכת מרוחקת

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

פעולות הצהרתיות

ב-Compose רגיל, פונקציות handler של קליקים הן פונקציות lambda שפועלות באפליקציה. המארח לא יכול להריץ את הקוד של האפליקציה, לכן ב-Remote Compose מצרפים פעולות במקום זאת, למשל באמצעות RemoteModifier.clickable(action). פעולה יכולה לעדכן את המצב מרחוק ישירות במארח באמצעות valueChange, או להעביר את הטיפול לאפליקציה או למארח באמצעות pendingIntentAction או hostAction. כדי להפעיל כמה פעולות בלחיצה אחת, משתמשים ב-combinedAction.

השוואה כללית בין הפרדיגמות

דומיין התכונה

‫Jetpack פיתוח נייטיב רגיל

כתיבה מרחוק

אירועי קליקים ואירועי מגע

פונקציות למבדה סינכרוניות בתהליך

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

לוגיקה של משפט תנאי

תהליך בקרה ב-Kotlin באמצעות if ו-when

‫if/when רגיל של Kotlin פועל כרגיל במצב סטטי שידוע בזמן יצירת המסמך.

הסתעפות דינמית משתמשת ב-RemoteStateLayout (לעצי ממשק משתמש).

לולאות של בקרת זרימה

לולאות for ו-while רגילות ב-Kotlin

לולאות Kotlin רגילות (repeat, ‏ for, ‏ forEach) פועלות רק כשהמסמך נוצר. הם לא יכולים לעבור בלולאה על מצב מרוחק במארח

מארח הביצוע

תהליך האפליקציה

מרכז האפליקציות (AppWidget), ווידג'טים של Wear OS או כל גורם צורה עם נגן תומך

מחזור החיים של תהליך

התהליך חייב להישאר פעיל כדי לבצע הרכבה מחדש ועיבוד

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

תופעות לוואי

מחזורי חיים של קורוטינות (LaunchedEffect)

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

פרדיגמת המצב

ערכים של מצב הזיכרון המקומי

פונקציות Factory‏ (rememberMutableRemoteInt()‎,‏ rememberNamedRemoteColor()‎) שמחזירות הפניות למצב סמלי במקום ערכים גולמיים.