startActivityForResult deprecated no Android, e agora?

alexfelipe

Alex Felipe

Posted on July 23, 2021

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.

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

}


Enter fullscreen mode Exit fullscreen mode

Ao rodar o App, temos o seguinte resultado:

Alt Text

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

}


Enter fullscreen mode Exit fullscreen mode

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

}


Enter fullscreen mode Exit fullscreen mode

Observe que nesta chamada foi enviado o null, isso acontece porque na implementação TakePicturePreview() recebe o tipo Void!, ou seja, nenhum dado e por isso podemos enviar null.

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


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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

}


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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!

💖 💪 🙅 🚩
alexfelipe
Alex Felipe

Posted on July 23, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

What was your win this week?
weeklyretro What was your win this week?

November 29, 2024

Where GitOps Meets ClickOps
devops Where GitOps Meets ClickOps

November 29, 2024

How to Use KitOps with MLflow
beginners How to Use KitOps with MLflow

November 29, 2024