intent.resolveActivity возвращает null в API 30

Просмотр intent.resolveActivity! = Null, но запуск намерения выдает исключение ActivityNotFound Я написал, открывая браузер или приложение с глубинными ссылками:

private fun openUrl(url: String) {
    val intent = Intent().apply {
        action = Intent.ACTION_VIEW
        data = Uri.parse(url)
//        setDataAndType(Uri.parse(url), "text/html")
//        component = ComponentName("com.android.browser", "com.android.browser.BrowserActivity")
//        flags = Intent.FLAG_ACTIVITY_CLEAR_TOP + Intent.FLAG_GRANT_READ_URI_PERMISSION
    }
    val activityInfo = intent.resolveActivityInfo(packageManager, intent.flags)
    if (activityInfo?.exported == true) {
        startActivity(intent)
    } else {
        Toast.makeText(
            this,
            "No application can handle the link",
            Toast.LENGTH_SHORT
        ).show()
    }
}

Не работает. В эмуляторе API 30 не обнаружен браузер, а обычный решение работает:

private fun openUrl(url: String) {
    val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
    try {
        startActivity(intent)
    } catch (e: ActivityNotFoundException) {
        Toast.makeText(
            this,
            "No application can handle the link",
            Toast.LENGTH_SHORT
        ).show()
    }
}

Первый метод не работает, потому что intent.resolveActivityInfo или intent.resolveActivity возвращает null. Но для средства просмотра PDF он работает.

Должны ли мы уволить intent.resolveActivity?


person CoolMind    schedule 23.06.2020    source источник
comment
Предполагая, что вы нацеливаетесь на уровень API 30, это, по-видимому, связано с следующим: Видимость пакета в Android 11. Действительно, когда я тестирую ваш первый фрагмент с соответствующим элементом <queries> в манифесте, он работает, как ожидалось. Если вы предпочитаете не включать такой <queries>, то можете просто придерживаться _3 _-_ 4_.   -  person Mike M.    schedule 23.06.2020
comment
@MikeM., Спасибо! Не могли бы вы опубликовать это в качестве ответа? Позже протестирую.   -  person CoolMind    schedule 23.06.2020
comment
Ой, извините, я неправильно прочитал ваш комментарий. Я думал, ты его выложишь после тестов. Я не могу дать правильный ответ прямо сейчас, но вернусь к нему позже, когда у меня появится свободное время. Если вы просто хотите, чтобы этот вопрос был закончен, пожалуйста, разместите его самостоятельно, если хотите. Меня не очень беспокоит репутация или что-то в этом роде. :-) Ваше здоровье!   -  person Mike M.    schedule 06.07.2020
comment
Извините, что это заняло так много времени. Я действительно хотел найти какую-то документацию или исходный код, более подходящие для вашего конкретного примера, но я так и не нашел. Потом я как бы забыл об этом. Виноват. Ваше здоровье!   -  person Mike M.    schedule 12.07.2020


Ответы (7)


По всей видимости, это связано с новыми ограничениями видимости пакетов, введенными в Android 11.

По сути, начиная с уровня API 30, если вы ориентируетесь на эту версию или выше, ваше приложение не может видеть или напрямую взаимодействовать с большинством внешних пакетов без явного запроса разрешения, либо через общее разрешение QUERY_ALL_PACKAGES, либо путем включения соответствующего элемента <queries>. в вашем манифесте.

Действительно, ваш первый фрагмент работает должным образом с этим разрешением или с соответствующим элементом <queries> в манифесте; Например:

<queries>
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="https" />
    </intent>
</queries>

Доступная в настоящее время информация не очень конкретна, но в ней говорится:

PackageManager методы, которые возвращают результаты о других приложениях, например _6 _, фильтруются на основе объявления <queries> вызывающего приложения

Хотя в вашем примере используется метод Intent, то есть resolveActivityInfo(), который фактически вызывает PackageManager методы запроса внутри. Исчерпывающий список всех методов и функций, затронутых этим изменением, может оказаться невозможным, но, вероятно, можно с уверенностью предположить, что если задействован PackageManager, вам может быть полезно проверить его поведение с новыми ограничениями.

