Простой поиск на stackoverflow.com показывает, что тема получения сообщения ACTION_BOOT_COMPLETED остается актуальной и по сей день. Как видно, многие новички сталкиваются с проблемой: они не получают в своих приложениях сообщение ACTION_BOOT_COMPLETED. В этой статья я попробую резюмировать данные из официальной документации, опыт многих разработчиков из stackoverflow.com, а также свой опыт. Итак, как же победить этого «коварного врага» под названием «ACTION_BOOT_COMPLETED»?
Взглянув на примеры из официального источника (например, этот и этот) и изучив рекомендации на сайте stackoverflow.com, можно выделить следующие правила:
Разберем ошибки, которые совершают новички при настройке приложения и в коде.
Чтобы ваше приложение запускалось при загрузке на всех устройствах, манифест как минимум должен выглядеть так:
Код ресивера, как правило, будет таким:
Надеюсь, эта статья поможет новичкам побороть «коварного врага» под названием «ACTION_BOOT_COMPLETED».
1. Теория
Взглянув на примеры из официального источника (например, этот и этот) и изучив рекомендации на сайте stackoverflow.com, можно выделить следующие правила:
- В манифесте в элементе «manifest» указать разрешение:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
- В манифесте в элементе «application» зарегистрировать ваш ресивер на прием сообщения ACTION_BOOT_COMPLETED:
<receiver android:name="com.example.app.MyBroadcastReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver>
или
<receiver android:name=".MyBroadcastReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver>
Используйте правильное полное или относительное имя класса вашего broadcast-ресивера. В описании ресивера без необходимости не указывайте атрибуты «enabled», «exported» и т.д. Вполне достаточно настроек и атрибутов по умолчанию.
- Код вашего broadcast-ресивера:
public class BootCompletedReceiver extends BroadcastReceiver { public BootCompletedReceiver() { } public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) { // ваш код здесь } } }
Если ваш ресивер используется только для сообщения ACTION_BOOT_COMPLETED, то проверка «if» не обязательна. Однако иногда разработчики используют один и тот же ресивер для разных сообщений. В этом случае фильтруйте сообщения, проверяя их внутри метода onReceive.
- Приложение должно быть установлено на внутреннюю память. ОС Android устроена таким образом, что сообщение ACTION_BOOT_COMPLETED отправляется приложениям перед монтированием внешний памяти. Поэтому приложения, установленные на внешней памяти, никогда не получат это сообщение. Чтобы указать системе не устанавливать приложение на внешнюю память, в манифесте НЕ нужно прописывать для атрибута "@android:installLocation" значения «auto» или «preferExternal». По умолчанию, т.е. если этот атрибут не указан, ОС установит ваше приложение только на внутреннюю память. Однако согласно официальной документации лучше явно указать значение «internalOnly», чтобы у вас и других разработчиков не возникло искушение в будущем указать иное значение.
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="internalOnly" ... >
- После установки или принудительной остановки (force stop) приложение должно быть запущено хотя бы один раз, чтобы система «запомнила» это приложение для отправки ему сообщения ACTION_BOOT_COMPLETED. Такое поведение было реализовано в версии Android 3.1 в целях безопасности. В чем суть? Все только что установленные приложения находятся в состоянии «stopped» (не путать с активити, т.к. ОС управляет этим состоянием у приложений и активити по-разному). В это же состояние приложение «уходит», когда пользователь в настройках телефона принудительно его останавливает. Пока приложение находится в таком состоянии, оно не будет запущено системой ни по какой причине (например, через ACTION_BOOT_COMPLETED), исключая, конечно же, запуск самим пользователем. Благодаря такому нововведению немалая часть«вирусни и троянцев» перестала работать, т.к. уже нет возможности запуститься автоматом после установки.
Исключение составляют системные приложения: см. замечание пользователя kolipass.
- Особенности режима Fast boot в HTC-устройствах. Известно, что HTC-устройства не перезагружаются в классическом смысле, а используют так наз. режим Fast boot (это одна из форм гибернации), сохраняя состояние ОС на диск. Поэтому сообщение ACTION_BOOT_COMPLETED не отправляется системой, т.к. в действительности перезагрузка не происходит (см. здесь). Вместо ACTION_BOOT_COMPLETED система может отправить следующие сообщения:
<action android:name="android.intent.action.QUICKBOOT_POWERON" /> <action android:name="com.htc.intent.action.QUICKBOOT_POWERON" />
В вашем приложении укажите в теге «receiver» кроме ACTION_BOOT_COMPLETED также вышеуказанные сообщения. Кроме этого необходимо прописать разрешение в дополнение к п.1:
<uses-permission android:name="android.permission.QUICKBOOT_POWERON" />
2. Практика: ошибки и особенности эксплуатации
Разберем ошибки, которые совершают новички при настройке приложения и в коде.
- После установки или force stop приложение ни разу не запускалось (см. п.1.5).
- Приложение установлено не на внутренней памяти, или пользователь вручную перенес его на внешнюю память (см. п. 1.4).
- У некоторых разработчиков прием начинал работать, когда они указывали относительное имя класса ресивера.
- Также некоторые разработчики, отлаживая приложение, в logcat не видели своих сообщений из ресивера. Используйте Toast для отладки:
Toast toast = Toast.makeText(context.getApplicationContext(), context.getResources().getString(R.string.your_message), Toast.LENGTH_LONG); toast.show()
- Опечатки или несуществующие сообщения внутри тега ресивера:
<action android:name="android.intent.action._BOOT_COMPLETED"/> <action android:name="android.intent.action.ACTION_BOOT_COMPLETED"/>
- Неправильное положение элементов в манифесте приложения:
- «uses-permission» должен быть указан только как прямой потомок элемента «manifest», не нужно его указывать/дублировать в теге «receiver»;
- тег «receiver» должен быть указан только как прямой потомок элемента «application».
- Различные диспетчеры задач, оптимизаторы, приложения безопасности, Startup-менеджеры и т.п. могут отслеживать регистрацию приложения для приема ACTION_BOOT_COMPLETED и запрещать/разрешать его получение при загрузке. Удалите эти приложения или добавьте в исключение вашу программу в их настройках.
- Как было указано выше, некоторые устройства используют режим Fast boot. Можно попробовать в настройках телефона отключить этот режим или учесть п. 1.6.
- В приложении нет ни одной активити, поэтому после установки у пользователя нет возможности хотя бы 1 раз запустить ваше приложение. Из-за этого сообщение ACTION_BOOT_COMPLETED не будет отправлено в ваше приложение.
- Не ошибки, но все же: указаны лишние, не обязательные атрибуты в теге «receiver», например («uses-permission», «enabled», «exported»):
<receiver android:name=".BootCompletedReceiver" android:enabled="true" android:exported="true" > <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver>
3. Отладка ресивера в эмуляторе и на реальных устройствах.
- В терминале выполните:
adb shell
Далее, чтобы отправить ACTION_BOOT_COMPLETED всем приложениям, наберите в терминале:
am broadcast -a android.intent.action.BOOT_COMPLETED
Или для отправки ACTION_BOOT_COMPLETED конкретному приложению наберите в терминале:
am broadcast -a android.intent.action.BOOT_COMPLETED my.package.name
- В эмуляторе: установите ваше ПО, запустив его из студии. При этом студия соберет ваш проект, установит приложение и запустит его. После этого закройте эмулятор (это аналогично выключению на реальном устройстве). Чтобы получить сообщение ACTION_BOOT_COMPLETED, запустите эмулятор из AVD-менеджера, а не с помощью кнопки «Run app» в тулбаре студии.
Картинка: как запустить эмулятор через AVD
После запуска эмулятора во вкладке Android Monitor укажите запущенный эмулятор и ваше приложение, чтобы просмотреть логи logcat.
Картинка: как посмотреть логи logcat
Итоги
Чтобы ваше приложение запускалось при загрузке на всех устройствах, манифест как минимум должен выглядеть так:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:installLocation="internalOnly"
package="com.site.yourapp" >
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.QUICKBOOT_POWERON" />
<application>
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name=".BootCompletedReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
</application>
</manifest>
Код ресивера, как правило, будет таким:
public class BootCompletedReceiver extends BroadcastReceiver {
public BootCompletedReceiver() {
}
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
Toast toast = Toast.makeText(context.getApplicationContext(),
context.getResources().getString(R.string.your_message), Toast.LENGTH_LONG);
toast.show();
Log.d("myapp", context.getResources().getString(R.string.your_message);
// ваш код здесь
}
}
}
Надеюсь, эта статья поможет новичкам побороть «коварного врага» под названием «ACTION_BOOT_COMPLETED».
Комментарии (7)
kolipass
26.11.2015 06:07От себя добавлю, что сделать приложение без Activity, и, соответственно, без первого запуска(пункт 1.5) можно. Ваше приложение сможет «услышать» событие если приложение установлено в /system/app/.
SannX
26.11.2015 07:24Вы правы. Ибо системные приложения не переводятся ОС в состсояние «stopped» (link и link, и еще эта link). Однако такие приложения не установить на любой девайс: как я понял, нужен root или custom recovery.
lampa
26.11.2015 12:37Также можно скомпилировать приложение как системное и установить его через adb.
stoplinux
03.12.2015 07:52При отсутствии root прав на устройстве? Расскажите как?
lampa
03.12.2015 11:31При отсутствии root прав. Я плясал от этого: stackoverflow.com/questions/5383401/android-inject-events-permission/21555223
точно уже не помню. На популярных устройствах не проверял, но на китайских работало.
TimsTims
Спасибо :) теперь наконец-то будет, с чего начинать писать свое первое «самое крутое приложение на андроиде», которое должно запускаться автоматически)
maseal
Некоторые производители телефонов сразу обламывают крылья таким писателям самых крутых приложений, предустанавливая в заводскую прошивку менеджер автозапуска, который по умолчанию запрещает автозапуск для любых приложений кроме google-овских и их собственных.
Ручками там конечно можно разрешить автозапуск для любого приложения, но не все пользователи об этом даже догадываются.