- StartActivityForResult is deprecatedВ !!
- #Introduction
- #Example 1: Activity A calls Activity B for the result
- #Example 2: Start external component like the camera to get the image:
- #Example 3: Fragment A calls Activity B for the result
- #Example 4: Receive the result in a non-Android class
- Получаем результат правильно (Часть 1). Activity Result API
- Чем плох onActivityResult()?
- Используем Activity Result API
- Шаг 1. Создание контракта
- Шаг 2. Регистрация контракта
- Шаг 3. Запуск контракта
- Важно!
- Работа с runtime permissions
- Подводим итоги
- StartActivityForResult is deprecated !!
- Introduction
- Example 1: Activity A calls Activity B for the result
- Example 2: Start external component like the camera to get the image:
- Example 3: Fragment A calls Activity B for the result
- Summary
- StartActivityForResult is deprecated !!
- Introduction
- Example 1: Activity A calls Activity B for the result
- Example 2: Start external component like the camera to get the image:
- Example 3: Fragment A calls Activity B for the result
- Example 4: Receive the result in a non-Android class
- Summary
StartActivityForResult is deprecatedВ !!
Updated: Nov 01, 2021 |
Published: Sep 14, 2021
#Introduction
Android has been on the edge of evolution for a while recently, with updates to androidx.activity:activity-ktx to 1.2.0 . It has deprecated startActivityForResult in favour of registerForActivityResult .
It was one of the first fundamentals that any Android developer has learned, and the backbone of Android’s way of communicating between two components. API design was simple enough to get started quickly but had its cons, like how it’s hard to find the caller in real-world applications (except for cmd+F in the project рџ‚), getting results on the fragment, results missed if the component is recreated, conflicts with the same request code, etc.
Let’s try to understand how to use the new API with a few examples.
#Example 1: Activity A calls Activity B for the result
As you would have noticed, registerForActivityResult takes two parameters. The first defines the type of action/interaction needed ( ActivityResultContracts ) and the second is a callback function where we receive the result.
Nothing much has changed, right? Let’s check another example.
#Example 2: Start external component like the camera to get the image:
The above snippet is the complete code getting a preview image from the camera. No need for permission request code, as this is taken care of automatically for us!
Another benefit of using the new API is that it forces developers to use the right contract. For example, with ActivityResultContracts.TakePicture() — which returns the full image — you need to pass a URI as a parameter to launch , which reduces the development time and chance of errors.
Other default contracts available can be found here.
#Example 3: Fragment A calls Activity B for the result
This has been another issue with the old system, with no clean implementation available, but the new API works consistently across activities and fragments. Therefore, we refer and add the snippet from example 1 to our fragments.
#Example 4: Receive the result in a non-Android class
With the new API, this is possible using ActivityResultRegistry directly.
Источник
Получаем результат правильно (Часть 1). Activity Result API
Каждый Android-разработчик сталкивался с необходимостью передать данные из одной Activity в другую. Эта тривиальная задача зачастую вынуждает нас писать не самый элегантный код.
Наконец, в 2020 году Google представила решение старой проблемы — Activity Result API. Это мощный инструмент для обмена данными между активностями и запроса runtime permissions.
В данной статье мы разберёмся, как использовать новый API и какими преимуществами он обладает.
Чем плох onActivityResult()?
Роберт Мартин в книге “Чистый код” отмечает важность переиспользования кода — принцип DRY или Don’t repeat yourself, а также призывает проектировать компактные функции, которые выполняют лишь единственную операцию.
Проблема onActivityResult() в том, что при его использовании соблюдение подобных рекомендаций становится практически невозможным. Убедимся в этом на примере простого экрана, который запрашивает доступ к камере, делает фото и открывает второй экран — SecondActivity . Пусть в SecondActivity мы передаём строку, а назад получаем целое значение.
Очевидно, что метод onActivityResult() нарушает принцип единственной ответственности, ведь он отвечает и за обработку результата фотографирования и за получение данных от второй Activity. Да и выглядит этот метод уже довольно запутанно, хоть мы и рассмотрели простой пример и опустили часть деталей.
Кроме того, если в приложении появится другой экран со схожей функциональностью, мы не сможем переиспользовать этот код и будем вынуждены его дублировать.
Используем Activity Result API
Новый API доступен начиная с AndroidX Activity 1.2.0-alpha02 и Fragment 1.3.0-alpha02 , поэтому добавим актуальные версии соответствующих зависимостей в build.gradle:
Применение Activity Result состоит из трех шагов:
Шаг 1. Создание контракта
Контракт — это класс, реализующий интерфейс ActivityResultContract . Где I определяет тип входных данных, необходимых для запуска Activity, а O — тип возвращаемого результата.
Для типовых задач можно воспользоваться реализациями “из коробки”: PickContact , TakePicture , RequestPermission и другими. Полный список доступен тут.
При создании контракта мы обязаны реализовать два его метода:
createIntent() — принимает входные данные и создает интент, который будет в дальнейшем запущен вызовом launch()
parseResult() — отвечает за возврат результата, обработку resultCode и парсинг данных
Ещё один метод — getSynchronousResult() — можно переопределить в случае необходимости. Он позволяет сразу же, без запуска Activity, вернуть результат, например, если получены невалидные входные данные. Если подобное поведение не требуется, метод по умолчанию возвращает null .
Ниже представлен пример контракта, который принимает строку и запускает SecondActivity, ожидая от неё целое число:
Шаг 2. Регистрация контракта
Следующий этап — регистрация контракта в активности или фрагменте с помощью вызова registerForActivityResult() . В параметры необходимо передать ActivityResultContract и ActivityResultCallback . Коллбек сработает при получении результата.
Регистрация контракта не запускает новую Activity , а лишь возвращает специальный объект ActivityResultLauncher , который нам понадобится далее.
Шаг 3. Запуск контракта
Для запуска Activity остаётся вызвать launch() на объекте ActivityResultLauncher , который мы получили на предыдущем этапе.
Важно!
Отметим несколько неочевидных моментов, которые необходимо учитывать:
Регистрировать контракты можно в любой момент жизненного цикла активности или фрагмента, но вот запустить его до перехода в состояние 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”, тогда стоит попросить пользователя зайти в настройки и предоставить разрешение вручную.
Реализуем запрос на доступ к камере согласно рассмотренной схеме:
Подводим итоги
Применим знания о новом API на практике и перепишем с их помощью экран из первого примера. В результате мы получим довольно компактный, легко читаемый и масштабируемый код:
Мы увидели недостатки обмена данными через onActivityResult(), узнали о преимуществах Activity Result API и научились использовать его на практике.
Новый API полностью стабилен, в то время как привычные onRequestPermissionsResult() , onActivityResult() и startActivityForResult() стали Deprecated. Самое время вносить изменения в свои проекты!
Демо-приложение с различными примерами использования Activty Result API, в том числе работу с runtime permissions, можно найти в моем Github репозитории.
Источник
StartActivityForResult is deprecated !!
Sep 24 · 3 min read
Introduction
Android has been on the edge of evolution for a while recently, with updates to androidx.activity:activity-ktx to 1.2.0 . It has deprecated startActivityForResult in favour of registerForActivityResult .
It was one of the first fundamentals that any Android developer has learne d , and the backbone of Android’s way of communicating between two components. API design was simple enough to get started quickly but had its cons, like how it’s hard to find the caller in real-world applications (except for cmd+F in the project 😂), getting results on the fragment, results missed if the component is recreated, conflicts with the same request code, etc.
Let’s try to understand how to use the new API with a few examples.
Example 1: Activity A calls Activity B for the result
As you would have noticed, registerForActivityResult takes two parameters. The first defines the type of action/interaction needed ( ActivityResultContracts ) and the second is a callback function where we receive the result.
Nothing much has changed, right? Let’s check another example.
Example 2: Start external component like the camera to get the image:
The above snippet is the complete code getting a preview image from the camera. No need for permission request code, as this is taken care of automatically for us!
Another benefit of using the new API is that it forces developers to use the right contract. For example, with ActivityResultContracts.TakePicture() — which returns the full image — you need to pass an URI as a parameter to launch , which reduces the development time and chance of errors.
Other default contracts available can be found here.
Example 3: Fragment A calls Activity B for the result
This has been another issue with the old system, with no clean implementation available, but the new API works consistently across activities and fragments. Therefore, we refer and add the snippet from example 1 to our fragments.
Example 4: Receive the result in a non-Android class
With the new API, this is possible using ActivityResultRegistry directly.
Summary
I have found the registerForActivityResult useful and clean. Some of the pros, in my opinion, are:
- Improve the code readability, no need to remember to jump to onActivityResult() after startActivityForResult .
- ActivityResultLauncher returned from registerForActivityResult used to launch components, clearly defining the input parameter for desired results.
- Removed the boilerplate code for requesting permission from the user.
Hope this was informative and enjoyed reading it.
Источник
StartActivityForResult is deprecated !!
Introduction
Android has been on the edge of evolution for a while recently, with updates to androidx.activity:activity-ktx
to 1.2.0 . It has deprecated startActivityForResult in favour of registerForActivityResult .
It was one of the first fundamentals that any Android developer has learned, and the backbone of Android’s way of
communicating between two components. API design was simple enough to get started quickly but had its cons, like how
it’s hard to find the caller in real-world applications (except for cmd+F in the project 😂), getting results on the
fragment, results missed if the component is recreated, conflicts with the same request code, etc.
Let’s try to understand how to use the new API with a few examples.
Example 1: Activity A calls Activity B for the result
Exit fullscreen mode
Exit fullscreen mode
As you would have noticed, registerForActivityResult takes two parameters. The first defines the type of
action/interaction needed ( ActivityResultContracts ) and the second is a callback function where we receive the result.
Nothing much has changed, right? Let’s check another example.
Example 2: Start external component like the camera to get the image:
Exit fullscreen mode
The above snippet is the complete code getting a preview image from the camera. No need for permission request code, as
this is taken care of automatically for us!
Another benefit of using the new API is that it forces developers to use the right contract. For example,
with ActivityResultContracts.TakePicture() — which returns the full image — you need to pass a URI as a parameter
to launch , which reduces the development time and chance of errors.
Other default contracts available can be
found here.
Example 3: Fragment A calls Activity B for the result
This has been another issue with the old system, with no clean implementation available, but the new API works
consistently across activities and fragments. Therefore, we refer and add the snippet from example 1 to our fragments.
Example 4: Receive the result in a non-Android class
With the new API, this is possible using ActivityResultRegistry directly.
Exit fullscreen mode
Summary
I have found the registerForActivityResult useful and clean. Some of the pros, in my opinion, are:
Improve the code readability, no need to remember to jump to onActivityResult() after startActivityForResult .
ActivityResultLauncher returned from registerForActivityResult used to launch components, clearly defining the
input parameter for desired results.
Removed the boilerplate code for requesting permission from the user.
Hope this was informative and enjoyed reading it.
Источник