Como criar um backend para um aplicativo Android?

Capa do aplicativo Back4app para Android

Neste artigo, falaremos sobre o Android, um dos sistemas operacionais mais populares. Veremos os prós e os contras do desenvolvimento do Android, as opções de backend para aplicativos móveis e ensinaremos a você como criar seu próprio backend móvel.

Principais conclusões

  • Domínio do Android: O Android, como um sistema operacional de código aberto, alimenta cerca de 70% dos dispositivos móveis com mais de três bilhões de usuários ativos.
  • Opções de back-end para aplicativos: Para back-ends de aplicativos móveis, os desenvolvedores podem optar por infraestrutura como serviço (IaaS), plataforma como serviço (PaaS) ou back-end como serviço (BaaS).
  • Etapas para criar seu backend: Forneceremos um tutorial detalhado sobre como criar um backend do Android usando uma plataforma BaaS e o código-fonte até o final do artigo.

O que é o Android?

O Android é um sistema operacional gratuito e de código aberto baseado em Linux. Ele foi projetado principalmente para dispositivos móveis, como smartphones e tablets, mas agora também é usado em smart TVs, sistemas incorporados, consoles de jogos, etc.

O desenvolvimento do Android começou em 2003 pela Android Inc. A empresa inicialmente queria criar um sistema operacional para câmeras digitais, mas rapidamente mudou para o desenvolvimento de sistemas operacionais móveis para atingir um mercado mais amplo.

Em 2005, a empresa foi adquirida pelo Google, juntamente com seus funcionários. A primeira versão do Android foi lançada em 2008.

Embora o Android seja de código aberto, a maioria dos dispositivos móveis executa a versão proprietária do Google. Sua versão vem pré-instalada com softwares como o Google Chrome, YouTube, Google TV e Gmail.

Devido às ótimas opções de personalização do Android, muitos fabricantes também personalizam o Android para representar melhor sua empresa. É por isso que o Android do OnePlus é bem diferente do Android do Pixel.

O Android tem sido o sistema operacional mais popular desde 2013. Ele é usado por cerca de 70% dos dispositivos móveis e tem mais de três bilhões de usuários ativos mensais.

Além disso, a Google Play Store oferece acesso a mais de três milhões de aplicativos móveis. Continue lendo para saber como criar um backend para um aplicativo Android.

Benefícios do desenvolvimento do Android

Vamos explorar algumas das vantagens do desenvolvimento para Android.

Plataforma cruzada

Ao desenvolver para o Android, você pode atingir uma ampla gama de dispositivos. Isso inclui telefones celulares, wearables, smart TVs, consoles de jogos e muito mais. Se você sabe como codificar um aplicativo móvel, não terá problemas para desenvolver um aplicativo para dispositivos portáteis ou smart TVs.

Mercado enorme

Conforme apresentado anteriormente, o Android detém uma participação de mercado maior, com 70%, em comparação com o iOS, com 30%. Ao desenvolver aplicativos para Android, você poderá automaticamente obter acesso a um público mais amplo.

Além disso, os aplicativos Android são conhecidos por seu baixo investimento e alto retorno sobre o investimento. Ao contrário do iOS, também não há taxas anuais de desenvolvedor envolvidas.

Personalização

Os dispositivos Android são altamente personalizáveis em comparação com outros sistemas operacionais. Eles permitem que você altere praticamente qualquer coisa que possa imaginar.

Além disso, o Android permite que você integre facilmente seu aplicativo a outros serviços de terceiros.

Comunidade

O Android é apoiado por uma enorme comunidade de desenvolvedores de código aberto. Além disso, ele vem com muitas ferramentas amigáveis ao desenvolvedor, como Android Studio, ADB e Logcat, que permitem codificar aplicativos facilmente.

Se você tiver algum problema, há várias plataformas em que pode encontrar ajuda, incluindo GitHub, StackOverflow, Reddit e outras comunidades voltadas para o Android.

Limitações do desenvolvimento do Android

Aqui estão alguns dos contras do desenvolvimento do Android.

Segurança

