איך מפתחים אפליקציות ל-Android באמצעות Firebase ו-Jetpack פיתוח נייטיב

1. מבוא

תאריך עדכון אחרון: 16 בנובמבר 2022

פיתוח אפליקציה ל-Android באמצעות Firebase ו-Jetpack Compose

בקודלאב הזה תלמדו ליצור אפליקציית Android בשם Make It So. ממשק המשתמש של האפליקציה הזו נוצר כולו באמצעות Jetpack Compose, ערכת הכלים המודרנית של Android ליצירת ממשק משתמש מקומי. הכלי הזה אינטואיטיבי ודורש פחות קוד מאשר כתיבת קובצי ‎.xml וקישור שלהם ל-Activities, ל-Fragments או ל-Views.

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

מומלץ מאוד לקרוא את המאמר הזה כדי להבין איך ה-Model – View – ViewModel חל על אפליקציית Android שנוצרה באמצעות Jetpack Compose. כך יהיה קל יותר להבין את קוד הבסיס ולהשלים את השלבים הבאים.

מה תפַתחו

Make It So היא אפליקציה פשוטה לרשימת משימות שמאפשרת למשתמשים להוסיף ולערוך משימות, להוסיף דגלים, רמות תעדוף ומועדים להגשה ולסמן את המשימות כמשימות שבוצעו. בתמונות הבאות מוצגים שני הדפים הראשיים של האפליקציה: דף היצירה של המשימות והדף הראשי עם רשימת המשימות שנוצרו.

המסך 'הוספת משימה' ב-Make it So מסך הבית של Make it So

תוסיפו כמה תכונות שחסרות באפליקציה הזו:

  • אימות משתמשים באמצעות כתובת אימייל וסיסמה
  • הוספת מאזין לאוסף ב-Firestore ויצירת תגובה של ממשק המשתמש לשינויים
  • הוספת מעקבים מותאמים אישית כדי לעקוב אחרי הביצועים של קוד ספציפי באפליקציה
  • איך יוצרים מתג להפעלה או להשבתה של תכונה באמצעות הגדרת תצורה מרחוק ומשתמשים בהשקה מדורגת כדי להשיק אותה

מה תלמדו

  • איך משתמשים באימות ב-Firebase, במעקב אחר ביצועים, בהגדרת תצורה מרחוק וב-Cloud Firestore באפליקציה מודרנית ל-Android
  • איך להתאים את ממשקי ה-API של Firebase לארכיטקטורה של MVVM
  • איך משקפים שינויים שבוצעו באמצעות ממשקי ה-API של Firebase בממשק המשתמש של Compose

מה צריך להכין

2. הורדת האפליקציה לדוגמה והגדרת Firebase

קבלת הקוד של האפליקציה לדוגמה

משכפלים את המאגר ב-GitHub משורת הפקודה:

git clone https://github.com/FirebaseExtended/make-it-so-android.git

יצירת פרויקט Firebase

בשלב הראשון, עוברים אל מסוף Firebase ויוצרים פרויקט Firebase בלחיצה על הלחצן '+ הוספת פרויקט', כפי שמוצג בהמשך:

מסוף Firebase

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

הוספת אפליקציה ל-Android לפרויקט Firebase

בפרויקט Firebase אפשר לרשום אפליקציות שונות: ל-Android, ל-iOS, לאינטרנט, ל-Flutter ול-Unity.

בוחרים באפשרות Android, כפי שמוצג כאן:

סקירה כללית על פרויקט Firebase

לאחר מכן מבצעים את השלבים הבאים:

  1. מזינים com.example.makeitso בתור שם החבילה, ואפשר גם להזין כינוי. בקודלאב הזה אין צורך להוסיף את אישור החתימה לניפוי באגים.
  2. לוחצים על הבא כדי לרשום את האפליקציה ולקבל גישה לקובץ התצורה של Firebase.
  3. לוחצים על Download google-services.json כדי להוריד את קובץ התצורה ולשמור אותו בספרייה make-it-so-android/app.
  4. לוחצים על הבא. מאחר ש-Firebase SDKs כבר כלולים בקובץ build.gradle בפרויקט לדוגמה, לוחצים על הבא כדי לדלג אל השלבים הבאים.
  5. לוחצים על Continue to console כדי לסיים.

כדי שהאפליקציה Make it So תפעל כמו שצריך, יש שני דברים שצריך לעשות במסוף לפני שממשיכים לקוד: מפעילים את ספקי האימות ויוצרים את מסד הנתונים של Firestore.

הגדרת אימות

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

  1. בתפריט Build, בוחרים באפשרות Authentication ולוחצים על Get Started.
  2. בכרטיס שיטת כניסה, בוחרים באפשרות אימייל/סיסמה ומפעילים אותה.
  3. לאחר מכן, לוחצים על Add new provider (הוספת ספק חדש) ובוחרים באפשרות Anonymous (אנונימי) ומפעילים אותה.

