Come costruire un backend per un’applicazione Android?

Back4app Copertina per applicazioni Android

In questo articolo parleremo di Android, uno dei sistemi operativi più diffusi. Analizzeremo i pro e i contro dello sviluppo Android, le opzioni di backend per le applicazioni mobili e vi insegneremo a costruire il vostro backend mobile.

Punti di forza

  • Il dominio di Android: Android, come sistema operativo open-source, alimenta circa il 70% dei dispositivi mobili con oltre tre miliardi di utenti attivi.
  • Opzioni di backend per le app: Per i backend delle app mobili, gli sviluppatori possono optare per Infrastructure as a Service (IaaS), Platform as a Service (PaaS) o Backend as a Service (BaaS).
  • Passi per costruire il backend: Alla fine dell’articolo forniremo un tutorial dettagliato su come realizzare un backend Android utilizzando una piattaforma BaaS e il codice sorgente.

Che cos’è Android?

Android è un sistema operativo gratuito e open-source basato su Linux. È stato progettato principalmente per dispositivi mobili come smartphone e tablet, ma ora viene utilizzato anche per smart TV, sistemi embedded, console di gioco, ecc.

Lo sviluppo di Android è iniziato nel 2003 da Android Inc. L’azienda inizialmente voleva creare un sistema operativo per fotocamere digitali, ma si è rapidamente orientata verso lo sviluppo di sistemi operativi mobili per raggiungere un mercato più ampio.

Nel 2005 l’azienda è stata acquisita da Google insieme ai suoi dipendenti. La prima versione di Android è stata rilasciata nel 2008.

Anche se Android è open-source, la maggior parte dei dispositivi mobili utilizza la versione proprietaria di Google. Questa versione è preinstallata con software come Google Chrome, YouTube, Google TV e Gmail.

A causa delle grandi possibilità di personalizzazione di Android, molti produttori hanno anche personalizzato Android per rappresentare meglio la propria azienda. Ecco perché l’aspetto di OnePlus Android è molto diverso da quello di Pixel Android.

Android è il sistema operativo più diffuso dal 2013. È utilizzato da circa il 70% dei dispositivi mobili e conta oltre tre miliardi di utenti attivi mensili.

Inoltre, Google Play Store consente di accedere a oltre tre milioni di applicazioni mobili. Continuate a leggere per scoprire come creare un backend per un’applicazione Android.

Vantaggi dello sviluppo Android

Esploriamo alcuni dei vantaggi dello sviluppo per Android.

Piattaforma trasversale

Sviluppando per Android è possibile rivolgersi a un’ampia gamma di dispositivi. Ciò include telefoni cellulari, indossabili, smart TV, console di gioco e altro ancora. Se sapete come codificare un’applicazione mobile, non avrete problemi a sviluppare un’applicazione per wearable o smart TV.

Un mercato enorme

Come già detto, Android detiene una quota di mercato maggiore (70%) rispetto a iOS (30%). Sviluppando applicazioni per Android, sarete automaticamente in grado di accedere a un pubblico più vasto.

Inoltre, le applicazioni Android sono note per il loro basso investimento e l’elevato ritorno sull’investimento. A differenza di iOS, inoltre, non sono previsti costi annuali per gli sviluppatori.

Personalizzazione

I dispositivi Android sono altamente personalizzabili rispetto ad altri sistemi operativi. Permettono di modificare praticamente tutto ciò che si può immaginare.

Inoltre, Android consente di integrare facilmente l’applicazione con altri servizi di terze parti.

Comunità

Android è supportato da una vasta comunità di sviluppatori open-source. Inoltre, è dotato di molti strumenti per gli sviluppatori, come Android Studio, ADB e Logcat, che consentono di codificare facilmente le applicazioni.

Se doveste rimanere bloccati, ci sono diverse piattaforme dove potete trovare aiuto, tra cui GitHub, StackOverflow, Reddit e altre comunità incentrate su Android.

Limitazioni dello sviluppo Android

Ecco alcuni dei contro dello sviluppo Android.

Sicurezza

