Wie erstellt man ein Backend für eine Android-App?

Back4app Android App Abdeckung

In diesem Artikel sprechen wir über Android – eines der beliebtesten Betriebssysteme. Wir befassen uns mit den Vor- und Nachteilen der Android-Entwicklung, Backend-Optionen für mobile Anwendungen und zeigen Ihnen, wie Sie Ihr eigenes mobiles Backend erstellen können.

Wichtigste Erkenntnisse

  • Die Dominanz von Android: Android ist ein Open-Source-Betriebssystem, das auf rund 70 % aller mobilen Geräte mit über drei Milliarden aktiven Nutzern zum Einsatz kommt.
  • Backend-Optionen für Apps: Für mobile App-Backends können sich Entwickler für Infrastructure as a Service (IaaS), Platform as a Service (PaaS) oder Backend as a Service (BaaS) entscheiden.
  • Schritte zur Erstellung Ihres Backends: Am Ende des Artikels finden Sie eine ausführliche Anleitung, wie Sie ein Android-Backend mit einer BaaS-Plattform und dem Quellcode erstellen können.

Was ist Android?

Android ist ein freies und quelloffenes Linux-basiertes Betriebssystem. Es wurde in erster Linie für mobile Geräte wie Smartphones und Tablets entwickelt, wird aber inzwischen auch für Smart-TVs, eingebettete Systeme, Spielkonsolen usw. verwendet.

Die Entwicklung von Android begann im Jahr 2003 durch Android Inc. Das Unternehmen wollte ursprünglich ein Betriebssystem für Digitalkameras entwickeln, verlegte sich aber schnell auf die Entwicklung mobiler Betriebssysteme, um einen breiteren Markt zu erreichen.

Im Jahr 2005 wurde das Unternehmen mitsamt seinen Mitarbeitern von Google übernommen. Die erste Version von Android wurde im Jahr 2008 veröffentlicht.

Obwohl Android ein Open-Source-Betriebssystem ist, läuft auf den meisten Mobilgeräten die proprietäre Version von Google. Auf dieser Version ist Software wie Google Chrome, YouTube, Google TV und Gmail vorinstalliert.

Aufgrund der großartigen Anpassungsmöglichkeiten von Android passen viele Hersteller auch Android an, um ihr Unternehmen besser zu repräsentieren. Aus diesem Grund sieht OnePlus Android ganz anders aus als Pixel Android.

Android ist seit 2013 das beliebteste Betriebssystem. Es wird von rund 70 % der mobilen Geräte verwendet und hat über drei Milliarden monatlich aktive Nutzer.

Darüber hinaus bietet Ihnen der Google Play Store Zugang zu mehr als drei Millionen mobilen Anwendungen. Lesen Sie weiter, um zu erfahren, wie Sie ein Backend für eine Android-App erstellen.

Vorteile der Android-Entwicklung

Lassen Sie uns einige der Vorteile der Entwicklung für Android erkunden.

Plattformübergreifend

Durch die Entwicklung für Android können Sie eine breite Palette von Geräten ansprechen. Dazu gehören Mobiltelefone, Wearables, Smart-TVs, Spielkonsolen und mehr. Wenn Sie wissen, wie man eine mobile App programmiert, werden Sie kein Problem haben, eine App für Wearables oder Smart-TVs zu entwickeln.

Enormer Markt

Wie bereits erwähnt, hat Android mit 70 % einen größeren Marktanteil als iOS mit 30 %. Wenn Sie Android-Apps entwickeln, erhalten Sie automatisch Zugang zu einem breiteren Publikum.

Darüber hinaus sind Android-Apps für ihre geringen Investitionen und ihre hohe Rentabilität bekannt. Im Gegensatz zu iOS fallen auch keine jährlichen Entwicklergebühren an.

Anpassungsfähigkeit

Android-Geräte sind im Vergleich zu anderen Betriebssystemen sehr anpassbar. Sie ermöglichen es Ihnen, praktisch alles zu ändern, was Sie sich vorstellen können.

Außerdem können Sie Ihre App unter Android problemlos in andere Dienste von Drittanbietern integrieren.

Gemeinschaft

Android wird von einer großen Gemeinschaft von Open-Source-Entwicklern unterstützt. Außerdem wird es mit vielen entwicklerfreundlichen Tools wie Android Studio, ADB und Logcat geliefert, mit denen Sie ganz einfach Anwendungen programmieren können.