הגדרת Cloud Firestore

בשלב הבא מגדירים את Firestore. תשתמשו ב-Firestore כדי לאחסן את המשימות של משתמש שמחובר לחשבון. כל משתמש יקבל מסמך משלו בתוך אוסף של מסדי הנתונים.

  1. בחלונית הימנית של מסוף Firebase, מרחיבים את Build ובוחרים באפשרות Firestore database.
  2. לוחצים על Create database.
  3. משאירים את הערך (default) בשדה Database ID.
  4. בוחרים מיקום למסד הנתונים ולוחצים על הבא.
    באפליקציה אמיתית, כדאי לבחור מיקום קרוב למשתמשים.
  5. לוחצים על התחלה במצב בדיקה. קוראים את כתב הוויתור לגבי כללי האבטחה.
    בשלב הבא בקטע הזה תוסיפו כללי אבטחה כדי לאבטח את הנתונים. אסור להפיץ או לחשוף אפליקציה באופן ציבורי בלי להוסיף כללי אבטחה למסד הנתונים.
  6. לוחצים על יצירה.

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

  1. פותחים את לוח הבקרה של Firestore ועוברים לכרטיסייה Rules.
  2. מעדכנים את כללי האבטחה כך שייראו כך:
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow create: if request.auth != null;
      allow read, update, delete: if request.auth != null && resource.data.userId == request.auth.uid;
    }
  }
}

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

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

עכשיו אפשר להריץ את האפליקציה. פותחים את התיקייה make-it-so-android/start ב-Android Studio ומריצים את האפליקציה (אפשר לעשות זאת באמצעות Android Emulator או מכשיר Android אמיתי).

3. אימות ב-Firebase

איזו תכונה אתם מתכוונים להוסיף?

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

הגיע הזמן לכתוב קוד!

ברגע שהמשתמש יוצר חשבון על ידי הקלדה של כתובת אימייל וסיסמה, צריך לבקש מ-Firebase Authentication API פרטי כניסה של כתובת אימייל, ואז לקשר את פרטי הכניסה החדשים לחשבון האנונימי. פותחים את הקובץ AccountServiceImpl.kt ב-Android Studio ומעדכנים את הפונקציה linkAccount כך שתהיה דומה לקוד הבא:

model/service/impl/AccountServiceImpl.kt

override suspend fun linkAccount(email: String, password: String) {
    val credential = EmailAuthProvider.getCredential(email, password)
    auth.currentUser!!.linkWithCredential(credential).await()
}

עכשיו פותחים את SignUpViewModel.kt ומפעילים את פונקציית השירות linkAccount בתוך הבלוק launchCatching של הפונקציה onSignUpClick:

screens/sign_up/SignUpViewModel.kt

launchCatching {
    accountService.linkAccount(email, password)
    openAndPopUp(SETTINGS_SCREEN, SIGN_UP_SCREEN)
}

קודם הוא מנסה לבצע אימות, ואם הקריאה מצליחה, הוא ממשיך למסך הבא (SettingsScreen). כשמריצים את הקריאות האלה בתוך בלוק launchCatching, אם מתרחשת שגיאה בשורה הראשונה, החריגה תתפס ותטופל, ולא תהיה גישה לשורה השנייה בכלל.

ברגע שחלון SettingsScreen נפתח שוב, צריך לוודא שהאפשרויות כניסה ויצירת חשבון לא מופיעות, כי המשתמש כבר מאומת. כדי לעשות זאת, נגרום ל-SettingsViewModel להאזין לסטטוס של המשתמש הנוכחי (זמין ב-AccountService.kt), כדי לבדוק אם החשבון אנונימי או לא. כדי לעשות זאת, מעדכנים את uiState ב-SettingsViewModel.kt כך שייראה כך:

screens/settings/SettingsViewModel.kt

val uiState = accountService.currentUser.map {
    SettingsUiState(it.isAnonymous)
}

השלב האחרון הוא לעדכן את uiState ב-SettingsScreen.kt כדי לאסוף את המצבים שמונפקים על ידי ה-SettingsViewModel:

screens/settings/SettingsScreen.kt

val uiState by viewModel.uiState.collectAsState(
    initial = SettingsUiState(false)
)

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

הגיע הזמן לבדוק!

מריצים את Make it So ולוחצים על סמל גלגל השיניים בפינה השמאלית העליונה של המסך כדי לעבור להגדרות. לאחר מכן, לוחצים על האפשרות 'יצירת חשבון':

מסך ההגדרות של Make it So מסך ההרשמה של Make it So

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

