1. מבוא
העדכון האחרון: 16 בנובמבר 2022
פיתוח אפליקציית Android באמצעות Firebase ו-Jetpack Compose
ב-codelab הזה תיצרו אפליקציית Android בשם Make It So. ממשק המשתמש של האפליקציה הזו בנוי כולו באמצעות Jetpack Compose, ערכת הכלים המודרנית של Android ליצירת ממשק משתמש מקורי – היא אינטואיטיבית ודורשת פחות קוד מאשר כתיבת קובצי .xml וקישור שלהם לפעילויות, לרכיבי Fragment או לתצוגות.
השלב הראשון כדי להבין עד כמה Firebase ו-Jetpack Compose עובדים טוב ביחד הוא להבין את הארכיטקטורה המודרנית של Android. ארכיטקטורה טובה מאפשרת להבין את המערכת בקלות, לפתח אותה בקלות ולתחזק אותה בקלות, כי היא מציגה בצורה ברורה מאוד את האופן שבו הרכיבים מאורגנים ומתקשרים זה עם זה. בעולם של Android, הארכיטקטורה המומלצת נקראת Model - View - ViewModel. המודל מייצג את השכבה שמקבלת גישה לנתונים באפליקציה. השכבה View היא שכבת ממשק המשתמש, ולא אמור להיות לה מידע על הלוגיקה העסקית. ב-ViewModel מוחל הלוגיקה העסקית, ולפעמים נדרשת קריאה של ViewModel לשכבת Model.
מומלץ מאוד לקרוא את המאמר הזה כדי להבין איך Model - View - ViewModel מיושם באפליקציית Android שנבנתה באמצעות Jetpack Compose. כך יהיה קל יותר להבין את בסיס הקוד ולבצע את השלבים הבאים.
מה תפַתחו
Make It So היא אפליקציה פשוטה של רשימת מטלות שמאפשרת למשתמש להוסיף ולערוך משימות, להוסיף דגלים, עדיפויות ותאריכי יעד ולסמן את המשימות כהושלמו. בתמונות שלמטה מוצגים שני הדפים הראשיים של האפליקציה הזו: הדף ליצירת משימות והדף הראשי עם רשימת המשימות שנוצרו.
תוסיפו כמה תכונות שחסרות באפליקציה הזו:
- אימות משתמשים באמצעות כתובת אימייל וסיסמה
- הוספת listener לקולקציית Firestore וגורמת לממשק המשתמש להגיב לשינויים
- הוספת עקבות בהתאמה אישית כדי לעקוב אחרי הביצועים של קוד ספציפי באפליקציה
- יצירת מתג להפעלה או להשבתה של תכונה באמצעות הגדרת תצורה מרחוק ושימוש בהשקה מדורגת כדי להשיק אותה
מה תלמדו
- איך משתמשים באימות ב-Firebase, במעקב אחר ביצועים, בהגדרת תצורה מרחוק וב-Cloud Firestore באפליקציית Android מודרנית
- איך משלבים ממשקי API של Firebase בארכיטקטורת MVVM
- איך משקפים בממשק משתמש של Compose שינויים שבוצעו באמצעות Firebase API
מה צריך
- Android Studio Flamingo+
- Android Emulator עם API מגרסה 21 ואילך
- היכרות עם שפת התכנות Kotlin
2. הורדת אפליקציה לדוגמה והגדרת Firebase
קבלת הקוד של האפליקציה לדוגמה
משכפלים את המאגר ב-GitHub משורת הפקודה:
git clone https://github.com/FirebaseExtended/make-it-so-android.git
יצירת פרויקט Firebase
- נכנסים למסוף Firebase באמצעות חשבון Google.
- לוחצים על הלחצן ליצירת פרויקט חדש ומזינים שם לפרויקט (לדוגמה,
Compose Firebase codelab
). - לוחצים על המשך.
- אם מוצגת בקשה לעשות זאת, קוראים ומאשרים את התנאים של Firebase, ואז לוחצים על המשך.
- (אופציונלי) מפעילים את העזרה מבוססת-AI במסוף Firebase (שנקראת Gemini ב-Firebase).
- ב-codelab הזה צריך להשתמש ב-Google Analytics כדי להשתמש באפשרויות טירגוט מתקדמות עם Remote Config, לכן חשוב להשאיר את המתג במצב מופעל באפשרות Google Analytics. פועלים לפי ההוראות במסך כדי להגדיר את Google Analytics.
- לוחצים על יצירת פרויקט, מחכים שהפרויקט יוקצה ולוחצים על המשך.
איך מוסיפים אפליקציית Android לפרויקט Firebase
בפרויקט Firebase, אפשר לרשום אפליקציות שונות: ל-Android, ל-iOS, לאינטרנט, ל-Flutter ול-Unity.
בוחרים באפשרות Android, כמו שרואים כאן:
ואז פועלים לפי השלבים הבאים:
- מזינים
com.example.makeitso
כשם החבילה, ואם רוצים מזינים גם כינוי. ב-codelab הזה, אין צורך להוסיף את אישור החתימה לניפוי באגים. - לוחצים על הבא כדי לרשום את האפליקציה ולגשת לקובץ התצורה של Firebase.
- לוחצים על הורדת google-services.json כדי להוריד את קובץ ההגדרות ולשמור אותו בספרייה
make-it-so-android/app
. - לוחצים על הבא. מכיוון ש-Firebase SDKs כבר כלולים בקובץ
build.gradle
בפרויקט לדוגמה, לוחצים על הבא כדי לדלג אל השלבים הבאים. - לוחצים על Continue to console (המשך אל מסוף Cloud) כדי לסיים.
כדי שאפליקציית Make it So תפעל בצורה תקינה, צריך לבצע שני דברים ב-Console לפני שקופצים לקוד: להפעיל ספקי אימות וליצור את מסד הנתונים של Firestore.
הגדרת אימות
קודם נפעיל אימות כדי שהמשתמשים יוכלו להתחבר לאפליקציה:
- בתפריט Build (יצירה), בוחרים באפשרות Authentication (אימות) ולוחצים על Get Started (תחילת העבודה).
- בכרטיס שיטת הכניסה, בוחרים באפשרות אימייל/סיסמה ומפעילים אותה.
- לאחר מכן, לוחצים על הוספת ספק חדש ובוחרים באפשרות אנונימי ומפעילים אותה.
הגדרה של Cloud Firestore
בשלב הבא, מגדירים את Firestore. תשתמשו ב-Firestore כדי לאחסן את המשימות של משתמשים שמחוברים לחשבון. כל משתמש יקבל מסמך משלו בתוך אוסף של מסד הנתונים.
- בחלונית הימנית במסוף Firebase, מרחיבים את Build ובוחרים באפשרות Firestore database.
- לוחצים על יצירת מסד נתונים.
- משאירים את הערך
(default)
בשדה מזהה מסד הנתונים. - בוחרים מיקום למסד הנתונים ולוחצים על הבא.
באפליקציה אמיתית, כדאי לבחור מיקום שקרוב למשתמשים. - לוחצים על התחלה במצב בדיקה. קוראים את כתב הוויתור בנוגע לכללי האבטחה.
בשלבים הבאים של הקטע הזה, תוסיפו כללי אבטחה כדי להגן על הנתונים. אל תפיצו או תחשפו אפליקציה באופן ציבורי בלי להוסיף כללי אבטחה למסד הנתונים. - לוחצים על יצירה.
כדאי להקדיש רגע לבניית כללי אבטחה חזקים למסד הנתונים של Firestore.
- פותחים את מרכז הבקרה של Firestore ועוברים לכרטיסייה כללים.
- מעדכנים את כללי האבטחה כך שייראו כמו בדוגמה הבאה:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /tasks/{document} {
allow create: if request.auth != null;
allow read, update, delete: if request.auth != null
&& resource.data.userId == request.auth.uid
&& request.data.userId == resource.data.userId;
}
}
}
הכללים האלה אומרים בעצם שכל משתמש שמחובר לאפליקציה יכול ליצור לעצמו מסמך בכל אוסף. אחרי שהמסמך נוצר, רק המשתמש שיצר אותו יוכל לצפות בו, לעדכן אותו או למחוק אותו.
הפעלת האפליקציה
עכשיו אפשר להפעיל את האפליקציה. פותחים את התיקייה make-it-so-android/start
ב-Android Studio ומריצים את האפליקציה (אפשר לעשות זאת באמצעות Android Emulator או מכשיר Android אמיתי).
3. אימות ב-Firebase
איזו תכונה אתה רוצה להוסיף?
במצב הנוכחי של אפליקציית הדוגמה Make It So, משתמש יכול להתחיל להשתמש באפליקציה בלי להיכנס לחשבון קודם. הוא משתמש באימות אנונימי כדי להשיג את זה. עם זאת, חשבונות אנונימיים לא מאפשרים למשתמש לגשת לנתונים שלו במכשירים אחרים או אפילו בסשנים עתידיים. למרות שאימות אנונימי שימושי להצטרפות חמה, תמיד כדאי לספק למשתמשים אפשרות לעבור לשיטת כניסה אחרת. לכן, במעבדת הקוד הזו, תוסיפו אימות באמצעות כתובת אימייל וסיסמה לאפליקציית 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 ועוברים להגדרות בלחיצה על סמל גלגל השיניים בפינה השמאלית העליונה של המסך. משם, לוחצים על האפשרות ליצירת חשבון:
כדי ליצור את החשבון, מקלידים כתובת אימייל תקינה וסיסמה חזקה. הפעולה אמורה לעבוד ותועברו לדף ההגדרות, שבו יוצגו שתי אפשרויות חדשות: יציאה מהחשבון ומחיקת החשבון. כדי לבדוק את החשבון החדש שנוצר, לוחצים על הכרטיסייה Users (משתמשים) בלוח הבקרה של Authentication (אימות) במסוף Firebase.
4. Cloud Firestore
איזו תכונה אתה רוצה להוסיף?
ב-Cloud Firestore, תוסיפו listener לאוסף Firestore שבו מאוחסנים המסמכים שמייצגים את המשימות שמוצגות ב-Make it So. אחרי שתוסיפו את ה-listener הזה, תקבלו כל עדכון שיתבצע באוסף הזה.
הגיע הזמן לכתוב קוד!
מעדכנים את Flow
available in 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
. שימו לב: אם הסטטוס של currentUser
ישתנה (לדוגמה, אם תתבצע יציאה מהחשבון), יישלח Flow
חדש.
עכשיו צריך לוודא ש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 Performance Monitoring מציע מעקב מותאם אישית. כדי להוסיף עקבות מותאמים אישית ולמדוד את הביצועים בחלקים שונים של הקוד ב-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
) ב-codelab הזה:
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. הגדרת תצורה מרחוק
איזו תכונה אתה רוצה להוסיף?
יש מגוון רחב של תרחישי שימוש בהגדרת תצורה מרחוק, החל משינוי המראה של האפליקציה מרחוק ועד להגדרת התנהגויות שונות לפלחים שונים של משתמשים. ב-codelab הזה, תשתמשו בהגדרת תצורה מרחוק כדי ליצור מתג תכונה שיציג או יסתיר את התכונה החדשה עריכת משימה באפליקציית Make it So.
הגיע הזמן לכתוב קוד!
הדבר הראשון שצריך לעשות הוא ליצור את ההגדרה במסוף Firebase. כדי לעשות את זה, צריך לעבור אל מרכז הבקרה של הגדרת התצורה מרחוק וללחוץ על הלחצן הוספת פרמטר. ממלאים את השדות לפי התמונה שלמטה:
אחרי שממלאים את כל השדות, אפשר ללחוץ על הלחצן שמירה ואז על פרסום. אחרי שיצרתם את הפרמטר והוא זמין ב-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
. זו הדרך הטובה ביותר לוודא שהערכים העדכניים ביותר יהיו זמינים בכל המסכים כבר מההתחלה. אם תשנו את ממשק המשתמש או את אופן הפעולה של האפליקציה בשלב מאוחר יותר, כשהמשתמש באמצע פעולה כלשהי, חוויית המשתמש לא תהיה טובה.
הפונקציה השנייה מחזירה את הערך הבוליאני שפורסם עבור הפרמטר שיצרתם זה עתה ב-Console. כדי לאחזר את המידע הזה ב-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
, כשתלחצו על סמל שלוש הנקודות יופיעו שלוש אפשרויות.
אפשר לנסות לשנות את הערך כמה פעמים ב-Console ולהפעיל מחדש את האפליקציה. כך קל להשיק תכונות חדשות באפליקציה באמצעות הגדרת התצורה מרחוק.
7. מזל טוב
יצרתם בהצלחה אפליקציית Android באמצעות Firebase ו-Jetpack Compose.
הוספתם אימות ב-Firebase, מעקב אחר ביצועים, הגדרת תצורה מרחוק ו-Cloud Firestore לאפליקציית Android שנבנתה לחלוטין באמצעות Jetpack Compose לממשק המשתמש, והתאמתם אותה לארכיטקטורת MVVM המומלצת.
קריאה נוספת
- פיתוח אפליקציית Android באמצעות Firebase ו-Compose
- הוספת אימות ב-Firebase לאפליקציית Jetpack Compose
- הוספת Cloud Firestore לאפליקציית Jetpack Compose
- הוספת Coroutines ו-Flow לאפליקציית Android שנבנתה באמצעות Firebase ו-Compose
- הוספת מעקב אחרי ביצועים ב-Firebase לאפליקציית Jetpack Compose
- הוספת הגדרת תצורה מרחוק ב-Firebase לאפליקציית Jetpack Compose