15
startActivityForResult deprecated no Android, e agora?
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.
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:
![](https://res.cloudinary.com/practicaldev/image/fetch/s--LvzK6BDq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ca6fag4ky7u8v3kt0jl3.gif)
Se você nunca fez esse tipo de implementação, confira este vídeo de Alura+ que fiz um exemplo com o
startActivityResultFor()
.
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.
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.
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
}
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.
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!
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!