O Android é menos seguro que o iOS e não segue protocolos de segurança rígidos. Como o Android é de código aberto, as vulnerabilidades de segurança são detectadas semanalmente. Isso dá aos hackers a capacidade de explorar essas vulnerabilidades antes que elas sejam corrigidas.

Os dispositivos Android também podem ser enraizados para obter acesso de superusuário. Embora isso torne seu dispositivo mais avançado, também é arriscado, pois desativa algumas medidas de segurança incorporadas.

Complexidade

Os dispositivos Android vêm em diferentes formas e tamanhos e, embora isso seja ótimo, pode ser uma grande desvantagem ao desenvolver aplicativos Android para vários tipos de dispositivos.

Para garantir que seu aplicativo seja compatível com o maior número possível de dispositivos, você terá que incorporar o design responsivo, pensar no hardware de diferentes dispositivos e assim por diante.

Aplicativos premium

Os aplicativos premium têm menos sucesso na Google Play Store em comparação com a App Store. Não é segredo que os usuários do Android tendem a gastar menos em aplicativos do que os usuários do iOS. Se estiver trabalhando em um aplicativo premium primeiro, considere o Android como sua segunda opção.

Opções de back-end para aplicativos Android

Qual é o melhor backend para aplicativos Android? Os back-ends para aplicativos móveis podem ser hospedados em sua própria infraestrutura ou implantados nos serviços de nuvem. Os modelos de computação em nuvem mais populares para back-ends móveis incluem:

  1. Infraestrutura como serviço (IaaS)
  2. Plataforma como serviço (PaaS)
  3. Backend como serviço (BaaS)

Cada opção abrange diferentes camadas de abstração, conforme ilustrado na imagem abaixo.

Camadas de abstração IaaS vs PaaS vs BaaS

Vamos examinar cada opção em detalhes para ajudá-lo a selecionar o modelo de nuvem mais adequado para seus aplicativos de back-end.

Infraestrutura como serviço (IaaS)

A infraestrutura como serviço (IaaS) é o modelo de computação em nuvem menos abstraído. Nesse modelo, o fornecedor oferece aos usuários recursos virtualizados, como servidores, redes, armazenamento e sistemas operacionais, por meio de APIs de alto nível ou painéis de controle intuitivos.

O melhor da IaaS é que ela oferece aos usuários controle total sobre toda a infraestrutura. A IaaS é o modelo mais flexível e dimensionável, mas também o mais difícil de gerenciar. Se você escolher essa opção, provavelmente precisará de um ou dois administradores de sistemas.

Alguns exemplos de IaaS são o Amazon Web Services, o Google Cloud Platform e o Azure.

Plataforma como serviço (PaaS)

A PaaS (Platform as a Service) é um modelo de computação em nuvem projetado para ajudar os usuários a desenvolver, gerenciar e implementar aplicativos.

Além de fornecer infraestrutura, a PaaS vem com ferramentas fáceis de usar para desenvolver, personalizar e testar aplicativos.

Ao utilizar a PaaS, você poderá se concentrar no seu software e na experiência do usuário sem se preocupar com a infraestrutura subjacente.

Além disso, a PaaS garantirá que seu aplicativo seja dimensionado sob demanda, cuidará dos backups, etc. As desvantagens da PaaS são o menor nível de controle, o risco de dependência do fornecedor e os custos comparativamente mais altos.

As plataformas PaaS mais populares incluem Heroku, Render e Digital Ocean App Platform.

Backend como serviço (BaaS)

O Backend as a Service (BaaS) automatiza a parte de backend do desenvolvimento, fornecendo uma solução de backend completa.

Os recursos de BaaS incluem gerenciamento de usuários, bancos de dados, autenticação, integrações sociais, notificações por push, APIs, SDKs e muito mais.

O BaaS oferece a você todas as vantagens do IaaS e do PaaS, ao mesmo tempo em que fornece funcionalidades adicionais. As equipes que utilizam o BaaS tendem a lançar seus produtos mais rapidamente, reduzir os custos de engenharia e criar softwares melhores.