person Mike M.    schedule 12.07.2020
comment
У меня сработало, кроме предупреждения: Element category is not allowed here. - person Westy92; 02.09.2020
comment
@ Westy92, гугл удалил это предупреждение. - person CoolMind; 14.12.2020
comment
Не могли бы вы объяснить, почему для этого рекомендуется категория ОБЗОР? Кажется, что без него все работает так же - person yuval; 17.12.2020
comment
Было бы неплохо, если бы было какое-то предупреждение / ошибка, сообщающее вам, что вам нужно добавить это в манифест. - person lasec0203; 03.03.2021
comment
возможно вам это понадобится: stackoverflow.com/questions/62969917/ - person AlexS; 01.06.2021

Благодаря Майку М. я добавил запросы для Browser, Camera и Gallery. Разместите их внутри AndroidManifest в любой его части (до или после тега <application>).

При просмотре MediaStore.ACTION_IMAGE_CAPTURE и Intent.ACTION_GET_CONTENT Я получил оба действия.

<queries>
    <!-- Browser -->
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <data android:scheme="http" />
    </intent>

    <!-- Camera -->
    <intent>
        <action android:name="android.media.action.IMAGE_CAPTURE" />
    </intent>

    <!-- Gallery -->
    <intent>
        <action android:name="android.intent.action.GET_CONTENT" />
    </intent>
</queries>

Дилан мне не требовался ответ, поэтому у меня все еще есть

<uses-feature
    android:name="android.hardware.camera"
    android:required="false"
    />
person CoolMind    schedule 05.10.2020

Что касается меня, я пытался отправить электронное письмо, поэтому мне нужно было задать запросы в манифесте следующим образом:

<queries>
    <intent>
        <action android:name="android.intent.action.SENDTO" />
        <data android:scheme="*" />
    </intent>
</queries>

затем отправьте электронное письмо и проверьте наличие таких почтовых клиентов:

        private fun sendEmail(to: Array<String>) {
        val intent = Intent(Intent.ACTION_SENDTO)
        intent.data = Uri.parse("mailto:") // only email apps should handle this
        intent.putExtra(Intent.EXTRA_EMAIL, to)
//        intent.putExtra(Intent.EXTRA_SUBJECT, subject)
        if (intent.resolveActivity(requireContext().packageManager) != null) {
            startActivity(intent)
        }
    }
person fullmoon    schedule 06.12.2020
comment
Это не упоминается в официальной ссылке Android, которая учит, как отправлять электронные письма developer.android .com / guide / components / intents-common # Электронная почта. Я часто ненавижу эту платформу, мне хочется вырвать все волосы. - person Muhammad Ahmed AbuTalib; 23.06.2021
comment
Полностью согласен @ muhammad-ahmed-abutalib. Для меня <data android:scheme="mailto" /> сработало. - person Favolas; 19.07.2021

В случаях, когда необходимо запустить Activity (а не только проверить, существует ли), следуйте этому советую я удалил

if (intent.resolveActivity(packageManager) != null)
                startActivity(intent);

и написал instaed

try {
     startActivity(intent);
} catch (ActivityNotFoundException e) {
     Toast.makeText(getContext(), getString(R.string.error), Toast.LENGTH_SHORT).show();
}

Нет необходимости добавлять <queries> в Manifest. Протестировано как на Api 28, так и на Api 30.

person Avital    schedule 21.01.2021
comment
Это тот случай, когда вам нужно начать деятельность. Но может возникнуть ситуация, когда нужно знать, есть ли приложения, но не запускать их. - person CoolMind; 21.01.2021
comment
Спасибо! Я этого не заметил. Редактирую ответ - person Avital; 21.01.2021

То, что сказал Майк в дополнение к приведенному ниже коду, помогло мне.

<manifest ... >
    <uses-feature android:name="android.hardware.camera"
                  android:required="true" />
    ...
</manifest>
person Dylan    schedule 29.09.2020

Вы можете добавить разрешение QUERY_ALL_PACKAGES в AndroidManifest. Это не требует запроса разрешения во время выполнения.

<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
person manas.abrol    schedule 27.05.2021
comment
Да, но Google Play ограничивает использование разрешений с высокой степенью риска или конфиденциальных разрешений. , в том числе разрешение QUERY_ALL_PACKAGES, которое дает видимость инвентаря установленных приложений на данном устройстве. - person CoolMind; 27.05.2021

Чтобы повторить ответ Авиталь, вам не нужно ничего объявлять, если вы хотите запустить намерение и узнать, было ли оно запущено:

private fun startIntent(intent: Intent): Boolean {
    return try {
        context.startActivity(intent)
        true
    } catch (e: ActivityNotFoundException) {
        Logger.error("Can't handle intent $intent")
        false
    }
}
person Stan    schedule 17.06.2021