Sollten Sie einmal nicht weiterkommen, gibt es mehrere Plattformen, auf denen Sie Hilfe finden können, darunter GitHub, StackOverflow, Reddit und andere Android-Communitys.

Beschränkungen der Android-Entwicklung

Hier sind einige der Nachteile der Android-Entwicklung.

Sicherheit

Android ist weniger sicher als iOS und hält sich nicht an strenge Sicherheitsprotokolle. Da Android Open-Source ist, werden wöchentlich Sicherheitslücken entdeckt. Dies gibt Hackern die Möglichkeit, diese Schwachstellen auszunutzen, bevor sie gepatcht werden.

Android-Geräte können auch verwurzelt werden, um Superuser-Zugriff zu erhalten. Dies macht Ihr Gerät zwar leistungsfähiger, ist aber auch riskant, da einige integrierte Sicherheitsmaßnahmen deaktiviert werden.

Komplexität

Android-Geräte gibt es in verschiedenen Formen und Größen. Das ist zwar großartig, kann aber auch ein großer Nachteil sein, wenn man Android-Apps für verschiedene Gerätetypen entwickelt.

Um sicherzustellen, dass Ihre App mit so vielen Geräten wie möglich kompatibel ist, müssen Sie ein responsives Design einführen, die Hardware der verschiedenen Geräte berücksichtigen und so weiter.

Premium-Anwendungen

Premium-Apps sind im Google Play Store weniger erfolgreich als im App Store. Es ist kein Geheimnis, dass Android-Nutzer tendenziell weniger für Apps ausgeben als iOS-Nutzer. Wenn Sie an einer Premium-Anwendung arbeiten, sollten Sie Android als Ihre zweite Wahl betrachten.

Backend-Optionen für Android-Anwendungen

Welches ist das beste Backend für eine Android-Anwendung? Backends für mobile Anwendungen können in Ihrer eigenen Infrastruktur gehostet oder in Cloud-Diensten bereitgestellt werden. Zu den beliebtesten Cloud-Computing-Modellen für mobile Backends gehören:

  1. Infrastruktur als Dienstleistung (IaaS)
  2. Plattform als Dienstleistung (PaaS)
  3. Backend als Dienstleistung (BaaS)

Jede Option deckt verschiedene Abstraktionsebenen ab, wie in der folgenden Abbildung dargestellt.

IaaS vs PaaS vs BaaS Abstraktionsschichten

Schauen wir uns jede Option im Detail an, damit Sie das am besten geeignete Cloud-Modell für Ihre Backend-Anwendungen auswählen können.

Infrastruktur als Dienstleistung (IaaS)

Infrastructure as a Service (IaaS) ist das am wenigsten abstrahierte Cloud-Computing-Modell. Bei diesem Modell bietet der Anbieter den Nutzern virtualisierte Ressourcen wie Server, Netzwerke, Speicher und Betriebssysteme über hochentwickelte APIs oder intuitive Dashboards an.

Das Beste an IaaS ist, dass es den Nutzern die vollständige Kontrolle über ihre gesamte Infrastruktur gibt. IaaS ist das flexibelste und skalierbarste Modell, aber auch das am schwierigsten zu verwaltende. Wenn Sie sich für diese Option entscheiden, benötigen Sie höchstwahrscheinlich einen oder zwei SysAdmins.

Einige Beispiele für IaaS sind Amazon Web Services, Google Cloud Platform und Azure.

Plattform als Dienstleistung (PaaS)

Platform as a Service (PaaS) ist ein Cloud-Computing-Modell, das Benutzern bei der Entwicklung, Verwaltung und Bereitstellung von Anwendungen helfen soll.

PaaS bietet nicht nur eine Infrastruktur, sondern auch benutzerfreundliche Tools für die Entwicklung, Anpassung und das Testen von Anwendungen.

Durch die Nutzung von PaaS können Sie sich auf Ihre Software und das Benutzererlebnis konzentrieren, ohne sich um die zugrunde liegende Infrastruktur kümmern zu müssen.

Außerdem stellt PaaS sicher, dass Ihre Anwendung bei Bedarf skaliert wird, kümmert sich um die Backups usw. Die Nachteile von PaaS sind ein geringeres Maß an Kontrolle, das Risiko der Anbieterbindung und vergleichsweise höhere Kosten.

Zu den beliebtesten PaaS-Plattformen gehören Heroku, Render und Digital Ocean App Platform.

Backend als Dienstleistung (BaaS)

