איך לבנות Backend לאפליקציית אנדרואיד?
במאמר זה, נדון באנדרואיד – אחת ממערכות ההפעלה הפופולריות ביותר. נבחן את היתרונות והחסרונות של פיתוח לאנדרואיד, אפשרויות Backend ליישומים ניידים, ונלמד אתכם כיצד לבנות Backend נייד משלכם.
Contents
- 1 עיקרי הדברים
- 2 מהי אנדרואיד?
- 3 יתרונות הפיתוח לאנדרואיד
- 4 מגבלות הפיתוח לאנדרואיד
- 5 אפשרויות Backend לאפליקציות אנדרואיד
- 6 כיצד ליצור Backend עבור אפליקציית אנדרואיד?
- 7 סיכום
עיקרי הדברים
- הדומיננטיות של אנדרואיד: אנדרואיד, בתור מערכת הפעלה בקוד פתוח, מפעילה כ-70% ממכשירי המובייל ומחזיקה מעל שלושה מיליארד משתמשים פעילים.
- אפשרויות Backend לאפליקציות: עבור Backend לאפליקציות מובייל, מפתחים יכולים לבחור ב-Infrastructure as a Service (IaaS), Platform as a Service (PaaS) או Backend as a Service (BaaS).
- שלבי בניית ה-Backend שלכם: נציג הדרכה מפורטת כיצד לבנות Backend לאנדרואיד באמצעות פלטפורמת BaaS ונשתף את קוד המקור בסוף המאמר.
מהי אנדרואיד?
אנדרואיד היא מערכת הפעלה מבוססת לינוקס בקוד פתוח וחינמית. היא תוכננה במקור עבור מכשירים ניידים כגון סמארטפונים וטאבלטים, אך כיום משמשת גם לטלוויזיות חכמות, מערכות משובצות, קונסולות משחקים ועוד.
הפיתוח של אנדרואיד החל בשנת 2003 על ידי החברה Android Inc. בתחילה התכנון היה ליצור מערכת הפעלה למצלמות דיגיטליות, אך מהר מאוד עברו לפיתוח מערכת הפעלה לניידים כדי להגיע לשוק רחב יותר.
בשנת 2005, החברה נרכשה על ידי Google יחד עם עובדיה. הגרסה הראשונה של אנדרואיד יצאה בשנת 2008.
אף על פי שאנדרואיד היא בקוד פתוח, רוב המכשירים הניידים מריצים את הגרסה הקניינית של Google. הגרסה הזו מגיעה עם תוכנות מותקנות מראש כגון Google Chrome, YouTube, Google TV ו-Gmail.
בזכות אפשרויות ההתאמה האישית הרבות של אנדרואיד, יצרנים רבים גם מבצעים התאמה גרפית (skin) למערכת ההפעלה כדי לשקף טוב יותר את המותג שלהם. זו הסיבה שאנדרואיד של OnePlus נראית שונה למדי מזו של Pixel.
אנדרואיד היא מערכת ההפעלה הפופולרית ביותר מאז 2013. היא בשימוש בכ-70% ממכשירי המובייל ומחזיקה מעל שלושה מיליארד משתמשים פעילים מדי חודש.
מעבר לכך, Google Play Store מספקת גישה ליותר משלושה מיליון אפליקציות מובייל. המשיכו לקרוא כדי ללמוד כיצד ליצור Backend עבור אפליקציית אנדרואיד.
יתרונות הפיתוח לאנדרואיד
בואו נבחן כמה מהיתרונות של פיתוח עבור אנדרואיד.
חוצה פלטפורמות
כשאתם מפתחים לאנדרואיד, תוכלו לכוון למגוון רחב של מכשירים. זה כולל טלפונים ניידים, התקני לביש, טלוויזיות חכמות, קונסולות משחק ועוד. אם אתם יודעים כיצד לכתוב קוד לאפליקציה ניידת, לא תתקשו לפתח אפליקציה להתקני לביש או לטלוויזיה חכמה.
שוק עצום
כפי שהוצג קודם, לאנדרואיד נתח שוק גדול יותר של 70%, בהשוואה ל-30% של iOS. על ידי פיתוח אפליקציות לאנדרואיד, אתם מקבלים גישה אוטומטית לקהל רחב יותר.
מעבר לכך, אפליקציות אנדרואיד ידועות בהשקעה נמוכה ובתשואה גבוהה על ההשקעה (ROI). בשונה מ-iOS, אין דמי מפתחים שנתיים.
יכולת התאמה אישית
מכשירי אנדרואיד ניתנים להתאמה אישית גבוהה יותר בהשוואה למערכות הפעלה אחרות. ניתן לשנות כמעט כל דבר שניתן להעלות על הדעת.
יתרה מזאת, אנדרואיד מאפשרת לשלב את האפליקציה שלכם בקלות עם שירותים חיצוניים אחרים.
קהילה
מאחורי אנדרואיד עומדת קהילה עצומה של מפתחים בקוד פתוח. בנוסף, היא מגיעה עם כלים נוחים למפתחים כמו Android Studio, ADB ו-Logcat המאפשרים לכתוב אפליקציות בקלות.
אם אי פעם תיתקלו בבעיה, ישנם מספר פלטפורמות שבהן תוכלו לקבל סיוע, כגון GitHub, StackOverflow, Reddit, וקהילות נוספות המתמקדות באנדרואיד.
מגבלות הפיתוח לאנדרואיד
הנה כמה מהחסרונות של פיתוח לאנדרואיד.
אבטחה
אנדרואיד נחשבת לפחות מאובטחת מ-iOS ולא פועלת על פי פרוטוקולי אבטחה קפדניים. מכיוון שאנדרואיד היא בקוד פתוח, מתגלים בה פרצות אבטחה באופן שבועי, מה שמאפשר להאקרים לנצל את הפרצות לפני שהן מתוקנות.
ניתן גם לבצע ‘root’ במכשירי אנדרואיד כדי לקבל הרשאות סופר-משתמש. אף על פי שזה מעניק למכשיר שלכם יותר יכולות, זה גם מסוכן משום שהוא מבטל חלק מאמצעי האבטחה המובנים.
מורכבות
מכשירי אנדרואיד מגיעים במגוון רחב של צורות וגדלים, ובזמן שזה יתרון, זה יכול להיות חיסרון משמעותי כשמפתחים אפליקציות לאנדרואיד עבור סוגים רבים של מכשירים.
כדי לוודא שהאפליקציה שלכם תואמת לכמה שיותר מכשירים, עליכם להטמיע עיצוב רספונסיבי, להתחשב בחומרה השונה של המכשירים, ועוד.
אפליקציות פרימיום
אפליקציות פרימיום מצליחות פחות ב-Google Play Store בהשוואה ל-App Store. אין זה סוד שמשתמשי אנדרואיד נוטים להוציא פחות כסף על אפליקציות מאשר משתמשי iOS. אם אתם מתכננים אפליקציה בתשלום עיקרי, ייתכן שכדאי לשקול את אנדרואיד כאפשרות שנייה.
אפשרויות Backend לאפליקציות אנדרואיד
מהו ה-Backend הטוב ביותר לאפליקציות אנדרואיד? את ה-Backend לאפליקציות מובייל ניתן לארח בתשתית שלכם או לפרוס לשירותי ענן. המודלים הפופולריים ביותר בתחום מחשוב הענן עבור Backend למובייל כוללים:
- תשתית כשירות (IaaS)
- פלטפורמה כשירות (PaaS)
- Backend כשירות (BaaS)
כל אחת מהאפשרויות מכסה שכבות הפשטה שונות, כפי שמוצג בתמונה למטה.
בואו נבחן כל אפשרות בפירוט כדי לעזור לכם לבחור את מודל הענן המתאים ביותר ל-Backend שלכם.
תשתית כשירות (IaaS)
תשתית כשירות (IaaS) היא המודל הפחות מופשט מבין מודלי מחשוב הענן. במודל זה, הספק מציע למשתמשים משאבים וירטואליים, כגון שרתים, רשתות, אחסון ומערכות הפעלה, דרך ממשקי API ברמה גבוהה או לוחות בקרה ידידותיים.
היתרון הגדול של IaaS הוא שהיא מעניקה למשתמשים שליטה מלאה על התשתית כולה. IaaS היא המודל הגמיש והניתן להתרחבות ביותר, אך גם הקשה ביותר לניהול. אם תבחרו באפשרות זו, סביר להניח שתזדקקו לאנשי SysAdmin.
מספר דוגמאות ל-IaaS הן Amazon Web Services (AWS), Google Cloud Platform ו-Azure.
פלטפורמה כשירות (PaaS)
פלטפורמה כשירות (PaaS) היא מודל מחשוב ענן המיועד לסייע למשתמשים לפתח, לנהל ולפרוס אפליקציות.
מעבר לאספקת תשתית, PaaS כוללת כלים ידידותיים למפתחים לצורך פיתוח, התאמה ובדיקת אפליקציות.
באמצעות PaaS, תוכלו להתמקד בתוכנה ובחוויית המשתמש מבלי לדאוג לתשתית הבסיסית.
בנוסף, PaaS תבטיח שהאפליקציה שלכם תתרחב לפי הצורך, תטפל בגיבויים וכדומה. החסרונות של PaaS הם רמת שליטה נמוכה יותר, סיכון לנעילת ספק ועלויות גבוהות יותר יחסית.
הפלטפורמות הפופולריות ביותר ל-PaaS כוללות Heroku, Render ו-Digital Ocean App Platform.
Backend כשירות (BaaS)
Backend כשירות (BaaS) מאיץ את הפיתוח בצד השרת על ידי אספקת פתרון Backend מלא.
BaaS מעניק את כל היתרונות של IaaS ו-PaaS תוך מתן יכולות נוספות. צוותים שמשתמשים ב-BaaS נוטים להשיק מוצרים מהר יותר, לצמצם עלויות פיתוח ולבנות תוכנה איכותית יותר.
החסרונות של BaaS כוללים רמה נמוכה יותר של שליטה והתאמה אישית, וכן אפשרות של נעילת ספק עבור פלטפורמות שאינן בקוד פתוח.
דוגמאות ל-BaaS כוללות Back4app, AWS Amplify ו-Firebase.
כיצד ליצור Backend עבור אפליקציית אנדרואיד?
בקטע זה של המאמר, נבחן כיצד לבנות אפליקציית Backend המבוססת על Back4app ולהתחבר אליה מאפליקציית אנדרואיד.
דרישות מוקדמות
- ניסיון עם Kotlin ופיתוח לאנדרואיד.
- הבנה בסיסית של Mobile Backend as a Service.
- Java ו-Android Studio מותקנים על המחשב המקומי שלכם.
מהו Back4app?
Back4app היא פלטפורמה מצוינת לבניית Backend לאפליקציות מודרניות ולהאצת תהליך הפיתוח.
היא מצוידת במגוון תכונות צד שרת שימושיות, כגון ניהול משתמשים, מסדי נתונים בזמן אמת, אינטגרציות חברתיות, פונקציות Cloud Code, התראות Push, APIs ועוד!
על ידי שימוש ב-Back4app, תוכלו להעביר חלק ניכר מעבודת ה-Backend ולהתמקד בחלקים הקריטיים של האפליקציה שלכם, ובכך להאיץ את הפיתוח של אפליקציית אנדרואיד.
לא תצטרכו להתמודד עם התשתית הבסיסית של ה-Backend שלכם, שרתי ה-Backend, התרחבות, תחזוקה וכו’.
הכי חשוב, Back4app מציעה שכבת שימוש חינמית, המצוינת לניסויים ובדיקת הפלטפורמה. כשאפליקצייתכם תגדל, תוכלו לשדרג לתוכניות בתשלום חודשי.
הקדמה לפרויקט
במאמר זה, נבנה אפליקציית פתקים פשוטה. האפליקציה תאפשר למשתמשים להוסיף, לערוך ולמחוק פתקים. הפתקים יאוכסנו במסד הנתונים של Back4app ויטופלו באמצעות Parse SDK.
נשתמש בשפת התכנות Kotlin כדי לבנות את האפליקציה. עבור ממשק המשתמש, נשתמש ב-Jetpack Compose – הכלי המודרני של אנדרואיד לבניית ממשקים מקוריים.
יצירת אפליקציית Back4app
כחלק ראשון בבניית Backend לאפליקציית אנדרואיד, תזדקקו לחשבון Back4app. אם עדיין אין לכם, תוכלו להירשם כאן.
לאחר שתיכנסו לחשבון Back4app שלכם, תועברו למסך הצגת האפליקציות. לחצו על “Build new app” כדי להתחיל בתהליך יצירת האפליקציה.
הפלטפורמה של Back4app מציעה שני פתרונות:
- Backend as a Service (BaaS) – פתרון Backend מקיף
- Containers as a Service (CaaS) – פלטפורמה לניהול קונטיינרים (במיוחד ליישומי ווב)
מכיוון שאנו בונים Backend לאפליקציית מובייל, נבחר ב-“Backend as a Service”.
העניקו לאפליקציה שלכם שם ברור ומעניין, בחרו “NoSQL” כמסד הנתונים ולחצו על “Create”. זהו צעד חשוב לפריסת Backend לאפליקציית אנדרואיד.
Back4app תיקח מעט זמן להכין את כל מה שנדרש עבור האפליקציה שלכם, כולל מסד הנתונים, ניהול משתמשים, התרחבות, הגדרות וכו’. כשתהליך ההכנה יסתיים, תועברו למסך מסד הנתונים של האפליקציה.
עיצוב מסד הנתונים
נמשיך הלאה ונעצב את מסד הנתונים. זהו שלב הכרחי בבניית Backend לאפליקציית אנדרואיד.
מכיוון שאנו בונים אפליקציית פתקים פשוטה, נצטרך רק מחלקה אחת. לחצו על “Create a class”, תנו לה את השם Note
, אפשרו “Public Read and Write” ולחצו על “Create class & add columns”.
הוסיפו לאחר מכן את שלושת השדות הבאים למחלקה החדשה:
+-------------+-------------+--------------------+----------+
| Data type | Name | Default value | Required |
+-------------+-------------+--------------------+----------+
| String | icon | <leave blank> | yes |
+-------------+-------------+--------------------+----------+
| String | title | <leave blank> | yes |
+-------------+-------------+--------------------+----------+
| String | content | <leave blank> | yes |
+-------------+-------------+--------------------+----------+
לבסוף, לחצו על “Add row” והוסיפו נתוני דוגמה למסד הנתונים שלכם. אם אין לכם רעיונות, תוכלו גם לייבא dump זה.
מעולה, זה הכול בשלב הזה לגבי ה-Backend.
כתיבת הקוד בצד ה-Frontend
בקטע זה של המאמר, נתחיל פרויקט אנדרואיד חדש, נגדיר ViewModel, נממש את ממשק המשתמש, נתקין ונגדיר את Parse SDK, ולבסוף נשלוף נתונים ממסד הנתונים של Back4app בזמן אמת.
אתחול הפרויקט
כאמור בסעיף הדרישות המוקדמות, הצעדים הבאים ידרשו מכם להתקין Android Studio. אם טרם התקנתם אותו, הורידו אותו.
התחילו בפתיחת Android Studio ולחצו על “New Project” (פרויקט חדש).
לאחר מכן, בחרו “Empty Activity” כתבנית הפרויקט ולחצו “Next”.
כדי ליצור את הפרויקט, עליכם להזין שם ושם חבילה (Package Name). אני ממליץ להשתמש ב-AndroidApp
כשם הפרויקט ובפורמט reverse domain name כשם החבילה.
לאחר שהגדרתם הכול, לחצו “Finish” כדי ליצור את הפרויקט.
Android Studio ייקח בערך שתי דקות כדי להכין את כל הדרוש: מבנה הפרויקט, הגדרות Gradle, התקנת תלויות וכו’.
לאחר שהפרויקט שלכם מוכן, השתמשו בסייר הקבצים כדי לנווט ל-MainActivity.kt והחליפו את התוכן שלו:
// app/src/main/java.<your_package_name>/MainActivity.kt
package <your.package.name>
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.sp
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MaterialTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background,
) {
Text(text = "Back4app rocks!", fontSize = 18.sp)
}
}
}
}
}
ודאו שאתם תמיד מחליפים את
<your_package_name>
בשם החבילה האמיתי שלכם.
לבסוף, נסו להריץ את האפליקציה על ידי לחיצה על כפתור ההפעלה הירוק או על מקש Shift + F10
במקלדת. אם הכול עובר בהצלחה, האמולטור אמור להיפתח ותראו את ההודעה Back4app rocks
על המסך.
ViewModel
תפקידו של ViewModel הוא לנהל את המצב הגלובלי של האפליקציה. אך לפני כן, עלינו ליצור מחלקת נתונים בשם Note
שתכלול את אותם מאפיינים כמו במסד הנתונים של Back4app.
// app/src/main/java.<your_package_name>/Note.kt
package <your.package.name>
data class Note(
var objectId: String? = null,
var icon: String,
var title: String,
var content: String,
) {
companion object {
fun generateObjectId(): String {
val chars = ('a'..'z') + ('A'..'Z') + ('0'..'9')
return (1..10).map { chars.random() }.joinToString("")
}
}
}
- הגדרנו את המתודה הסטטית
generateObjectId()
, שמייצרת מזהים דומים ל-Parse. objectId
הוא שדה Nullable כיוון שבהמשך נניח שאםobjectId == null
, העצם עדיין לא נשמר במסד הנתונים.- כעת ניתן לגשת למופע של
AppViewModel
בפעילויות שלנו באמצעותAppViewModel.getInstance()
.
Activity ראשית (MainActivity)
נמשיך, החליפו את התוכן של MainActivity.kt בקוד הבא כדי להציג את הפתקים:
// app/src/main/java.<your_package_name>/MainActivity.kt
package <your.package.name>
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
class MainActivity : ComponentActivity() {
private val viewModel = AppViewModel.getInstance()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val context = this as Context
setContent {
MainActivityContent(
viewModel = viewModel,
onNoteListItemClick = {
// TODO: Open note edit form
},
onNoteAddClick = {
// TODO: Open note create form
},
)
}
}
}
@Composable
fun MainActivityContent(
viewModel: AppViewModel,
onNoteListItemClick: (note: Note) -> Unit,
onNoteAddClick: () -> Unit,
) {
val notes = viewModel.notes.value
MaterialTheme {
Scaffold(
topBar = { TopAppBar(title = { Text("My Notes") }) },
floatingActionButton = {
ExtendedFloatingActionButton(
onClick = { onNoteAddClick() },
icon = { Icon(Icons.Filled.Add, contentDescription = "Add") },
text = { Text("Add") }
)
},
) { contentPadding ->
Box(modifier = Modifier.padding(contentPadding)) {
NoteList(notes, onNoteListItemClick = { onNoteListItemClick(it) })
}
}
}
}
@Composable
fun NoteListItem(note: Note, onNoteListItemClick: (note: Note) -> Unit) {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = { onNoteListItemClick(note) })
.padding(16.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text(text = note.icon, fontSize = 32.sp, modifier = Modifier.size(48.dp))
Spacer(modifier = Modifier.width(8.dp))
Column {
Text(text = note.title, fontSize = 18.sp)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = note.content,
fontSize = 14.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
}
}
@Composable
fun NoteList(notes: Map<String, Note>, onNoteListItemClick: (note: Note) -> Unit) {
LazyColumn {
items(notes.entries.toList()) { (_, note) ->
NoteListItem(note = note, onNoteListItemClick = onNoteListItemClick)
}
}
}
- השדה
notes
נטען כעת מתוךAppViewModel
. - במקום להגדיר את אירועי הלחיצה בתוך ה-Composables עצמם, העברנו אותם כפרמטרים.
- השתמשנו ב-Compose כדי לעצב את הממשק. כדי להעניק לו מראה חדשני יותר, שילבנו
MaterialTheme
ו-Scaffold
.
אם תבנו מחדש את האפליקציה כעת, תראו רשימה של הפתקים ‘המובנים’ על המסך.
Activity לטופס הפתק (NoteForm)
בקטע זה נוסיף Activity חדש שיאפשר למשתמשים להוסיף ולערוך פתקים.
// app/src/main/java.<your_package_name>/FormActivity.kt
package <your.package.name>
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.Button
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
class FormActivity : ComponentActivity() {
private val viewModel = AppViewModel.getInstance()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val objectId = intent.getStringExtra("objectId")
val note = if (objectId !== null) viewModel.notes.value[objectId] else null
setContent {
FormActivityContent(
note,
onNoteAddClick = { icon: String, title: String, content: String ->
if (note !== null) return@FormActivityContent
val newNote = Note(
icon = icon,
title = title,
content = content,
)
newNote.objectId = Note.generateObjectId()
viewModel.notes.value += (newNote.objectId!! to newNote)
finish()
},
onNoteSaveClick = { icon: String, title: String, content: String ->
if (note === null) return@FormActivityContent
val updatedNote = note.copy()
updatedNote.icon = icon
updatedNote.title = title
updatedNote.content = content
viewModel.notes.value += (updatedNote.objectId!! to updatedNote)
finish()
},
onNoteDeleteClick = {
if (note === null) return@FormActivityContent
viewModel.notes.value = viewModel.notes.value.filter {
it.value.objectId != note.objectId
}
finish()
},
)
}
}
}
@Composable
fun FormActivityContent(
note: Note?,
onNoteAddClick: (icon: String, title: String, content: String) -> Unit,
onNoteSaveClick: (icon: String, title: String, content: String) -> Unit,
onNoteDeleteClick: () -> Unit,
) {
MaterialTheme {
Scaffold(
topBar = {
TopAppBar(title = { Text(note?.let { "Edit Note" } ?: ("Add Note")) })
},
floatingActionButton = {
if (note !== null) {
ExtendedFloatingActionButton(
onClick = { onNoteDeleteClick() },
icon = { Icon(Icons.Filled.Delete, "Delete") },
text = { Text("Delete") },
)
}
},
) { contentPadding ->
Box(modifier = Modifier.padding(contentPadding)) {
NoteForm(note = note, onNoteSave = { icon, title, content ->
if (note === null) {
onNoteAddClick(icon, title, content)
} else {
onNoteSaveClick(icon, title, content)
}
})
}
}
}
}
@Composable
fun NoteForm(
note: Note?,
onNoteSave: (icon: String, title: String, content: String) -> Unit
) {
var icon by remember { mutableStateOf(TextFieldValue(note?.icon ?: "")) }
var title by remember { mutableStateOf(TextFieldValue(note?.title ?: "")) }
var content by remember { mutableStateOf(TextFieldValue(note?.content ?: "")) }
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
) {
TextField(
label = { Text(text = "Icon") },
value = icon,
onValueChange = { icon = it },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
TextField(
label = { Text(text = "Title") },
value = title,
onValueChange = { title = it },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
TextField(
label = { Text(text = "Content") },
value = content,
onValueChange = { content = it },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
Button(
onClick = { onNoteSave(icon.text, title.text, content.text) },
Modifier.fillMaxWidth()
) {
Text(text = "Save")
}
}
}
- טופס זה משמש גם להוספה וגם לעריכה של פתקים. כדי לקבוע את הפעולה, אנו בודקים אם
objectId == null
. במקרה של null, מדובר בהוספה, אחרת בעריכה. - אם
objectId !== null
, אנו שולפים את הפתק מה-ViewModel וממלאים מראש את הטופס. - כדי ש-
MutableState
יפעל כראוי, אנו מתייחסים לתוכנו כבלתי משתנה (Immutable). לכן לפעמים אנחנו מעתיקים את התוכן במקום לשנותו ישירות. - ניהול מצב הטפסים ב-Compose מתבצע בעזרת
mutableStateOf()
.
הבא, רשמו את FormActivity
בקובץ AndroidManifest.xml בסוף תג <application>
.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application>
<!-- ... -->
<activity android:name=".FormActivity"/>
</application>
</manifest>
לבסוף, צרו Intent
חדש עבור פעולות הלחיצה onNoteListItemClick
ו-onNoteAddClick
ב-MainActivity.kt:
// app/src/main/java.<your_package_name>/MainActivity.kt
class MainActivity : ComponentActivity() {
// ...
override fun onCreate(savedInstanceState: Bundle?) {
// ...
setContent {
MainActivityContent(
viewModel = viewModel,
onNoteListItemClick = {
// triggers the edit action, since `objectId` is provided
val intent = Intent(context, FormActivity::class.java)
intent.putExtra("objectId", it.objectId)
context.startActivity(intent)
},
onNoteAddClick = {
// triggers the add action, since no `objectId` is present
val intent = Intent(context, FormActivity::class.java)
context.startActivity(intent)
},
)
}
}
}
אל תשכחו לייבא את Intent
בראש הקובץ:
import android.content.Intent
אם תבנו מחדש את האפליקציה, תגלו שיש לנו כעת אפליקציה עובדת לחלוטין. היא מאפשרת להוסיף פתקים, לערוך אותם ולמחוק אותם.
הבעיה היחידה היא שכאשר אנו מפעילים מחדש את האפליקציה, המצב שלה מתאפס (בחזרה לפתקים המוגדרים מראש).
בקטע הבא, נחבר את האפליקציה ל-Backend ב-Back4app, מה שיאפשר לנו לשמור את המצב ולסנכרן אותו בין מכשירים שונים.
התקנת Parse SDK
כפי שאתם ודאי יודעים, Back4app מבוסס על פלטפורמת Parse. אם אנו מעוניינים לעבוד עם מסד הנתונים של Back4app או עם הפלטפורמה בכללי, עלינו להתקין את Parse SDK.
התחילו בהוספת מאגר JitPack ל-settings.gradle:
// settings.gradle
pluginManagement {
repositories {
// ...
maven { url "https://jitpack.io" }
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
// ...
maven { url "https://jitpack.io" }
}
}
לאחר מכן, הוסיפו את Android Parse SDK ל-build.gradle ברמת האפליקציה:
// app/build.gradle
dependencies {
// ...
implementation "com.github.parse-community.Parse-SDK-Android:parse:4.2.0"
}
סנכרנו את הגדרות Gradle וודאו שאין שגיאות.
הגדרת Parse SDK
כדי להתחבר ל-Backend של Back4app, עליכם לספק ל-Parse SDK את מזהה האפליקציה (applicationId) ומפתח הלקוח (clientKey) שלכם. כדי להשיג אישורים אלו, עברו לאפליקציית Back4app שלכם ובחרו “App Settings > Security & Keys” בסרגל הצד.
לאחר מכן, הוסיפו אותם לקובץ strings.xml כך:
<!-- app/src/main/res/values/strings.xml -->
<resources>
<string name="app_name">back4app-android-app</string>
<string name="back4app_server_url">https://parseapi.back4app.com/</string>
<string name="back4app_app_id">your_parse_app_id</string>
<string name="back4app_client_key">your_parse_client_key</string>
</resources>
ודאו שאתם מחליפים את
your_parse_app_id
ואתyour_parse_client_key
בפרטי ההזדהות האמיתיים שלכם.
לאחר מכן, עדכנו את AndroidManifest.xml כדי לאפשר גישה לאינטרנט ולהוסיף את מטא הנתונים של האישורים:
<!-- app/src/main/AndroidManifest.xml -->
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<application>
<!-- ... -->
<meta-data
android:name="com.parse.SERVER_URL"
android:value="@string/back4app_server_url" />
<meta-data
android:name="com.parse.APPLICATION_ID"
android:value="@string/back4app_app_id" />
<meta-data
android:name="com.parse.CLIENT_KEY"
android:value="@string/back4app_client_key" />
</application>
</manifest>
הדבר האחרון שנותר הוא לאתחל את Parse. כדי לוודא ש-Parse מאותחל לפני כל Activity, ניצור מחלקה חדשה בשם App
, היורשת מ-Application
.
// app/src/main/java/<your_package_name>/App.kt
package <your.package.name>
import android.app.Application
import com.parse.Parse
class App : Application() {
override fun onCreate() {
super.onCreate()
Parse.initialize(
Parse.Configuration.Builder(this)
.applicationId(getString(R.string.back4app_app_id))
.clientKey(getString(R.string.back4app_client_key))
.server(getString(R.string.back4app_server_url))
.build()
)
}
}
לאחר מכן, רשמו את המחלקה App
בתוך AndroidManifest.xml:
<!-- app/src/main/AndroidManifest.xml -->
<application
android:name=".App"
...
>
<!-- ... -->
</application>
בנו מחדש את האפליקציה שוב ובדקו את חלון Logcat לאיתור שגיאות. אם אין שגיאות, סימן שההתחברות ל-Back4app הצליחה.
טעינת הפתקים מ-Back4app
הדבר האחרון שנותר לעשות לפני שהאפליקציה שלנו תהיה מוכנה לחלוטין הוא לטעון את הפתקים ממסד הנתונים של Back4app ולראות כיצד להשתמש ב-Parse בפועל.
התחילו בכך שתעברו ל-AppViewModel
. הסירו את הנתונים מה-Map והוסיפו את הבלוק init
:
// app/src/main/java/<your_package_name>/AppViewModel.kt
package <your.package.name>
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
class AppViewModel : ViewModel() {
val notes: MutableState<Map<String, Note>> = mutableStateOf(mapOf())
init {
val query = com.parse.ParseQuery.getQuery<com.parse.ParseObject>("Note")
query.orderByDescending("createdAt")
query.findInBackground { notes, e ->
if (e == null) {
for (parseNote in notes) {
val note = Note(
objectId = parseNote.objectId,
icon = parseNote.getString("icon")!!,
title = parseNote.getString("title")!!,
content = parseNote.getString("content")!!,
)
this.notes.value += (note.objectId!! to note)
}
} else {
println("Error: ${e.message}")
}
}
}
companion object {
@Volatile
private var instance: AppViewModel? = null
fun getInstance(): AppViewModel {
return instance ?: synchronized(this) {
instance ?: AppViewModel().also { instance = it }
}
}
}
}
קוד זה משתמש ב-Parse SDK כדי לשלוף את הפתקים מהמסד, להמיר אותם למחלקת הנתונים Note
ולשמור אותם במפה.
לאחר מכן, הוסיפו שלוש מתודות אלו ל-Note
:
// app/src/main/java/<your_package_name>/Note.kt
package <your.package.name>
import com.parse.ParseObject
import com.parse.ParseQuery
data class Note(
var objectId: String? = null,
var icon: String,
var title: String,
var content: String,
) {
fun addToParse(callback: (objectId: String) -> Unit) {
if (objectId !== null) throw Exception("Note is already saved to Parse!")
val parseNote = ParseObject("Note")
parseNote.put("icon", icon)
parseNote.put("title", title)
parseNote.put("content", content)
parseNote.saveInBackground {
if (it !== null) throw Exception("Error: ${it.message}")
objectId = parseNote.objectId
callback(parseNote.objectId)
}
}
fun updateToParse(callback: (objectId: String) -> Unit) {
if (objectId === null) throw Exception("Note hasn't been saved to Parse yet!")
val query = ParseQuery.getQuery<ParseObject>("Note")
val parseNote = query.get(objectId)
parseNote.put("icon", icon)
parseNote.put("title", title)
parseNote.put("content", content)
parseNote.saveInBackground {
if (it !== null) throw Exception("Error: ${it.message}")
callback(parseNote.objectId)
}
}
fun deleteFromParse(callback: () -> Unit) {
if (objectId === null) throw Exception("Note hasn't been saved to Parse yet!")
val query = ParseQuery.getQuery<ParseObject>("Note")
val parseNote = query.get(objectId)
parseNote.deleteInBackground {
if (it !== null) throw Exception("Error: ${it.message}")
callback()
}
}
}
addToParse()
מוסיפה פתק חדש לשרת Parse עם פרטיו. אם הפעולה מצליחה, נשמר מזהה ייחודי לפתק ומתקבלת התראה שהפעולה הסתיימה.updateToParse()
מעדכנת מידע של פתק קיים בשרת Parse. אם הפעולה מצליחה, מתקבלת התראה בסיום.deleteFromParse()
מוחקת פתק שמור משרת Parse. אם הפעולה מצליחה, מתקבלת התראה על השלמת המחיקה.
לבסוף, עדכנו את מתודות הלחיצה ב-FormActivity
כך שיקראו למתודות שהגדרנו זה עתה:
// app/src/main/java/<your_package_name>/NoteFormActivity.kt
class FormActivity : ComponentActivity() {
// ...
override fun onCreate(savedInstanceState: Bundle?) {
// ...
setContent {
FormActivityContent(
note,
onNoteAddClick = { icon: String, title: String, content: String ->
if (note !== null) return@FormActivityContent
val newNote = Note(
icon = icon,
title = title,
content = content,
)
newNote.addToParse {
viewModel.notes.value += (it to newNote)
finish()
}
},
onNoteSaveClick = { icon: String, title: String, content: String ->
if (note === null) return@FormActivityContent
val updatedNote = note.copy()
updatedNote.icon = icon
updatedNote.title = title
updatedNote.content = content
updatedNote.updateToParse {
viewModel.notes.value += (it to updatedNote)
finish()
}
},
onNoteDeleteClick = {
if (note === null) return@FormActivityContent
viewModel.notes.value = viewModel.notes.value.filter {
it.value.objectId != note.objectId
}
note.deleteFromParse {
finish()
}
},
)
}
}
}
וזהו!
כעת האפליקציה פועלת במלואה ומסונכרנת עם ה-Backend של Back4app. נסו לשחק עם הנתונים ולראות אם השינויים משתקפים בצפייה במסד הנתונים.
סיכום
במאמר זה, בנינו בהצלחה Backend לאפליקציה ניידת והתחברנו אליו מתוך אפליקציית אנדרואיד. השתמשנו בפתרון BaaS של Back4app עבור ה-Backend, ובצד ה-Frontend, עבדנו עם Kotlin ו-Jetpack Compose.
עד עכשיו, אמור להיות לכם מושג טוב לגבי אופן הפעולה של Backend למובייל, ותוכלו לבנות אחד בעצמכם. ניתן לצפות בקוד המקור הסופי ב-מאגר GitHub.