Todos los desarrolladores de Android se enfrentaron a la necesidad de transferir datos de una actividad a otra. Esta tarea trivial a menudo nos obliga a escribir código menos elegante.
Finalmente, en 2020, Google presentó una solución a un viejo problema: la API de resultados de actividad. Es una herramienta poderosa para intercambiar datos entre actividades y solicitar permisos de tiempo de ejecución.
En este artículo, entenderemos cómo utilizar la nueva API y qué ventajas tiene.
¿Qué pasa con 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)
}
}
}
}
Pongamos en práctica el conocimiento de la nueva API y reescribamos la pantalla del primer ejemplo con su ayuda. Como resultado, obtenemos un código bastante compacto, fácilmente legible y escalable:
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 las desventajas de comunicarse a través de onActivityResult (), aprendimos sobre las ventajas de la API de resultados de actividad y aprendimos cómo usarla en la práctica.
La nueva API es completamente estable, mientras que la habitual onRequestPermissionsResult()
, onActivityResult()
y startActivityForResult()
comenzó en desuso. ¡Es hora de hacer cambios en tus proyectos!
En mi repositorio de Github se puede encontrar una aplicación de demostración con varios ejemplos del uso de Activty Result API, incluido el trabajo con permisos de tiempo de ejecución .