As desvantagens do BaaS incluem um nível mais baixo de controle e personalização e a possibilidade de dependência do fornecedor para plataformas de código não aberto.

Exemplos de BaaS incluem Back4app, AWS Amplify e Firebase.

Como criar um backend para um aplicativo Android?

Nesta seção do artigo, veremos como criar um aplicativo backend baseado no Back4app e conectar-se a ele a partir de um aplicativo Android.

Pré-requisitos

O que é o Back4app?

O Back4app é uma excelente plataforma para criar backends para aplicativos modernos e acelerar o processo de desenvolvimento.

Ele vem bem equipado com recursos úteis do lado do servidor, como gerenciamento de usuários, bancos de dados em tempo real, integrações sociais, funções do Cloud Code, notificações push, APIs e muito mais!

Ao usar o Back4app, você poderá terceirizar grande parte do trabalho de back-end e se concentrar nos aspectos críticos do seu aplicativo, além de acelerar o desenvolvimento do back-end de um aplicativo Android.

Não haverá necessidade de lidar com a infraestrutura subjacente de seu backend, servidor de backend, dimensionamento, manutenção, etc.

Mais importante ainda, o Back4app oferece um nível gratuito que é ótimo para experimentar e testar a plataforma. À medida que seu aplicativo cresce, você pode fazer upgrade para planos premium mediante o pagamento de uma taxa mensal.

Introdução ao projeto

Neste artigo, criaremos um aplicativo simples de anotações. O aplicativo permitirá que os usuários adicionem, editem e excluam notas. As anotações serão armazenadas no banco de dados do Back4app e manipuladas por meio do Parse SDK.

Usaremos a linguagem de programação Kotlin para criar o aplicativo. Para a interface do usuário, utilizaremos o Jetpack Compose, o kit de ferramentas moderno do Android para criar interfaces do usuário nativas.

Back4app Aplicativo de notas para Android

Criar o aplicativo Back4app

Como primeira etapa para desenvolver um backend para um aplicativo Android, você precisará de uma conta no Back4app. Se ainda não tiver uma, vá em frente e registre-se.

Ao fazer login na sua conta Back4app, você será redirecionado para a visualização do aplicativo. Clique em “Build new app” (Criar novo aplicativo) para iniciar o processo de criação do aplicativo.

Back4app Criar novo aplicativo

A plataforma Back4app oferece duas soluções:

  1. Backend as a Service (BaaS) – solução robusta de backend
  2. Contêineres como serviço (CaaS): plataforma para gerenciar contêineres (especialmente aplicativos da Web)

Considerando que estamos criando um backend para um aplicativo móvel, escolheremos o “Backend as a Service”.

Solução Back4app BaaS

Dê ao seu aplicativo um nome bonito e informativo, selecione “NoSQL” como o banco de dados e clique em “Create”. Essa é uma etapa importante para implementar um backend para um aplicativo Android.

A Back4app levará algum tempo para preparar tudo o que é necessário para seu aplicativo. Isso inclui o banco de dados, o gerenciamento de usuários, o dimensionamento, as configurações, etc. Quando o aplicativo estiver pronto, você será redirecionado para a visualização do banco de dados do aplicativo.

Visualização do banco de dados do Back4app

Projetar o banco de dados

Continuando, vamos projetar o banco de dados. Essa é uma etapa necessária para desenvolver um backend para um aplicativo Android.

Como estamos criando um aplicativo simples de notas, precisaremos apenas de uma classe. Clique em “Create a class” (Criar uma classe), chame-a de Note (Nota), ative “Public Read and Write” (Leitura e gravação públicas) e clique em “Create class & add columns” (Criar classe e adicionar colunas).

Classe Back4app Create Database

Em seguida, adicione os três campos a seguir à classe recém-criada:

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

Por fim, clique no botão “Add row” (Adicionar linha) e preencha seu banco de dados com alguns dados de amostra. Se não tiver nenhuma ideia, você também pode importar esse despejo de dados.

Back4app Popular banco de dados

