Neste tutorial, estaremos discutindo os Android Intents e implementando-os usando Kotlin em nossa aplicação.
O que você aprenderá?
- O que são Intents?
- Tipos de Intents?
- Utilizando Intents entre atividades
- Envio de dados usando Android Intents
- Utilizando Parcelable e Serializable para passar objetos
- Criando intents abreviados
Android Intents
Como o nome diz, Intent é algo usado para realizar alguma ação em relação ao fluxo da aplicação Android. Intents podem ser usados para:
- Iniciar uma nova atividade e passar alguns dados.
- Iniciar Fragments/Comunicar entre fragments.
- Iniciar/Terminar serviço.
- Iniciar atividades a partir de um receptor de transmissão
Neste tutorial, estaremos focando principalmente em intents para lidar com atividades. A definição de um intent consiste principalmente em uma instância da atividade atual. Definimos o nome do componente, que pode ser: O nome da classe totalmente qualificado da atividade a ser chamada. Esse tipo de Intent é um intent explícito. Uma ação como URL, número de telefone, localização. Ele exibirá todas as aplicações disponíveis desses tipos. Isso se enquadra na categoria de intent implícito. Em Kotlin, o seguinte é a maneira de criar uma atividade.
val intent = Intent(this, OtherActivity::class.java)
startActivity(intent)
startActivity
adicionaria OtherActivity
na pilha de atividades e a lançaria. Como nossa aplicação identifica qual atividade será a primeira a ser invocada? No AndroidManifest.xml, definimos o filtro de intenções com a ação android.intent.action.MAIN
e a categoria android.intent.category.LAUNCHER
na primeira atividade a ser lançada quando nossa aplicação é aberta. finish()
é usado para destruir uma atividade e removê-la da pilha.
Flags de Intenções
As flags são como opções que podem ser definidas em intenções para personalizar o processo de lançamento. Se você iniciar a mesma atividade sempre, uma nova instância será criada e adicionada à pilha de atividades. Para evitar isso, você pode usar as flags: FLAG_ACTIVITY_SINGLE_TOP
– Se definida, a atividade não será lançada se já estiver em execução no topo da pilha de atividades.
intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
Da mesma forma, o uso da flag FLAG_ACTIVITY_CLEAR_TOP
não lançaria outra instância da atividade se ela já existir. Essa flag limparia todas as atividades acima da atividade chamada e a colocaria no topo da pilha.
Passando Dados por Intenções
Para passar dados para as novas atividades, utilizamos pares de chave-valor dentro da função putExtra
, putStringArrayListExtra
, etc. O putExtra geralmente passa os tipos básicos, como Int, Float, Char, Double, Boolean, String, juntamente com IntArray… etc.
val intent = Intent(this, OtherActivity::class.java)
intent.putExtra("keyString", "Androidly String data")
Esses campos Extras são encapsulados internamente no objeto Bundle
, que, em última instância, contém todos os dados a serem transmitidos. Para recuperar os dados na outra atividade, precisamos utilizar a propriedade extras
sobre os bundles
. Recuperando Dados na Nova Atividade
val bundle: Bundle? = intent.extras
val string: String? = intent.getString("keyString")
val myArray: ArrayList<String>? = intent.getStringArrayList("myArray")
intent
, extras
são equivalentes a getIntent()
, getExtras()
em Java. Utilizamos um tipo anulável Bundle?
para evitar NullPointerExceptions
quando não há dados. Da mesma forma, para os dados obtidos usando as chaves, utilizamos tipos anuláveis para evitar NPE que podem ocorrer quando a chave está incorreta.
Usando Dados Parcelable e Serializable
Às vezes, precisamos passar um objeto completo de uma atividade para outra. Isso não é possível a menos que implementemos a interface Parcelable ou Serializable. Diferença entre Parcelable e Serializable
- A interface Parcelable faz parte do Android SDK. Serializable é uma interface padrão do Java.
- No Parcelable, você precisa definir todos os dados que precisa passar em um objeto Parcel e também sobrescrever os métodos writeToParcel(), etc. No Serializable, implementar a interface é suficiente para passar os dados.
- O Parcelable é mais rápido que o Serializable.
Enviando Dados Parcelable
O Kotlin oferece algumas anotações úteis para nos poupar de sobrescrever o método writeToParcel() para definir os dados no Parcelable. Em vez disso, podemos usar a anotação @Parcelize, como mostrado abaixo:
@Parcelize
data class Student(
val name: String = "Anupam",
val age: Int = 24
) : Parcelable
Observação: Atualmente, no seu build.gradle, você deve adicionar o seguinte código para que a anotação @Parcelize funcione:
android {
androidExtensions {
experimental = true
}
//..
....
}
No seu Activity, faça o seguinte:
val student = Student()
val intent = Intent(this, OtherActivity::class.java)
intent.putExtra("studentData", student)
startActivity(intent)
Enviando Dados Serializable
data class Blog(val name: String = "Androidly", val year: Int = 2018) : Serializable
val blog = Blog("a", 1)
val intent = Intent(this, OtherActivity::class.java)
intent.putExtra("blogData", blog as Serializable)
startActivity(intent)
Vamos utilizar o conhecimento acima no nosso Projeto Android Studio.
Estrutura do Projeto
Código de Layout
O código para o layout activity_main.xml
é fornecido abaixo:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/btnSimpleIntent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SIMPLE INTENT" />
<Button
android:id="@+id/btnSimpleIntentAndData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SIMPLE INTENT WITH DATA" />
<Button
android:id="@+id/btnParcelableIntent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Parcelable Intent" />
<Button
android:id="@+id/btnSerializableIntent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Serializable Intent" />
<Button
android:id="@+id/btnBrowserIntent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Browser Intent" />
<Button
android:id="@+id/btnMapsIntent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Maps Intent" />
<Button
android:id="@+id/btnGenericIntent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Generic Intent" />
</LinearLayout>
O código para o layout activity_other.xml
é fornecido abaixo:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Intent Data goes here" />
</LinearLayout>
Código da Atividade
O código para a classe MainActivity.kt é fornecido abaixo:
package net.androidly.androidlyintents
import android.app.Activity
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.os.Parcelable
import android.view.View
import android.widget.Toast
import android.widget.Toast.LENGTH_LONG
import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.activity_main.*
import java.io.Serializable
@Parcelize
data class Student(
val name: String = "Anupam",
val age: Int = 24
) : Parcelable
data class Blog(val name: String = "Androidly", val year: Int = 2018) : Serializable
class MainActivity : AppCompatActivity(), View.OnClickListener {
fun Context.gotoClass(targetType: Class<*>) =
ComponentName(this, targetType)
fun Context.startActivity(f: Intent.() -> Unit): Unit =
Intent().apply(f).run(this::startActivity)
inline fun <reified T : Activity> Context.start(
noinline createIntent: Intent.() -> Unit = {}
) = startActivity {
component = gotoClass(T::class.java)
createIntent(this)
}
var arrayList = ArrayList<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnSimpleIntent.setOnClickListener(this)
btnSimpleIntentAndData.setOnClickListener(this)
btnParcelableIntent.setOnClickListener(this)
btnSerializableIntent.setOnClickListener(this)
btnBrowserIntent.setOnClickListener(this)
btnMapsIntent.setOnClickListener(this)
btnGenericIntent.setOnClickListener(this)
arrayList.add("Androidly")
arrayList.add("Android")
arrayList.add("Intents")
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btnSimpleIntent -> {
val intent = Intent(this, OtherActivity::class.java)
startActivity(intent)
}
R.id.btnSimpleIntentAndData -> {
val intent = Intent(this, OtherActivity::class.java)
with(intent)
{
putExtra("keyString", "Androidly String data")
putStringArrayListExtra("arrayList", arrayList)
putExtra("keyBoolean", true)
putExtra("keyFloat", 1.2f)
}
startActivity(intent)
}
R.id.btnParcelableIntent -> {
val student = Student()
val intent = Intent(this, OtherActivity::class.java)
intent.putExtra("studentData", student)
startActivity(intent)
}
R.id.btnSerializableIntent -> {
val blog = Blog("a", 1)
val intent = Intent(this, OtherActivity::class.java)
intent.putExtra("blogData", blog as Serializable)
startActivity(intent)
}
R.id.btnBrowserIntent -> {
val url = "https://www.androidly.net"
val uri = Uri.parse(url)
val intent = Intent(Intent.ACTION_VIEW, uri)
if (intent.resolveActivity(packageManager) != null) {
startActivity(intent)
} else {
Toast.makeText(applicationContext, "No application found", LENGTH_LONG).show()
}
}
R.id.btnMapsIntent -> {
val loc = "12.9538477,77.3507442"
val addressUri = Uri.parse("geo:0,0?q=" + loc)
val intent = Intent(Intent.ACTION_VIEW, addressUri)
if (intent.resolveActivity(packageManager) != null) {
startActivity(intent)
} else {
Toast.makeText(applicationContext, "No application found", LENGTH_LONG).show()
}
}
else -> start<OtherActivity> {
putExtra("keyString", "Androidly Generic Intent")
}
}
}
}
No código acima, usamos Botões para cada tipo de Intent. Usamos a expressão with
do Kotlin para evitar definir dados sobre o objeto intent
toda vez. Além disso, criamos três intents diferentes além dos já discutidos acima. Um intent de navegador é usado para abrir o URL presente no intent no aplicativo do navegador. Ele usa Intent(Intent.ACTION_VIEW, uri)
. Um intent de localização é usado para abrir a localização lat,lng no aplicativo de mapas. Ambos são intents implícitos. Por último, usamos um intent genérico no qual usamos as funções de extensão do Kotlin e expressões lambda para criar uma função abreviada para iniciar um intent. Para isso, usamos as seguintes funções:
fun Context.gotoClass(targetType: Class<*>) =
ComponentName(this, targetType)
fun Context.startActivity(createIntent: Intent.() -> Unit): Unit =
Intent().apply(createIntent).run(this::startActivity)
inline fun <reified T : Activity> Context.start(
noinline createIntent: Intent.() -> Unit = {}
) = startActivity {
component = gotoClass(T::class.java)
createIntent(this)
}
startActivity é uma função de extensão que procura por uma função de ordem superior como seu parâmetro. Graças a isso, agora podemos iniciar intents em apenas algumas linhas, como: start<OtherActivity>
. O código para a classe OtherActivity.kt é fornecido abaixo.
package net.androidly.androidlyintents
import android.content.Context
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_other.*
class OtherActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_other)
val bundle: Bundle? = intent.extras
bundle?.let {
bundle.apply {
//Intent com dados
val string: String? = getString("keyString")
textView.text = string
val myArray: ArrayList? = getStringArrayList("myArray")
showToast(message = "MyArrayList size:${myArray?.size}")
val arrayList: ArrayList? = getStringArrayList("arrayList")
showToast(message = "ArrayList size:${arrayList?.size}")
val float: Float? = bundle.get("keyFloat") as Float?
var boolean = bundle.get("boolean") as? Boolean
showToast(message = "Float data is:$float")
showToast(message = "Boolean data is:$boolean")
boolean = bundle.get("keyBoolean") as? Boolean
showToast(message = "Boolean correct key data is:$boolean")
}
bundle.apply {
//Dados serializáveis
val blog = getSerializable("blogData") as Blog?
if (blog != null) {
textView.text = "Blog name is ${blog?.name}. Year started: ${blog?.year}"
}
}
bundle.apply {
//Dados parceláveis
val student: Student? = getParcelable("studentData")
if (student != null) {
textView.text = "Name is ${student?.name}. Age: ${student?.age}"
}
}
}
}
private fun showToast(context: Context = applicationContext, message: String, duration: Int = Toast.LENGTH_SHORT) {
if (!message.contains("null"))
Toast.makeText(context, message, duration).show()
}
}
Utilizamos let
e apply
para lidar com tipos nulos e evitar chamar bundle.field em cada linha. A saída da aplicação acima em ação é apresentada abaixo: Isso encerra este tutorial sobre intents do Android em Kotlin. Você pode baixar o projeto a partir do link abaixo.