4. Cloud Firestore

איזו תכונה אתם מתכוונים להוסיף?

ב-Cloud Firestore, תצטרכו להוסיף מאזין לאוסף ב-Firestore שמאחסן את המסמכים שמייצגים את המשימות שמוצגות ב-Make it So. אחרי שתוסיפו את המאזין הזה, תקבלו עדכון על כל שינוי שיתבצע באוסף הזה.

הגיע הזמן לכתוב קוד!

מעדכנים את Flow שזמין ב-StorageServiceImpl.kt כך שייראה כך:

model/service/impl/StorageServiceImpl.kt

override val tasks: Flow<List<Task>>
    get() =
      auth.currentUser.flatMapLatest { user ->
        firestore.collection(TASK_COLLECTION).whereEqualTo(USER_ID_FIELD, user.id).dataObjects()
      }

הקוד הזה מוסיף מאזין לאוסף המשימות על סמך user.id. כל משימה מיוצגת על ידי מסמך באוסף בשם tasks, וכל אחד מהם מכיל שדה בשם userId. חשוב לזכור שיישלח Flow חדש אם הסטטוס של currentUser ישתנה (למשל, אם תצאו מהחשבון).

עכשיו צריך לוודא שהערך של Flow ב-TasksViewModel.kt זהה לערך בשירות:

screens/tasks/TasksViewModel.kt

val tasks = storageService.tasks

השלב האחרון הוא לגרום ל-composable function ב-TasksScreens.kt, שמייצג את ממשק המשתמש, להיות מודע לתהליך הזה ולאסוף אותו כמצב. בכל פעם שהמצב משתנה, הפונקציה הניתנת ליצירה מחדש תתגבש מחדש באופן אוטומטי ותציג למשתמש את המצב העדכני ביותר. מוסיפים את הפרטים האלה לTasksScreen composable function:

screens/tasks/TasksScreen.kt

val tasks = viewModel
    .tasks
    .collectAsStateWithLifecycle(emptyList())

אחרי שלפונקציה הניתנת לקיבוץ תהיה גישה למצבים האלה, תוכלו לעדכן את LazyColumn (המבנה שבו משתמשים כדי להציג רשימה במסך) כך שייראה כך:

screens/tasks/TasksScreen.kt

LazyColumn {
    items(tasks.value, key = { it.id }) { taskItem ->
        TaskItem( [...] )
    }
}

הגיע הזמן לבדוק!

כדי לבדוק אם זה עבד, מוסיפים משימה חדשה באמצעות האפליקציה (לוחצים על לחצן ההוספה בפינה השמאלית התחתונה של המסך). בסיום יצירת המשימה, היא אמורה להופיע באוסף Firestore במסוף Firestore. אם תתחברו ל-Make it So במכשירים אחרים עם אותו חשבון, תוכלו לערוך את המשימות ולראות אותן מתעדכנות בכל המכשירים בזמן אמת.

5. מעקב אחר ביצועים

איזו תכונה אתם מתכוונים להוסיף?

חשוב מאוד לשים לב לביצועים, כי סביר מאוד שהמשתמשים יפסיקו להשתמש באפליקציה אם הביצועים שלה לא טובים והם נדרשים להשקיע יותר מדי זמן כדי לבצע בה משימה פשוטה. לכן, לפעמים כדאי לאסוף מדדים מסוימים לגבי תהליך ספציפי שמשתמש מבצע באפליקציה. כדי לעזור לכם בכך, שירות ניטור הביצועים של Firebase מציע מעקב מותאם אישית. כדי להוסיף מעקבים מותאמים אישית ולמדוד את הביצועים בקטעי קוד שונים, פועלים לפי השלבים הבאים ב-Make it So.

הגיע הזמן לכתוב קוד!

אם פותחים את הקובץ Performance.kt, רואים פונקציה מוטמעת שנקראת trace. הפונקציה הזו קוראת ל-Performance Monitoring API כדי ליצור מעקב מותאם אישית, ומעבירה את שם המעקב כפרמטר. הפרמטר השני שמוצג הוא בלוק הקוד שרוצים לעקוב אחריו. מדד ברירת המחדל שנאסף לכל מעקב הוא הזמן שנדרש להרצה המלאה:

model/service/Performance.kt

inline fun <T> trace(name: String, block: Trace.() -> T): T = Trace.create(name).trace(block)

אתם יכולים לבחור אילו חלקים של קוד המקור חשוב למדוד ולהוסיף אליו מעקב מותאם אישית. בדוגמה הבאה מוצגת הוספה של מעקב מותאם אישית לפונקציה linkAccount שראיתם קודם (ב-AccountServiceImpl.kt) בקודלאב הזה:

model/service/impl/AccountServiceImpl.kt