Ótimo, isso é tudo para o backend.

Código Frontend

Nesta seção do artigo, faremos o bootstrap de um novo aplicativo Android, configuraremos um ViewModel, implementaremos a interface do usuário, instalaremos e configuraremos o Parse SDK e, por fim, buscaremos dados do banco de dados em tempo real do Back4app.

Projeto Init

Conforme mencionado na seção de pré-requisitos, as etapas a seguir exigirão que você tenha o Android Studio instalado. Se você ainda não o tiver, faça o download.

Comece abrindo o Android Studio e clicando no botão “New Project” (Novo projeto).

Índice do Android Studio

Em seguida, selecione “Empty Activity” (Atividade vazia) como seu modelo de projeto e clique em “Next” (Avançar).

Para criar seu projeto, você deve fornecer um nome e um nome de pacote. Sugiro que você use AndroidApp como o nome do projeto e o nome de domínio reverso como o pacote do projeto.

Depois de configurar tudo, clique em “Finish” (Concluir) para criar o projeto.

Configurações do projeto do Android Studio

O Android Studio levará cerca de dois minutos para preparar tudo o que for necessário. Isso inclui a criação da estrutura de arquivos do projeto, a configuração do Gradle, a instalação de dependências etc.

Quando seu projeto estiver pronto, use o explorador para navegar até MainActivity.kt e substituir seu conteúdo:

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

Certifique-se de sempre substituir pelo nome real do pacote.

Por fim, tente executar o aplicativo clicando no botão verde de reprodução ou em Shift + F10 no teclado. Se tudo correr bem, o emulador deverá ser iniciado e você deverá ver a mensagem Back4app rocks na tela.

Primeiro aplicativo para Android

ViewModel

Para gerenciar o estado global do nosso aplicativo, utilizaremos um ViewModel. Mas, antes disso, temos de criar uma classe de dados Note com os mesmos atributos da classe do nosso banco de dados Back4app.

Crie uma classe de dados chamada 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. Definimos o método estático generateObjectId() para gerar IDs de objeto do tipo Parse.
  2. objectId é anulável, pois mais tarde assumiremos que, se objectId == null, o objeto ainda não foi salvo no banco de dados.

Em seguida, crie uma classe AppViewModel com o seguinte conteúdo:

// 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. Usamos o MutableState do Compose para acionar atualizações da IU em caso de alteração de dados.
  2. O MutableState contém um mapa de objectIdse Notes, que preenchemos com dados.
  3. Para garantir que haja apenas uma única instância do AppViewModel, usamos o padrão singleton.

Agora podemos acessar a instância do AppViewModel em nossas atividades por meio de AppViewModel.getInstance().

Atividade principal

Em seguida, substitua o conteúdo do MainActivity.kt pelo seguinte código para exibir as anotações:

// 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. As notas agora estão sendo obtidas do AppViewModel.
  2. Em vez de definir os eventos de clique em composables, nós os passamos como argumentos.
  3. Usamos o Compose para projetar a interface do usuário. Para torná-la mais elegante, incorporamos o MaterialTheme ao layout do Scaffold.

Se você reconstruir o aplicativo agora, deverá ver a lista de notas “codificadas” na tela.

Lista de aplicativos do Back4app Notes

Atividade do formulário de notas

Nesta seção, adicionaremos uma nova atividade que permitirá aos usuários adicionar e editar notas.

Comece criando uma nova classe chamada FormActivity com o seguinte conteúdo:

// 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. Esse formulário é usado para adicionar e editar notas. Para determinar a ação, comparamos objectId == null. No caso de null, é uma ação de adição, caso contrário, é uma ação de edição.
  2. Se objectId !== null, buscaremos a nota do estado e preencheremos o formulário.
  3. Para que o MutableState funcionasse corretamente, tivemos que tratar seu conteúdo como imutável. É por isso que, às vezes, copiamos o conteúdo em vez de modificá-lo.
  4. Tratamos o estado do formulário Compose por meio de mutableStateOf().

Em seguida, registre o FormActivity na parte inferior do aplicativo no 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>

