3. Использование неявных намерений. Фильтры намерений

Рассмотрим пример использования неявных намерений.

Пример работы неявного намерения

Представим, что мы хотим выслать некоторую информацию с помощью электронной почты. Создадим проект с одним Activity и кнопкой, по нажатию на которую должно открыться приложение для отправки электронной почты. Напишем обработчик нажатия на кнопку

MainActivity.java
public void handleClick(View view) {
    Intent i = new Intent(Intent.ACTION_SEND);
    
    i.setType("text/plain");
    i.putExtra(Intent.EXTRA_SUBJECT,"Важное сообщение");
    i.putExtra(Intent.EXTRA_TEXT,"Привет, как дела?");
    
    startActivity(i);
}

Действие, которое мы хотим совершить, является обычной текстовой строкой. Набор стандартных действий содержится в классе Intent в виде набора статических констант. В этом же классе есть статические константы для указания дополнительных параметров действия. В нашем случае, это тема письма и содержимое сообщения. Метод setType() устанавливает MIME-тип сообщения.

После указания параметров сообщения, мы вызываем метод startActivity(), операционная система выбирает нужное Activity из списка всех Activity приложений, которые установлены на данном устройстве. Таким образом, с помощью неявного намерения мы можем открыть Activity другого приложения.

Запустим приложение в эмуляторе и нажмем на кнопку.

Как мы видим, операционная система показала диалоговое окно с предложением выбрать приложение, которое может обработать данное действие. Выбираем почтовый клиент Gmail (предварительно добавьте в приложение почтовый ящик) и видим, что тема и текст письма заполнены корректно.

Что будет, если никто не сможет обработать неявное намерение?

А что случится, если операционная система не найдет подходящее Activity, которое может обработать событие? Так как действие для обработки представляет собой обычную строку, мы можем указать произвольную строку в качестве действия.

MainActivity.java
public void handleClick(View view) {
    Intent i = new Intent("some_random_action");

    i.setType("text/plain");
    i.putExtra(Intent.EXTRA_SUBJECT,"Важное сообщение");
    i.putExtra(Intent.EXTRA_TEXT,"Привет, как дела?");

    startActivity(i);
}

Запустим приложение и нажмем на кнопку.

Как мы видим, было выброшено исключение и приложение закрылось с ошибкой. Если посмотреть логи устройства, можем обнаружить следующее сообщение

Операционная система не нашла подходящее Activity для обработки этого действия и выбросила исключение.

Чтобы обезопасить себя от подобных ситуаций, мы можем предварительно запросить у операционной системы список Activity, которое может обработать то или иное действие, и если список будет нулевой длины, мы можем не высылать неявное намерение, выдать пользователю сообщение о том, что невозможно выполнить действие или еще каким-либо образом обработать эту ситуацию и избежать закрытия приложения с ошибкой.

MainActivity.java
public void handleClick(View view) {

    Intent i = new Intent("some_random_action");

    i.setType("text/plain");
    i.putExtra(Intent.EXTRA_SUBJECT, "Важное сообщение");
    i.putExtra(Intent.EXTRA_TEXT, "Привет, как дела?");

    // Получаем Package Manager
    PackageManager manager = this.getPackageManager();
    // Получаем список обработчиков намерения
    List<ResolveInfo> list = manager.queryIntentActivities(i, 0);

    if (list.size() > 0) {
        startActivity(i);
    } else {
        Toast.makeText(this,"Невозможно обработать намерение!",Toast.LENGTH_LONG).show();
    }
}

И теперь вместо закрытия приложения с ошибкой, пользователь видит следующее сообщение

Выбор обработчика намерения и самостоятельная обработка намерений

Давайте задумаемся, а как операционная система знает, какое Activity может обработать то или иное неявное намерение и можем ли мы создать Actvity, которое будет обрабатывать те или иные неявные намерения?

Мы интуитивно понимаем, что операционная система не обладает искусственным интеллектом и где-то должна быть указана информация о том, что то или иное Activity может обработать то или иное действие.

Данные об этом хранятся в манифесте приложения. При установке приложения, операционная система считывает файл манифеста и записывает информацию об Activity и о том, какие действия эта Activity может обработать.

В качестве примера создадим в тестовом приложении еще одно Activity и пропишем в файле манифеста следующую информацию

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="ua.opu.pnit.lab2project">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        tools:ignore="GoogleAppIndexingWarning">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- Объявляем второе Activity в манифесте и указываем action для обработки -->
        <activity android:name=".SecondActivity">
            <intent-filter>
                <action android:name="android.intent.action.SEND" />
                <data android:mimeType="text/plain"/>
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        
    </application>
</manifest>

Как видите, мы используем элемент <intent-filter>, внутри которого прописываем action и некоторые дополнительные параметры действия (например, в данном случае мы можем обработать ACTION_SEND где тип сообщения text/plain).

В обработчике кнопки изменим действие на Intent.ACTION_SEND и запустим эмулятор.

Как видим, в списке предложений, которые могут обработать действие Intent.ACTION_SEND появилось наше приложение. Если мы выберем приложение, будет запущено SecondActivity.

Кстати обратите внимание на <intent-filter> для MainActivity

AndroidManifest.xml
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

Значение ACTION_MAIN означает, что это Activity является входной точкой приложения - стартовым окном. Значение CATEGORY_LAUNCHER означает, что данное приложение должно отображаться в лаунчере устройства.

Таким образом, MainActivity является стартовым окном приложения. Если вы хотите изменить стартовое окно, просто переместите элемент <intent-filter> из элемента MainActivity в другое Activity.

Last updated