Backend as a Service (BaaS) automatisiert den Backend-Teil der Entwicklung durch Bereitstellung einer vollwertigen Backend-Lösung.

Zu den BaaS-Funktionen gehören Benutzerverwaltung, Datenbanken, Authentifizierung, soziale Integrationen, Push-Benachrichtigungen, APIs, SDKs und mehr.

BaaS bietet Ihnen alle Vorteile von IaaS und PaaS und bietet gleichzeitig zusätzliche Funktionen. Teams, die BaaS nutzen, bringen ihre Produkte in der Regel schneller auf den Markt, senken die Entwicklungskosten und entwickeln bessere Software.

Zu den Nachteilen von BaaS gehören ein geringeres Maß an Kontrolle und Anpassung sowie die Möglichkeit einer Anbieterbindung bei nicht quelloffenen Plattformen.

BaaS-Beispiele sind Back4app, AWS Amplify und Firebase.

Wie erstellt man ein Backend für eine Android-App?

In diesem Artikel sehen wir uns an, wie man eine Back4app-basierte Backend-App erstellt und sich von einer Android-Anwendung aus mit ihr verbindet.

Voraussetzungen

Was ist Back4app?

Back4app ist eine hervorragende Plattform, um Backends für moderne Anwendungen zu erstellen und den Entwicklungsprozess zu beschleunigen.

Es ist mit nützlichen serverseitigen Funktionen wie Benutzerverwaltung, Echtzeit-Datenbanken, sozialen Integrationen, Cloud-Code-Funktionen, Push-Benachrichtigungen, APIs und vielem mehr ausgestattet!

Durch den Einsatz von Back4app können Sie einen Großteil der Backend-Arbeiten auslagern und sich auf die kritischen Aspekte Ihrer Anwendung konzentrieren und die Entwicklung einer Android-App im Backend beschleunigen.

Sie müssen sich nicht mit der zugrundeliegenden Infrastruktur Ihres Backends, dem Backend-Server, der Skalierung, der Wartung usw. befassen.

Am wichtigsten ist, dass Back4app eine kostenlose Stufe anbietet, die sich hervorragend zum Experimentieren und Ausprobieren der Plattform eignet. Wenn Ihre App wächst, können Sie gegen eine monatliche Gebühr auf Premium-Pläne upgraden.

Projekt-Einführung

In diesem Artikel werden wir eine einfache Notizen-App erstellen. Mit der App können Benutzer Notizen hinzufügen, bearbeiten und löschen. Die Notizen werden in der Back4app-Datenbank gespeichert und über das Parse SDK bearbeitet.

Wir werden die Programmiersprache Kotlin verwenden, um die App zu erstellen. Für die Benutzeroberfläche verwenden wir Jetpack Compose – das moderne Android-Toolkit zur Erstellung nativer Benutzeroberflächen.

Back4app Android Notizen App

Back4app App erstellen

Als ersten Schritt zur Entwicklung eines Backends für eine Android-App benötigen Sie ein Back4app-Konto. Wenn Sie noch keins haben, melden Sie sich an.

Wenn Sie sich bei Ihrem Back4app-Konto anmelden, werden Sie zur App-Ansicht weitergeleitet. Klicken Sie auf “Neue App erstellen”, um den App-Erstellungsprozess zu starten.

Back4app Neue App erstellen

Die Back4app-Plattform bietet zwei Lösungen:

  1. Backend as a Service (BaaS) — robuste Backend-Lösung
  2. Containers as a Service (CaaS) – Plattform für die Verwaltung von Containern (insbesondere Webanwendungen)

In Anbetracht der Tatsache, dass wir ein Backend für eine mobile App erstellen, entscheiden wir uns für “Backend as a Service”.

Back4app BaaS Lösung

Geben Sie Ihrer App einen schönen und informativen Namen, wählen Sie “NoSQL” als Datenbank und klicken Sie auf “Erstellen”. Das ist ein wichtiger Schritt für die Bereitstellung eines Backends für eine Android-App.

Back4app benötigt eine gewisse Zeit, um alles für Ihre Anwendung vorzubereiten. Dazu gehören die Datenbank, die Benutzerverwaltung, die Skalierung, die Konfigurationen und so weiter. Sobald Ihre Anwendung fertig ist, werden Sie zur Datenbankansicht Ihrer Anwendung weitergeleitet.

Back4app Datenbank Ansicht

Entwerfen Sie die Datenbank

Lassen Sie uns nun die Datenbank entwerfen. Dies ist ein notwendiger Schritt zur Entwicklung eines Backends für eine Android-App.

