Pull to refresh

Android. Автозапуск приложения при загрузке: теория и практика

Reading time 7 min
Views 95K
Простой поиск на stackoverflow.com показывает, что тема получения сообщения ACTION_BOOT_COMPLETED остается актуальной и по сей день. Как видно, многие новички сталкиваются с проблемой: они не получают в своих приложениях сообщение ACTION_BOOT_COMPLETED. В этой статья я попробую резюмировать данные из официальной документации, опыт многих разработчиков из stackoverflow.com, а также свой опыт. Итак, как же победить этого «коварного врага» под названием «ACTION_BOOT_COMPLETED»?

1. Теория


Взглянув на примеры из официального источника (например, этот и этот) и изучив рекомендации на сайте stackoverflow.com, можно выделить следующие правила:

  1. В манифесте в элементе «manifest» указать разрешение:

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    

  2. В манифесте в элементе «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» и т.д. Вполне достаточно настроек и атрибутов по умолчанию.

  3. Код вашего 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.

  4. Приложение должно быть установлено на внутреннюю память. ОС Android устроена таким образом, что сообщение ACTION_BOOT_COMPLETED отправляется приложениям перед монтированием внешний памяти. Поэтому приложения, установленные на внешней памяти, никогда не получат это сообщение. Чтобы указать системе не устанавливать приложение на внешнюю память, в манифесте НЕ нужно прописывать для атрибута "@android:installLocation" значения «auto» или «preferExternal». По умолчанию, т.е. если этот атрибут не указан, ОС установит ваше приложение только на внутреннюю память. Однако согласно официальной документации лучше явно указать значение «internalOnly», чтобы у вас и других разработчиков не возникло искушение в будущем указать иное значение.

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        android:installLocation="internalOnly"
        ... >
    

  5. После установки или принудительной остановки (force stop) приложение должно быть запущено хотя бы один раз, чтобы система «запомнила» это приложение для отправки ему сообщения ACTION_BOOT_COMPLETED. Такое поведение было реализовано в версии Android 3.1 в целях безопасности. В чем суть? Все только что установленные приложения находятся в состоянии «stopped» (не путать с активити, т.к. ОС управляет этим состоянием у приложений и активити по-разному). В это же состояние приложение «уходит», когда пользователь в настройках телефона принудительно его останавливает. Пока приложение находится в таком состоянии, оно не будет запущено системой ни по какой причине (например, через ACTION_BOOT_COMPLETED), исключая, конечно же, запуск самим пользователем. Благодаря такому нововведению немалая часть«вирусни и троянцев» перестала работать, т.к. уже нет возможности запуститься автоматом после установки.

    image

    Исключение составляют системные приложения: см. замечание пользователя kolipass.

  6. Особенности режима 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. Практика: ошибки и особенности эксплуатации


Разберем ошибки, которые совершают новички при настройке приложения и в коде.

  1. После установки или force stop приложение ни разу не запускалось (см. п.1.5).

  2. Приложение установлено не на внутренней памяти, или пользователь вручную перенес его на внешнюю память (см. п. 1.4).

  3. У некоторых разработчиков прием начинал работать, когда они указывали относительное имя класса ресивера.

  4. Также некоторые разработчики, отлаживая приложение, в logcat не видели своих сообщений из ресивера. Используйте Toast для отладки:

    Toast toast = Toast.makeText(context.getApplicationContext(),
            context.getResources().getString(R.string.your_message), Toast.LENGTH_LONG);
    toast.show()
    

  5. Опечатки или несуществующие сообщения внутри тега ресивера:

    <action android:name="android.intent.action._BOOT_COMPLETED"/>
    <action android:name="android.intent.action.ACTION_BOOT_COMPLETED"/>
    

  6. Неправильное положение элементов в манифесте приложения:

    • «uses-permission» должен быть указан только как прямой потомок элемента «manifest», не нужно его указывать/дублировать в теге «receiver»;
    • тег «receiver» должен быть указан только как прямой потомок элемента «application».

  7. Различные диспетчеры задач, оптимизаторы, приложения безопасности, Startup-менеджеры и т.п. могут отслеживать регистрацию приложения для приема ACTION_BOOT_COMPLETED и запрещать/разрешать его получение при загрузке. Удалите эти приложения или добавьте в исключение вашу программу в их настройках.

    image

  8. Как было указано выше, некоторые устройства используют режим Fast boot. Можно попробовать в настройках телефона отключить этот режим или учесть п. 1.6.

  9. В приложении нет ни одной активити, поэтому после установки у пользователя нет возможности хотя бы 1 раз запустить ваше приложение. Из-за этого сообщение ACTION_BOOT_COMPLETED не будет отправлено в ваше приложение.

  10. Не ошибки, но все же: указаны лишние, не обязательные атрибуты в теге «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. Отладка ресивера в эмуляторе и на реальных устройствах.


  1. В терминале выполните:

    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
    

  2. В эмуляторе: установите ваше ПО, запустив его из студии. При этом студия соберет ваш проект, установит приложение и запустит его. После этого закройте эмулятор (это аналогично выключению на реальном устройстве). Чтобы получить сообщение ACTION_BOOT_COMPLETED, запустите эмулятор из AVD-менеджера, а не с помощью кнопки «Run app» в тулбаре студии.

    Картинка: как запустить эмулятор через AVD
    image

    После запуска эмулятора во вкладке Android Monitor укажите запущенный эмулятор и ваше приложение, чтобы просмотреть логи logcat.

    Картинка: как посмотреть логи logcat
    image


Итоги


Чтобы ваше приложение запускалось при загрузке на всех устройствах, манифест как минимум должен выглядеть так:

<?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».
Tags:
Hubs:
+24
Comments 7
Comments Comments 7

Articles