Android è meno sicuro di iOS e non segue protocolli di sicurezza rigorosi. Poiché Android è open-source, le vulnerabilità di sicurezza vengono rilevate settimanalmente. Questo dà agli hacker la possibilità di sfruttare queste vulnerabilità prima che vengano corrette.

I dispositivi Android possono anche essere rootati per ottenere l’accesso come superutente. Se da un lato rende il dispositivo più potente, dall’altro è anche rischioso perché disabilita alcune misure di sicurezza integrate.

Complessità

I dispositivi Android sono disponibili in diverse forme e dimensioni e, sebbene questo sia un aspetto positivo, può rappresentare un enorme svantaggio quando si sviluppano applicazioni Android per più tipi di dispositivi.

Per garantire che la vostra applicazione sia compatibile con il maggior numero possibile di dispositivi, dovrete incorporare un design reattivo, pensare all’hardware dei diversi dispositivi e così via.

Applicazioni premium

Le app premium hanno meno successo su Google Play Store rispetto ad App Store. Non è un segreto che gli utenti Android tendano a spendere meno per le app rispetto agli utenti iOS. Se state lavorando a un’applicazione premium, considerate Android come seconda scelta.

Opzioni di backend per le applicazioni Android

Qual è il miglior backend per un’applicazione Android? I backend per le applicazioni mobili possono essere ospitati sulla propria infrastruttura o distribuiti su servizi cloud. I modelli di cloud computing più diffusi per i backend mobili includono:

  1. Infrastruttura come servizio (IaaS)
  2. Piattaforma come servizio (PaaS)
  3. Backend come servizio (BaaS)

Ogni opzione copre diversi livelli di astrazione, come illustrato nell’immagine seguente.

Livelli di astrazione IaaS vs PaaS vs BaaS

Analizziamo ogni opzione in dettaglio per aiutarvi a scegliere il modello di cloud più adatto alle vostre applicazioni di backend.

Infrastruttura come servizio (IaaS)

L’Infrastruttura come servizio (IaaS) è il modello di cloud computing meno astratto. In questo modello, il fornitore offre agli utenti risorse virtualizzate, come server, reti, storage e sistemi operativi, attraverso API di alto livello o dashboard intuitivi.

L’aspetto migliore dello IaaS è che offre agli utenti il controllo completo dell’intera infrastruttura. Lo IaaS è il modello più flessibile e scalabile, ma anche il più difficile da gestire. Se scegliete questa opzione, molto probabilmente avrete bisogno di uno o due SysAdmin.

Alcuni esempi di IaaS sono Amazon Web Services, Google Cloud Platform e Azure.

Piattaforma come servizio (PaaS)

Platform as a Service (PaaS) è un modello di cloud computing progettato per aiutare gli utenti a sviluppare, gestire e distribuire applicazioni.

Oltre a fornire l’infrastruttura, i PaaS sono dotati di strumenti di facile utilizzo per lo sviluppo, la personalizzazione e il test delle applicazioni.

Utilizzando il PaaS, potrete concentrarvi sul vostro software e sull’esperienza dell’utente senza preoccuparvi dell’infrastruttura sottostante.

Inoltre, il PaaS garantisce la scalabilità dell’applicazione su richiesta, si occupa dei backup e così via. Gli aspetti negativi del PaaS sono il minor livello di controllo, il rischio di vendor lock-in e i costi relativamente più elevati.

Le piattaforme PaaS più popolari includono Heroku, Render e Digital Ocean App Platform.

Backend come servizio (BaaS)

Backend as a Service (BaaS) automatizza la parte backend dello sviluppo fornendo una soluzione backend completa.

Le funzionalità BaaS includono la gestione degli utenti, i database, l’autenticazione, le integrazioni sociali, le notifiche push, le API, gli SDK e altro ancora.

BaaS offre tutti i vantaggi di IaaS e PaaS, fornendo al contempo funzionalità aggiuntive. I team che utilizzano BaaS tendono a rilasciare i loro prodotti più velocemente, a ridurre i costi di progettazione e a creare software migliore.