Da wir eine einfache Notizen-App erstellen, benötigen wir nur eine Klasse. Klicken Sie auf “Klasse erstellen”, nennen Sie sie Note, aktivieren Sie “Public Read and Write” und klicken Sie auf “Klasse erstellen und Spalten hinzufügen”.

Back4app Datenbank Klasse erstellen

Fügen Sie anschließend die folgenden drei Felder zu der neu erstellten Klasse hinzu:

+-------------+-------------+--------------------+----------+
| Data type   | Name        | Default value      | Required |
+-------------+-------------+--------------------+----------+
| String      | icon        | <leave blank>      | yes      |
+-------------+-------------+--------------------+----------+
| String      | title       | <leave blank>      | yes      |
+-------------+-------------+--------------------+----------+
| String      | content     | <leave blank>      | yes      |
+-------------+-------------+--------------------+----------+

Klicken Sie abschließend auf die Schaltfläche “Zeile hinzufügen” und füllen Sie Ihre Datenbank mit einigen Beispieldaten auf. Wenn Ihnen nichts einfällt, können Sie diesen Daten-Dump auch importieren.

Back4app Datenbank befüllen

Gut, das war’s mit dem Backend.

Code Frontend

In diesem Artikel werden wir eine neue Android-App booten, ein ViewModel einrichten, die Benutzeroberfläche implementieren, Parse SDK installieren und konfigurieren und schließlich Daten aus der Back4app-Echtzeitdatenbank abrufen.

Init-Projekt

Wie im Abschnitt “Voraussetzungen” erwähnt, müssen Sie für die folgenden Schritte Android Studio installiert haben. Wenn Sie es noch nicht haben, laden Sie es herunter.

Öffnen Sie zunächst Android Studio und klicken Sie auf die Schaltfläche “Neues Projekt”.

Android Studio-Index

Wählen Sie dann “Leere Aktivität” als Projektvorlage und klicken Sie auf “Weiter”.

Um Ihr Projekt zu erstellen, müssen Sie einen Namen und einen Paketnamen angeben. Ich schlage vor, dass Sie AndroidApp als Projektnamen und den Namen der umgekehrten Domäne als Paketnamen für Ihr Projekt verwenden.

Nachdem Sie alles konfiguriert haben, klicken Sie auf “Fertigstellen”, um das Projekt zu erstellen.

Android Studio Projekteinstellungen

Android Studio benötigt etwa zwei Minuten, um alles Notwendige vorzubereiten. Dazu gehören die Erstellung der Projektdateistruktur, die Einrichtung von Gradle, die Installation von Abhängigkeiten usw.

Sobald Ihr Projekt fertig ist, verwenden Sie den Explorer, um zu MainActivity.kt zu navigieren und seinen Inhalt zu ersetzen:

// 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)
                }
            }
        }
    }
}

Stellen Sie sicher, dass Sie immer durch Ihren tatsächlichen Paketnamen ersetzen.

Versuchen Sie schließlich, die App zu starten, indem Sie auf den grünen Play-Button oder die Tastenkombination Shift + F10 auf Ihrer Tastatur klicken. Wenn alles gut geht, sollte der Emulator starten und Sie sollten die Back4app-Rocks-Meldung auf dem Bildschirm sehen.

Erste Android-App

ViewModel

Um den globalen Zustand unserer App zu verwalten, werden wir ein ViewModel verwenden. Zuvor müssen wir jedoch eine Note-Datenklasse mit denselben Attributen wie in unserer Back4app-Datenbank erstellen.

Erstellen Sie eine Datenklasse mit dem Namen Note:

// 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("")
        }
    }
}
  1. Wir haben die statische Methode generateObjectId() definiert, um Parse-ähnliche Objekt-IDs zu erzeugen.
  2. objectId ist nullbar, da wir später davon ausgehen, dass das Objekt noch nicht in der Datenbank gespeichert wurde, wenn objectId == null ist.

Als nächstes erstellen Sie eine AppViewModel-Klasse mit dem folgenden Inhalt:

