startActivityForResult deprecated no Android, e agora?
Published: 2021-07-23 13:02:34

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:

```kotlin
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:

![Alt Text](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+](https://www.youtube.com/watch?v=jLcrcTMoB88) 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](https://developer.android.com/training/basics/intents/result). 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](https://developer.android.com/reference/androidx/activity/result/contract/ActivityResultContracts) ou [permite que a gente implemente o nosso](https://developer.android.com/training/basics/intents/result#custom).

## 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](https://developer.android.com/reference/androidx/activity/result/contract/ActivityResultContracts.TakePicturePreview). A partir dela, temos acesso a um callback que devolve um `Bitmap?`, a foto tirada, como parâmetro da expressão lambda:

```kotlin
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()`.

```kotlin
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çã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:

```kotlin
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:

```kotlin
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:

```kotlin
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](https://developer.android.com/training/basics/intents/result).

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](https://www.alura.com.br/cursos-online-mobile/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](https://www.alura.com.br/promocao/alexfelipe)!