A first look at AndroidX Activity Result APIs — небольшая заметка о решении одной из самых раздражающих задач, возникающих при разработке приложений для Android.
Речь идет о функции startActivityForResult(), которая позволяет запустить активность другого приложения, чтобы переложить на нее решение определенной задачи: получение снимка с помощью камеры, выбор файла и так далее. Данный механизм серьезно облегчает жизнь разработчика, но реализован самым неудобным из возможных способов. Разработчику необходимо запустить активность, передав ей специальный код, а затем ждать результат в колбэке, реализованном с помощью переопределения метода onActivityResult() в активности или фрагменте. И все бы ничего, но точно таким же способом реализован запрос полномочий, так что код приложения в итоге расползается по множеству внешне никак не связанных между собой функций.
РЕКОМЕНДУЕМ:
Как написать приложение с помощью JavaFX
Существует масса способов решения этой проблемы с помощью сторонних библиотек, но эта статья рассказывает об официальном решении от Google. В альфа-версии библиотеки AndroidX Activity наконец появился удобный в использовании API, позволяющий работать с активностями других приложений, не размазывая код по активности своего приложения.
Для начала нужно с помощью контракта описать интент, который будет использован для запуска активности, и обработчик результата выполнения активности:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class MyContract : ActivityResultContract<Int, String>() { companion object { const val ACTION = "com.myapp.action.MY_ACTION" const val INPUT_INT = "input_int" const val OUTPUT_STRING = "output_string" } override fun createIntent(input: Int): Intent { return Intent(ACTION) .apply { putExtra(INPUT_INT, input) } } override fun parseResult(resultCode: Int, intent: Intent?): String? { return when (resultCode) { Activity.RESULT_OK -> intent?.getStringExtra(OUTPUT_STRING) else -> null } } } |
Затем мы используем prepareCall(), чтобы создать объект класса ActivityResultsLauncher, с помощью которого запускаем активность и получаем результат в колбэке:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class MyActivity : AppCompatActivity() { private val myActionCall = prepareCall(MyContract()) { result -> Log.i("MyActivity", "Obtained result: $result") } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... button.setOnClickListener { myActionCall(500) } } } |
Выглядит немного сложно. Но такой подход не разрушает связность кода. Кроме того, уже сейчас библиотека содержит несколько предопределенных контрактов, в том числе для получения снимка (TakePicture), выполнения звонка (Dial) и, конечно же, запроса полномочий (RequestPermission).