// 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(
        "7IggsqFAKt" to Note(
            objectId = "7IggsqFAKt",
            icon = "\uD83D\uDE80",
            title = "Deploy app",
            content = "Go ahead and deploy your backend to Back4app.",
        ),
        "eFRNm0hTat" to Note(
            objectId = "eFRNm0hTat",
            icon = "\uD83C\uDFA8",
            title = "Design website",
            content = "Design the website for the conference.",
        ),
        "uC7hTQmG5F" to Note(
            objectId = "uC7hTQmG5F",
            icon = "\uD83D\uDC42",
            title = "Attend meeting",
            content = "Attend meeting with the team to discuss the conference.",
        ),
    ))

    companion object {
        @Volatile
        private var instance: AppViewModel? = null

        fun getInstance(): AppViewModel {
            return instance ?: synchronized(this) {
                instance ?: AppViewModel().also { instance = it }
            }
        }
    }
}
  1. Wir haben Compose’s MutableState verwendet, um UI-Updates im Falle von Datenänderungen auszulösen.
  2. Der MutableState enthält eine Map mit ObjectIdsund Notes, die wir mit Daten gefüllt haben.
  3. Um sicherzustellen, dass es nur eine einzige Instanz von AppViewModel gibt, haben wir das Singleton-Muster verwendet.

Wir können nun auf die AppViewModel-Instanz in unseren Aktivitäten über AppViewModel.getInstance() zugreifen.

Haupttätigkeit

Ersetzen Sie den Inhalt von MainActivity.kt durch den folgenden Code, um die Notizen anzuzeigen:

// 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.activity.viewModels
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)
        }
    }
}
  1. Die Notizen werden jetzt aus dem AppViewModel geholt.
  2. Anstatt die Klick-Ereignisse in Composables zu definieren, haben wir sie als Argumente weitergegeben.
  3. Wir haben Compose verwendet, um die Benutzeroberfläche zu gestalten. Um die Oberfläche glatter zu gestalten, haben wir MaterialTheme in das Scaffold-Layout integriert.

Wenn Sie die App jetzt neu erstellen, sollten Sie die Liste der “fest codierten” Notizen auf dem Bildschirm sehen.

Back4app Notes App-Liste

Notizformular Aktivität

In diesem Abschnitt fügen wir eine neue Aktivität hinzu, die es den Benutzern ermöglicht, Notizen hinzuzufügen und zu bearbeiten.

Beginnen Sie mit der Erstellung einer neuen Klasse namens FormActivity mit folgendem Inhalt:

// 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")
        }
    }
}
  1. Dieses Formular wird sowohl zum Hinzufügen als auch zum Bearbeiten von Notizen verwendet. Um die Aktion zu bestimmen, vergleichen wir objectId == null. Im Falle von null handelt es sich um eine Hinzufügung, ansonsten um eine Bearbeitung.
  2. Wenn objectId !== null ist, holen wir die Notiz aus dem Status und füllen das Formular aus.
  3. Damit MutableState korrekt funktioniert, mussten wir seinen Inhalt als unveränderlich behandeln. Deshalb haben wir manchmal den Inhalt kopiert, anstatt ihn zu ändern.
  4. Wir haben den Zustand des Compose-Formulars über mutableStateOf() behandelt.

Als nächstes registrieren Sie die FormActivity am unteren Ende der Anwendung in 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">

    <application>
        <!-- ... -->
        <activity android:name=".FormActivity"/>
    </application>

</manifest>

Erstellen Sie schließlich ein neues Intent auf onNoteListItemClick und onNoteAddClick in 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)
                },
            )
        }
    }
}

Vergessen Sie nicht, Intent am Anfang der Datei zu importieren:

import android.content.Intent

Wenn Sie die Anwendung neu erstellen, werden Sie feststellen, dass wir jetzt eine funktionierende Anwendung haben. Die Anwendung ermöglicht es uns, Notizen hinzuzufügen, sie zu bearbeiten und sie zu löschen.

Das einzige Problem ist, dass ein Neustart der App ihren Status zurücksetzt (auf die “hart kodierten” Notizen).

Im nächsten Abschnitt werden wir die App mit dem Back4app-Backend verbinden. So können wir den Status beibehalten und ihn zwischen mehreren Geräten synchronisieren.

Android Notes App Bearbeitungsvorschau

Parse SDK installieren

Wie Sie vielleicht wissen, basiert Back4app auf der Parse-Plattform. Wenn wir mit der Back4app-Datenbank oder Back4app im Allgemeinen interagieren wollen, müssen wir das Parse SDK installieren.

Beginnen Sie damit, das JitPack-Repository zu settings.gradle hinzuzufügen:

// settings.gradle

pluginManagement {
    repositories {
        // ...
        maven { url "https://jitpack.io" }
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        // ...
        maven { url "https://jitpack.io" }
    }
}