override suspend fun linkAccount(email: String, password: String): Unit =
  trace(LINK_ACCOUNT_TRACE) {
      val credential = EmailAuthProvider.getCredential(email, password)
      auth.currentUser!!.linkWithCredential(credential).await()
  }

עכשיו תורך! מוסיפים כמה מעקבים מותאמים אישית לאפליקציה Make it So וממשיכים לקטע הבא כדי לבדוק אם היא פועלת כצפוי.

הגיע הזמן לבדוק!

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

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

6. הגדרת תצורה מרחוק

איזו תכונה אתם מתכוונים להוסיף?

יש מגוון רחב של תרחישים לדוגמה לשימוש בתכונה 'הגדרת תצורה מרחוק', החל משינויים מרחוק במראה של האפליקציה ועד להגדרת התנהגויות שונות לפלחים שונים של משתמשים. בקודלאב הזה תלמדו איך להשתמש ב-Remote Config כדי ליצור מתג להפעלה או השבתה של תכונה, שיציג או יסתיר את התכונה החדשה עריכת משימה באפליקציה Make it So.

הגיע הזמן לכתוב קוד!

השלב הראשון הוא ליצור את ההגדרה במסוף Firebase. כדי לעשות זאת, עוברים אל מרכז הבקרה של Remote Config ולוחצים על הלחצן Add parameter (הוספת פרמטר). ממלאים את השדות לפי התמונה שבהמשך:

תיבת הדו-שיח &#39;יצירת פרמטר&#39; ב-Remote Config

אחרי שממלאים את כל השדות, אפשר ללחוץ על הלחצן שמירה ואז על פרסום. עכשיו, אחרי שהפרמטר נוצר וזמין ל-codebase, צריך להוסיף לאפליקציה את הקוד שיאחזר את הערכים החדשים. פותחים את הקובץ ConfigurationServiceImpl.kt ומעדכנים את ההטמעה של שתי הפונקציות האלה:

model/service/impl/ConfigurationServiceImpl.kt

override suspend fun fetchConfiguration(): Boolean {
  return remoteConfig.fetchAndActivate().await()
}

override val isShowTaskEditButtonConfig: Boolean
  get() = remoteConfig[SHOW_TASK_EDIT_BUTTON_KEY].asBoolean()

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

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

screens/tasks/TasksViewModel.kt

fun loadTaskOptions() {
  val hasEditOption = configurationService.isShowTaskEditButtonConfig
  options.value = TaskActionOption.getOptions(hasEditOption)
}

אתם מאחזרים את הערך בשורה הראשונה ומשתמשים בו כדי לטעון את אפשרויות התפריט של פריטי המשימות בשורה השנייה. אם הערך הוא false, המשמעות היא שהתפריט לא יכיל את האפשרות לעריכה. עכשיו, אחרי שיצרתם את רשימת האפשרויות, אתם צריכים להגדיר את ממשק המשתמש כך שיציג אותה בצורה נכונה. כשאתם מפתחים אפליקציה באמצעות Jetpack Compose, אתם צריכים לחפש את ה-composable function שמצהיר איך ממשק המשתמש של ה-TasksScreen אמור להיראות. לכן פותחים את הקובץ TasksScreen.kt ומעדכנים את LazyColum כך שיצביע על האפשרויות הזמינות ב-TasksViewModel.kt:

screens/tasks/TasksScreen.kt

val options by viewModel.options

LazyColumn {
  items(tasks.value, key = { it.id }) { taskItem ->
    TaskItem(
      options = options,
      [...]
    )
  }
}

ה-TaskItem הוא composable function נוסף שמצהיר איך ממשק המשתמש של משימה אחת אמור להיראות. לכל משימה יש תפריט עם אפשרויות שמוצג כשהמשתמש לוחץ על סמל שלוש הנקודות בסופה.

הגיע הזמן לבדוק!

עכשיו אפשר להריץ את האפליקציה. בודקים שהערך שפרסמתם באמצעות מסוף Firebase תואם להתנהגות של האפליקציה:

  • אם הערך הוא false, אמורות להופיע רק שתי אפשרויות כשתלחצו על סמל שלוש הנקודות.
  • אם הוא true, אמורות להופיע שלוש אפשרויות כשתלחצו על סמל שלוש הנקודות:

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

7. מזל טוב

כל הכבוד, יצרתם אפליקציית Android באמצעות Firebase ו-Jetpack Compose!

הוספתם את Firebase Authentication, ‏ Performance Monitoring, ‏ Remote Config ו-Cloud Firestore לאפליקציית Android שנבנתה כולה באמצעות Jetpack Compose לממשק המשתמש, והתאמתם אותה לארכיטקטורה המומלצת של MVVM.

מקורות מידע נוספים

מסמכי עזרה