Todo desenvolvedor Android enfrentou a necessidade de transferir dados de uma atividade para outra. Essa tarefa trivial geralmente nos força a escrever um código menos elegante.
Finalmente, em 2020, o Google introduziu uma solução para um problema antigo - a API Activity Result. É uma ferramenta poderosa para trocar dados entre atividades e solicitar permissões de tempo de execução.
Neste artigo, vamos entender como usar a nova API e quais as vantagens que ela tem.
O que há de errado com onActivityResult ()?
“ ” — DRY Don’t repeat yourself, , .
onActivityResult()
, . , , — SecondActivity
. SecondActivity
, .
class OldActivity : AppCompatActivity(R.layout.a_main) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
vButtonCamera.setOnClickListener {
when {
checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED -> {
// ,
startActivityForResult(
Intent(MediaStore.ACTION_IMAGE_CAPTURE),
PHOTO_REQUEST_CODE
)
}
shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) -> {
// ,
}
else -> {
// ,
requestPermissions(
arrayOf(Manifest.permission.CAMERA),
PHOTO_PERMISSIONS_REQUEST_CODE
)
}
}
}
vButtonSecondActivity.setOnClickListener {
val intent = Intent(this, SecondActivity::class.java)
.putExtra("my_input_key", "What is the answer?")
startActivityForResult(intent, SECOND_ACTIVITY_REQUEST_CODE)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
PHOTO_REQUEST_CODE -> {
if (resultCode == RESULT_OK && data != null) {
val bitmap = data.extras?.get("data") as Bitmap
// bitmap
} else {
//
}
}
SECOND_ACTIVITY_REQUEST_CODE -> {
if (resultCode == RESULT_OK && data != null) {
val result = data.getIntExtra("my_result_extra")
// result
} else {
//
}
}
else -> super.onActivityResult(requestCode, resultCode, data)
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (requestCode == PHOTO_PERMISSIONS_REQUEST_CODE) {
when {
grantResults[0] == PackageManager.PERMISSION_GRANTED -> {
// ,
startActivityForResult(
Intent(MediaStore.ACTION_IMAGE_CAPTURE),
PHOTO_REQUEST_CODE
)
}
!shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) -> {
// , Don't ask again.
}
else -> {
// ,
}
}
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
}
companion object {
private const val PHOTO_REQUEST_CODE = 1
private const val PHOTO_PERMISSIONS_REQUEST_CODE = 2
private const val SECOND_ACTIVITY_REQUEST_CODE = 3
}
}
, onActivityResult()
, Activity. , .
, , .
Activity Result API
API AndroidX Activity 1.2.0-alpha02
Fragment 1.3.0-alpha02
, build.gradle:
implementation 'androidx.activity:activity-ktx:1.3.0-alpha02'
implementation 'androidx.fragment:fragment-ktx:1.3.0'
Activity Result :
1.
— , ActivityResultContract<I,O>.
I
, Activity, O
— .
“ ”: PickContact
, TakePicture
, RequestPermission
. .
:
createIntent()
— , launch()
parseResult()
— , resultCode
— getSynchronousResult()
— . , Activity, , , . , null
.
, SecondActivity, :
class MySecondActivityContract : ActivityResultContract<String, Int?>() {
override fun createIntent(context: Context, input: String?): Intent {
return Intent(context, SecondActivity::class.java)
.putExtra("my_input_key", input)
}
override fun parseResult(resultCode: Int, intent: Intent?): Int? = when {
resultCode != Activity.RESULT_OK -> null
else -> intent?.getIntExtra("my_result_key", 42)
}
override fun getSynchronousResult(context: Context, input: String?): SynchronousResult<Int?>? {
return if (input.isNullOrEmpty()) SynchronousResult(42) else null
}
}
2.
— registerForActivityResult()
. ActivityResultContract
ActivityResultCallback
. .
val activityLauncher = registerForActivityResult(MySecondActivityContract()) { result ->
// result
}
Activity
, ActivityResultLauncher
, .
3.
Activity launch()
ActivityResultLauncher
, .
vButton.setOnClickListener {
activityLauncher.launch("What is the answer?")
}
!
, :
, CREATED . — .
registerForActivityResult()
if
when
. , (, , ). , .
, Activity, ActivityNotFoundException: “No Activity found to handle Intent”. ,
launch()
getSynchronousResult()
resolveActivity()
cPackageManager
.
runtime permissions
Activity Result API . checkSelfPermission()
, requestPermissions()
onRequestPermissionsResult()
, — RequestPermission
RequestMultiplePermissions
.
, — . RequestPermission
true
, , false
. RequestMultiplePermissions
Map
, — , — .
. Google :
runtime permissions:
, , ( 5a)
( 8b), , , “Don't ask again”
shouldShowRequestPermissionRationale()
. true
, , . shouldShowRequestPermissionRationale()
false
— “Don't ask again”, .
:
class PermissionsActivity : AppCompatActivity(R.layout.a_main) {
val singlePermission = registerForActivityResult(RequestPermission()) { granted ->
when {
granted -> {
// ,
}
!shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) -> {
// , Don't ask again.
}
else -> {
// ,
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
vButtonPermission.setOnClickListener {
if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
// ,
} else {
singlePermission.launch(Manifest.permission.CAMERA)
}
}
}
}
Vamos colocar o conhecimento da nova API em prática e reescrever a tela do primeiro exemplo com a ajuda deles. Como resultado, obtemos um código bastante compacto, facilmente legível e escalonável:
class NewActivity : AppCompatActivity(R.layout.a_main) {
val permission = registerForActivityResult(RequestPermission()) { granted ->
when {
granted -> {
camera.launch() // ,
}
!shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) -> {
// , Don't ask again.
}
else -> {
//
}
}
}
val camera = registerForActivityResult(TakePicturePreview()) { bitmap ->
// bitmap
}
val custom = registerForActivityResult(MySecondActivityContract()) { result ->
// result
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
vButtonCamera.setOnClickListener {
if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
// ,
} else {
permission.launch(Manifest.permission.CAMERA)
}
}
vButtonSecondActivity.setOnClickListener {
custom.launch("What is the answer?")
}
}
}
Vimos as desvantagens da comunicação por meio de onActivityResult (), aprendemos sobre as vantagens da API Activity Result e aprendemos como usá-la na prática.
A nova API é completamente estável, embora o normal onRequestPermissionsResult()
, onActivityResult()
e startActivityForResult()
começou obsoleta. É hora de fazer alterações em seus projetos!
Um aplicativo de demonstração com vários exemplos de uso da API de resultado do Activty, incluindo trabalho com permissões de tempo de execução, pode ser encontrado em meu repositório Github .