Getting the result right (Part 1). Activity Result API

Every Android developer faced the need to transfer data from one Activity to another. This trivial task often forces us to write less elegant code. 





Finally, in 2020, Google introduced a solution to an old problem - the Activity Result API. It is a powerful tool for exchanging data between activities and requesting runtime permissions. 





In this article, we will understand how to use the new API and what advantages it has.





What's wrong with 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()



    c   PackageManager



    .





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


      
      



Let's put the knowledge of the new API into practice and rewrite the screen from the first example with their help. As a result, we get a fairly compact, easily readable and scalable code:





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


      
      



We saw the disadvantages of communicating via onActivityResult (), learned about the advantages of the Activity Result API, and learned how to use it in practice.  





The new API is completely stable, while the usual onRequestPermissionsResult()



, onActivityResult()



and startActivityForResult()



began Deprecated. It's time to make changes to your projects! 





A demo application with various examples of using the Activty Result API, including working with runtime permissions, can be found in my Github repository .








All Articles