Gli svantaggi del BaaS includono un livello di controllo e personalizzazione inferiore e la possibilità di vendor lock-in per le piattaforme non open-source.

Gli esempi di BaaS includono Back4app, AWS Amplify e Firebase.

Come creare un backend per un’applicazione Android?

In questa sezione dell’articolo, vedremo come costruire un’applicazione backend basata su Back4app e come collegarsi ad essa da un’applicazione Android.

Prerequisiti

Che cos’è Back4app?

Back4app è una piattaforma eccellente per la creazione di backend per applicazioni moderne e per accelerare il processo di sviluppo.

È dotato di utili funzionalità lato server come la gestione degli utenti, i database in tempo reale, le integrazioni sociali, le funzioni di Cloud Code, le notifiche push, le API e molto altro ancora!

Utilizzando Back4app, sarete in grado di esternalizzare gran parte del lavoro di backend e di concentrarvi sugli aspetti critici della vostra applicazione, accelerando lo sviluppo del backend di un’applicazione Android.

Non sarà necessario occuparsi dell’infrastruttura sottostante del backend, del server backend, della scalabilità, della manutenzione e così via.

Soprattutto, Back4app offre un livello gratuito, ottimo per sperimentare e testare la piattaforma. Quando la vostra applicazione cresce, potete passare ai piani premium con un canone mensile.

Introduzione al progetto

In questo articolo, costruiremo una semplice applicazione per le note. L’applicazione consentirà agli utenti di aggiungere, modificare e cancellare note. Le note saranno memorizzate nel database di Back4app e manipolate tramite l’SDK Parse.

Per costruire l’applicazione utilizzeremo il linguaggio di programmazione Kotlin. Per l’interfaccia utente utilizzeremo Jetpack Compose, il moderno toolkit di Android per la creazione di interfacce utente native.

Back4app App Appunti per Android

Creare l’applicazione Back4app

Come primo passo per sviluppare un backend per un’applicazione Android, è necessario un account Back4app. Se non ne avete uno, procedete con l’iscrizione.

Quando si accede al proprio account Back4app, si viene reindirizzati alla visualizzazione delle app. Fare clic su “Build new app” per avviare il processo di creazione dell’app.

Back4app Crea una nuova applicazione

La piattaforma Back4app offre due soluzioni:

  1. Backend as a Service (BaaS): una solida soluzione di backend
  2. Containers as a Service (CaaS) — piattaforma per la gestione di container (soprattutto applicazioni web)

Considerando che stiamo costruendo un backend per un’applicazione mobile, sceglieremo “Backend as a Service”.

Soluzione BaaS di Back4app

Date alla vostra applicazione un nome simpatico e informativo, selezionate “NoSQL” come database e fate clic su “Crea”. Questo è un passo importante per distribuire un backend per un’applicazione Android.

Back4app impiegherà un po’ di tempo per preparare tutto ciò che è necessario per la vostra applicazione. Questo include il database, la gestione degli utenti, il ridimensionamento, le configurazioni, ecc. Una volta che l’applicazione è pronta, si verrà reindirizzati alla vista del database dell’applicazione.

Vista del database di Back4app

Progettare il database

Procediamo con la progettazione del database. Si tratta di una fase necessaria per sviluppare un backend per un’applicazione Android.

Poiché stiamo costruendo una semplice applicazione per le note, avremo bisogno di una sola classe. Facciamo clic su “Crea una classe”, chiamiamola Nota, abilitiamo “Lettura e scrittura pubblica” e facciamo clic su “Crea classe e aggiungi colonne”.

Back4app Crea classe database

Quindi, aggiungere i tre campi seguenti alla classe appena creata:

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

Infine, fate clic sul pulsante “Aggiungi riga” e popolate il database con alcuni dati di esempio. Se non avete idee, potete anche importare questo dump di dati.

Back4app Popola il database

Bene, questo è tutto per il backend.

Codice Frontend

In questa sezione dell’articolo, avvieremo una nuova applicazione Android, imposteremo un ViewModel, implementeremo l’interfaccia utente, installeremo e configureremo Parse SDK e infine recupereremo i dati dal database in tempo reale di Back4app.