Por fim, crie um novo Intent em onNoteListItemClick e onNoteAddClick em 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)
                },
            )
        }
    }
}

Não se esqueça de importar o Intent na parte superior do arquivo:

import android.content.Intent

Se você reconstruir o aplicativo, perceberá que agora temos um aplicativo em funcionamento. O aplicativo nos permite adicionar notas, editá-las e excluí-las.

O único problema é que reiniciar o aplicativo redefine seu estado (para as notas “codificadas”).

Na próxima seção, conectaremos o aplicativo ao backend do Back4app. Isso nos permitirá manter o estado e sincronizá-lo entre vários dispositivos.

Visualização de edição do aplicativo Android Notes

Instalar o Parse SDK

Como você deve saber, o Back4app é baseado na plataforma Parse. Se quisermos interagir com o banco de dados do Back4app ou com o Back4app em geral, teremos de instalar o Parse SDK.

Comece adicionando o repositório do JitPack ao 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" }
    }
}

Em seguida, adicione o Android Parse SDK ao build.gradle do nível do aplicativo:

// app/build.gradle

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

Sincronize as configurações do Gradle e verifique se não há erros.

Configurar o Parse SDK

Para se conectar ao backend do Back4app, você terá de fornecer ao Parse SDK o ID do aplicativo e a chave do cliente. Para obter suas credenciais, navegue até o aplicativo Back4app e selecione “App Settings > Security & Keys” (Configurações do aplicativo > Segurança e chaves) na barra lateral.

Em seguida, adicione-os ao seu strings.xml da seguinte forma:

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

Certifique-se de substituir your_parse_app_id e your_parse_client_key por suas credenciais.

Em seguida, modifique seu AndroidManifest.xml para habilitar o acesso à Internet e anexar metadados de credenciais:

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

A última coisa que temos de fazer é inicializar o Parse. Para garantir que o Parse seja inicializado antes de qualquer atividade, criaremos uma nova classe chamada App, que herda da classe Application.

Crie o App.kt e inicialize o Parse da seguinte forma:

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

Em seguida, registre a classe App no AndroidManifest.xml:

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

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

Reconstrua o aplicativo mais uma vez e verifique se há erros na janela Logcat. Se não houver erros, a conexão com o Back4app foi bem-sucedida.

Carregar notas do Back4app

A última coisa que precisamos fazer antes de concluir nosso aplicativo é carregar as anotações do banco de dados do Back4app e ver como usar o Parse na prática.

Comece navegando até o AppViewModel. Remova os dados do mapa e adicione o bloco de inicialização:

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

Esse código usa o Parse SDK para buscar as anotações nos bancos de dados, transformá-las na classe de dados Note e salvá-las no mapa.

Em seguida, adicione os três métodos a seguir ao 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() adiciona uma nova nota ao servidor Parse com seus detalhes. Se for bem-sucedido, ele armazena o identificador exclusivo da nota e avisa quando terminar.
  2. updateToParse() atualiza as informações de uma nota existente no servidor do Parse. Se for bem-sucedido, ele sinaliza a conclusão após fazer as alterações.
  3. deleteFromParse() remove uma nota salva do servidor do Parse. Se for bem-sucedido, ele confirmará a exclusão quando terminar.

Por fim, modifique os métodos de clique em FormActivity para invocar os métodos recém-definidos:

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

É isso aí!

O aplicativo agora está totalmente funcional e sincronizado com o backend do Back4app. Experimente brincar com o aplicativo alterando os dados e verificando se as alterações são refletidas na visualização do banco de dados.

Conclusão

Neste artigo, criamos com sucesso um backend móvel e nos conectamos a ele a partir do nosso aplicativo Android. Usamos a solução BaaS da Back4app para o backend e, no frontend, utilizamos o Kotlin com o Jetpack Compose.

A esta altura, você já deve ter uma boa compreensão de como funcionam os back-ends móveis e deve ser capaz de criar o seu próprio. Veja o código-fonte final no repositório back4app-android-app.


Leave a reply

Your email address will not be published.