startActivityForResult deprecated no Android, e agora?
Alex Felipe
Posted on July 23, 2021
Você tentou utilizar o startActivityResultFor()
e notou que agora ele é deprecated? E então, ficou em dúvida sem saber o que fazer? Se sim, neste artigo eu vou mostrar pra você como lidar com essa situação.
Código de demonstração
Como exemplo, vou utilizar este código que inicializa uma Activity para tirar uma foto:
class MainActivity : AppCompatActivity() {
private val binding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
binding.button.setOnClickListener {
startActivityForResult(
Intent(MediaStore.ACTION_IMAGE_CAPTURE),
REQUEST_IMAGE_CAPTURE
)
}
}
override fun onActivityResult(
requestCode: Int,
resultCode: Int,
data: Intent?
) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_IMAGE_CAPTURE &&
resultCode == RESULT_OK
) {
(data?.extras?.get("data") as? Bitmap?)?.let { foto ->
binding.imageView.setImageBitmap(foto)
}
}
}
}
Ao rodar o App, temos o seguinte resultado:
Se você nunca fez esse tipo de implementação, confira este vídeo de Alura+ que fiz um exemplo com o
startActivityResultFor()
.
Falando um pouco de deprecated
Em casos que existe alguma classe ou método deprecated, significa que há uma maneira mais adequada de implementação.
Para isso, a equipe de desenvolvedores do Android, sugere o uso da nova API de Activity Result para fazer essas tarefas. Sendo assim, vou demonstrar como funciona essa API e como esse mesmo código é escrito com ela.
Conhecendo a nova API
Diferente de sobrescrever um método da Activity, na nova API, o nosso primeiro passo é registrar um resultado de callback a partir do método registerForActivityResult()
.
Neste método, como primeiro argumento, é esperado o envio de ActivityResultContract
, uma classe abstrata responsável em determinar qual o tipo de dado que enviamos ao inicializar uma Activity e o tipo de dado que recebemos como resultado do callback.
A própria API oferece algumas implementações padrões pra isso ou permite que a gente implemente o nosso.
Utilizando implementação padrão da ActivityResultContract
Considerando que neste exemplo queremos chamar um App de camera, já existe uma implementação padrão, a TakePicturePreview. A partir dela, temos acesso a um callback que devolve um Bitmap?
, a foto tirada, como parâmetro da expressão lambda:
class MainActivity : AppCompatActivity() {
private val binding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}
private val register = registerForActivityResult(
ActivityResultContracts.TakePicturePreview()
) { image: Bitmap? ->
}
//rest of the code
}
Inicializando a Activity
Note que criamos a property register
com o retorno da registerForActivityResult
que é do tipo ActivityResultLauncher
. Por meio dela, podemos inicializar a Activity configurada com a TakePicturePreview
chamando o método launcher()
.
class MainActivity : AppCompatActivity() {
private val binding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}
private val register = registerForActivityResult(
ActivityResultContracts.TakePicturePreview()
) { image: Bitmap? ->
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
binding.button.setOnClickListener {
register.launch(null)
}
}
}
Observe que nesta chamada foi enviado o
null
, isso acontece porque na implementaçãoTakePicturePreview()
recebe o tipoVoid!
, ou seja, nenhum dado e por isso podemos enviarnull
.
Com apenas esse código, somos capazes de abrir a câmera ao clicar no botão, e então, na expressão lambda do registerForActivityResult()
, podemos carregar a imagem e chegar ao mesmo resultado da implementação anterior:
private val register = registerForActivityResult(
ActivityResultContracts.TakePicturePreview()
) { image: Bitmap? ->
image?.let {
binding.imageView.setImageBitmap(image)
}
}
Bem simples, né? Nesse momento, talvez você está com a seguinte dúvida:
"Legal, mas e se a minha inicialização por um resultado não for atendida por alguma implementação de contrato padrão?"
Além de implementações mais específicas, também temos acesso a um contrato mais genérico que permite reutilizar Intent
s, ou seja, vamos explorar como podemos utilizá-lo.
Contrato de inicialização genérica
A StartActivityForResult
é uma implementação de contrato que envia uma Intent
na inicialização e recebe um ActivityResult
via callback, que é uma referência capaz de identificar se teve um resultado de sucesso e permiter recuperar dados a partir de uma chave.
Para exemplificar uma implementação, vou considerar a seguinte Activity:
private const val TAG = "TestResultApi"
class TestResultActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test_result)
if (intent.hasExtra("dataTest")) {
Log.i(
TAG, "onCreate: ${intent.getStringExtra("dataTest")}"
)
}
Intent().apply {
putExtra("resultTest", "result ok")
setResult(RESULT_OK, this)
}
finish()
}
}
Note que a TestResultActivity
espera um dado via extra com a chave "dataTest"
, faz o log da informação, cria um dado de retorno via extra com a chave "resultTest"
com o valor "result ok"
e finaliza a Activity. Então, no nosso código que registramos esse resultado podemos fazer as seguintes modificações para fazer o nosso teste:
private const val TAG = "MainActivity"
class MainActivity : AppCompatActivity() {
private val binding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}
private val register = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result: ActivityResult ->
if (result.resultCode == RESULT_OK) {
result.data?.let {
if (it.hasExtra("resultTest")) {
Log.i(TAG, "callback result: ${it.getStringExtra("resultTest")}")
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
binding.button.setOnClickListener {
Intent(this, TestResultActivity::class.java).let {
it.putExtra("dataTest", "test string")
register.launch(it)
}
}
}
}
Observe que agora enviamos uma Intent
com extra e, no callback, verificamos se o código do resultado está ok e se temos o dado esperado para então fazer o log. Ao rodar o App, clicar no botão de tirar e verificar os logs temos o seguinte resultado:
br.com.alura.alugram I/TestResultApi: onCreate: test string
br.com.alura.alugram I/MainActivity: callback result: result ok
Como podemos notar, as mensagens foram apresentadas como o esperado!
Conclusão
Considerando todos os pontos de mudança, não precisamos mais utilizar a implementação antiga para entregar o mesmo comportamento nessa comunicação entre Activities esperando um resultado. É válido ressaltar que em alguns casos a adaptação pode ficar mais complicada, se for o seu caso, considere uma leitura mais aprofundada nesta página da documentação.
Agora que finalizamos, aproveite para comentar, curtir e compartilhar esse artigo para outras pessoas aprenderem a lidar com essa situação também! 😉
Além disso, na Alura, temos diversos cursos de Android caso você tenha interesse em aprender desde o zero ou aperfeiçoar os seus conhecimentos! E se você ainda não assinou a Alura e tem interesse, aproveite esse cupom de 10% de desconto!
Posted on July 23, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.