Progetto Init

Come indicato nella sezione dei prerequisiti, i passaggi seguenti richiedono l’installazione di Android Studio. Se non lo avete ancora, scaricatelo.

Iniziate aprendo Android Studio e facendo clic sul pulsante “Nuovo progetto”.

Indice di Android Studio

Selezionate quindi “Attività vuota” come modello di progetto e fate clic su “Avanti”.

Per creare il progetto, è necessario fornire un nome e un nome di pacchetto. Suggerisco di utilizzare AndroidApp come nome del progetto e il nome del dominio inverso come pacchetto del progetto.

Dopo aver configurato tutto, fare clic su “Fine” per creare il progetto.

Impostazioni del progetto Android Studio

Android Studio impiegherà circa due minuti per preparare tutto il necessario. Questo include la creazione della struttura dei file del progetto, l’impostazione di Gradle, l’installazione delle dipendenze, ecc.

Una volta che il progetto è pronto, usare l’explorer per navigare in MainActivity.kt e sostituirne il contenuto:

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

Assicuratevi di sostituire sempre con il nome effettivo del pacchetto.

Infine, provate a eseguire l’applicazione facendo clic sul pulsante verde play o su Shift + F10 sulla tastiera. Se tutto va bene, l’emulatore dovrebbe avviarsi e sullo schermo dovrebbe apparire il messaggio Back4app rocks.

Prima applicazione Android

Modello di vista

Per gestire lo stato globale della nostra applicazione, utilizzeremo un ViewModel. Ma prima di questo, dobbiamo creare una classe di dati Note con gli stessi attributi di quella presente nel database di Back4app.

Creare una classe di dati chiamata Nota:

// 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. Abbiamo definito il metodo statico generateObjectId() per generare ID di oggetti simili a Parse.
  2. objectId è nullable, poiché in seguito si assumerà che se objectId == null, l’oggetto non è ancora stato salvato nel database.

Quindi, creare una classe AppViewModel con i seguenti contenuti:

// 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. Abbiamo usato MutableState di Compose per attivare gli aggiornamenti dell’interfaccia utente in caso di modifica dei dati.
  2. Il MutableState contiene una mappa di objectIdse Notes, che abbiamo popolato con i dati.
  3. Per garantire che ci sia una sola istanza di AppViewModel, abbiamo usato lo schema singleton.

Ora possiamo accedere all’istanza di AppViewModel nelle nostre attività tramite AppViewModel.getInstance().

Attività principale

Procedendo, sostituite il contenuto di MainActivity.kt con il seguente codice per visualizzare le note:

// 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. Le note vengono ora recuperate dall’AppViewModel.
  2. Invece di definire gli eventi di clic nei composable, li abbiamo passati come argomenti.
  3. Abbiamo utilizzato Compose per progettare l’interfaccia utente. Per renderla più elegante, abbiamo incorporato MaterialTheme con il layout di Scaffold.

Se si ricostruisce l’applicazione ora, si dovrebbe vedere l’elenco delle note “hard-coded” sullo schermo.

Back4app Notes Elenco applicazioni

Modulo note Attività

In questa sezione, aggiungeremo una nuova attività che consentirà agli utenti di aggiungere e modificare le note.

Si inizia creando una nuova classe chiamata FormActivity con i seguenti contenuti:

// 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. Questo modulo viene utilizzato sia per aggiungere che per modificare le note. Per determinare l’azione, si confronta objectId == null. In caso di null, si tratta di un’azione di aggiunta, altrimenti di modifica.
  2. Se objectId !== null, si recupera la nota dallo stato e si popola il modulo.
  3. Affinché MutableState funzioni correttamente, dobbiamo trattare i suoi contenuti come immutabili. Per questo motivo, a volte abbiamo copiato i contenuti invece di modificarli.
  4. Abbiamo gestito lo stato del form Compose tramite mutableStateOf().

Quindi, registrate la FormActivity in fondo all’applicazione 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>

Infine, creare un nuovo Intent su onNoteListItemClick e 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)
                },
            )
        }
    }
}