Als nächstes fügen Sie das Android Parse SDK zur build.gradle der App-Ebene hinzu:

// app/build.gradle

dependencies {
    // ...
    implementation "com.github.parse-community.Parse-SDK-Android:parse:4.2.0"
}

Synchronisieren Sie die Gradle-Einstellungen und stellen Sie sicher, dass keine Fehler vorhanden sind.

Parse SDK konfigurieren

Um sich mit dem Back4app-Backend zu verbinden, müssen Sie dem Parse SDK Ihre Anwendungs-ID und Ihren Client-Schlüssel mitteilen. Um Ihre Anmeldedaten zu erhalten, navigieren Sie zu Ihrer Back4app-App und wählen Sie “App Settings > Security & Keys” in der Seitenleiste.

Fügen Sie sie dann wie folgt zu Ihrer strings.xml hinzu:

<!-- 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>

Stellen Sie sicher, dass Sie your_parse_app_id und your_parse_client_key durch Ihre Anmeldedaten ersetzen.

Ändern Sie als Nächstes Ihre AndroidManifest.xml, um den Internetzugang zu aktivieren und die Metadaten für die Anmeldedaten anzuhängen:

<!-- 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">

    <!-- these two permissions enable internet access -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />

    <application>
        <!-- ... -->

        <!-- newly added metadata -->
        <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>

Das letzte, was wir tun müssen, ist die Initialisierung von Parse. Um sicherzustellen, dass Parse vor allen anderen Aktivitäten initialisiert wird, erstellen wir eine neue Klasse namens App, die von der Klasse Application erbt.

Erstellen Sie App.kt und initialisieren Sie Parse wie folgt:

// 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()
        )
    }
}

Registrieren Sie dann die App-Klasse in AndroidManifest.xml:

<!-- app/src/main/AndroidManifest.xml -->

<application
    android:name=".App"
    ...
>
    <!-- ... -->
</application>

Erstellen Sie die Anwendung erneut und überprüfen Sie das Logcat-Fenster auf Fehler. Wenn es keine Fehler gibt, war die Verbindung zu Back4app erfolgreich.

Notizen von Back4app laden

Das letzte, was wir vor der Fertigstellung unserer App tun müssen, ist, die Notizen aus der Back4app-Datenbank zu laden und uns anzusehen, wie man Parse in der Praxis einsetzt.

Navigieren Sie zunächst zum AppViewModel. Entfernen Sie die Daten aus der Karte und fügen Sie den Init-Block hinzu:

// 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 {
        // ...
    }
}

Dieser Code verwendet Parse SDK, um die Notizen aus den Datenbanken zu holen, sie in die Datenklasse "Note" umzuwandeln und sie in der Karte zu speichern.

Fügen Sie dann die folgenden drei Methoden zu Note hinzu:

// 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()
        }
    }
}
  1. addToParse() fügt dem Parse-Server eine neue Notiz mit ihren Details hinzu. Bei Erfolg speichert sie den eindeutigen Bezeichner der Notiz und benachrichtigt, wenn sie fertig ist.
  2. updateToParse() aktualisiert die Informationen zu einer bestehenden Notiz auf dem Parse-Server. Im Erfolgsfall meldet sie nach den Änderungen den Abschluss.
  3. deleteFromParse() löscht eine gespeicherte Notiz vom Parse-Server. Im Erfolgsfall wird die Löschung nach Beendigung bestätigt.

Schließlich ändern Sie die Klickmethoden in FormActivity, um die neu definierten Methoden aufzurufen:

// 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()
                    }
                },
            )
        }
    }
}

Das war’s!

Die Anwendung ist nun voll funktionsfähig und mit dem Back4app-Backend synchronisiert. Versuchen Sie, mit der Anwendung zu spielen, indem Sie die Daten ändern und prüfen, ob die Änderungen in der Datenbankansicht wiedergegeben werden.

Schlussfolgerung

In diesem Artikel haben wir erfolgreich ein mobiles Backend gebaut und uns von unserer Android-App aus mit diesem verbunden. Für das Backend haben wir die BaaS-Lösung von Back4app verwendet, und für das Frontend haben wir Kotlin mit Jetpack Compose eingesetzt.

Inzwischen sollten Sie ein gutes Verständnis dafür haben, wie mobile Backends funktionieren und in der Lage sein, Ihre eigenen zu erstellen. Sehen Sie sich den endgültigen Quellcode auf dem back4app-android-app-Repositorium an.


Leave a reply

Your email address will not be published.