Non dimenticate di importare l’Intento all’inizio del file:

import android.content.Intent

Se si ricostruisce l’applicazione, si noterà che ora abbiamo un’applicazione funzionante. L’applicazione consente di aggiungere note, modificarle ed eliminarle.

L’unico problema è che riavviando l’applicazione si ripristina il suo stato (alle note “codificate”).

Nella prossima sezione, collegheremo l’applicazione al backend Back4app. Questo ci permetterà di persistere lo stato e di sincronizzarlo tra più dispositivi.

Anteprima di modifica dell'app Android Notes

Installare l’SDK Parse

Come forse sapete, Back4app si basa sulla piattaforma Parse. Se vogliamo interagire con il database di Back4app o con Back4app in generale, dobbiamo installare il Parse SDK.

Si inizia aggiungendo il repository JitPack a 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" }
    }
}

Quindi, aggiungere l’SDK Android Parse al file build.gradle del livello dell’applicazione:

// app/build.gradle

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

Sincronizzare le impostazioni di Gradle e verificare che non ci siano errori.

Configurare l’SDK Parse

Per connettersi al backend di Back4app, è necessario fornire a Parse SDK l’ID dell’applicazione e la chiave del client. Per ottenere le vostre credenziali, visitate la vostra applicazione Back4app e selezionate “Impostazioni dell’applicazione > Sicurezza e chiavi” nella barra laterale.

Quindi aggiungerli al file strings.xml in questo modo:

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

Assicuratevi di sostituire your_parse_app_id e your_parse_client_key con le vostre credenziali.

Quindi, modificare il file AndroidManifest.xml per abilitare l’accesso a Internet e allegare i metadati delle credenziali:

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

L’ultima cosa da fare è inizializzare Parse. Per assicurarci che Parse sia inizializzato prima di qualsiasi attività, creeremo una nuova classe chiamata App, che eredita dalla classe Application.

Creare App.kt e inizializzare Parse in questo modo:

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

Quindi registrare la classe App in AndroidManifest.xml:

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

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

Ricostruire nuovamente l’applicazione e controllare la finestra Logcat per verificare la presenza di eventuali errori. Se non ci sono errori, la connessione a Back4app è avvenuta con successo.

Note di caricamento da Back4app

L’ultima cosa da fare prima che la nostra applicazione sia completa è caricare le note dal database di Back4app e vedere come usare Parse nella pratica.

Iniziare navigando nell’AppViewModel. Rimuovere i dati dalla mappa e aggiungere il blocco 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 {
        // ...
    }
}

Questo codice utilizza Parse SDK per recuperare le note dai database, trasformarle nella classe di dati Note e salvarle nella mappa.

Quindi, aggiungere i tre metodi seguenti a 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()
        }
    }
}
  1. addToParse() aggiunge una nuova nota al server Parse con i suoi dettagli. In caso di successo, memorizza l’identificatore univoco della nota e notifica quando ha finito.
  2. updateToParse() aggiorna le informazioni di una nota esistente sul server Parse. Se ha successo, segnala il completamento dopo aver apportato le modifiche.
  3. deleteFromParse() rimuove una nota salvata dal server Parse. In caso di successo, conferma l’eliminazione al termine.

Infine, modificare i metodi di clic in FormActivity per richiamare i nuovi metodi definiti:

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

Ecco fatto!

L’applicazione è ora completamente funzionante e sincronizzata con il backend di Back4app. Provate a giocare con l’applicazione modificando i dati e verificando se le modifiche si riflettono nella vista del database.

Conclusione

In questo articolo abbiamo costruito con successo un backend mobile e ci siamo collegati ad esso dalla nostra applicazione Android. Per il backend abbiamo utilizzato la soluzione BaaS di Back4app, mentre per il frontend abbiamo utilizzato Kotlin con Jetpack Compose.

A questo punto, dovreste avere una conoscenza decente di come funzionano i backend per dispositivi mobili ed essere in grado di costruire il vostro. Il codice sorgente finale è disponibile sul repo back4app-android-app.


Leave a reply

Your email address will not be published.