Освой программирование играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

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

Тема получения сообщения ACTION_BOOT_COMPLETED остается актуальной и по сей день. Многие новички сталкиваются с проблемой: они не получают в своих приложениях данное сообщение.

Можно выделить следующие правила:

В манифесте указать разрешение:


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

В манифесте в блоке application зарегистрировать приёмник на приём сообщения ACTION_BOOT_COMPLETED:


<receiver android:name=".MyBroadcastReceiver">  
    <intent-filter>  
        <action android:name="android.intent.action.BOOT_COMPLETED" />  
    </intent-filter>  
</receiver>

В описании без необходимости не указывайте атрибуты «enabled», «exported» и т.д. Вполне достаточно настроек и атрибутов по умолчанию.

Код вашего широковещательного приёмника:


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». По умолчанию, т.е. если этот атрибут не указан, Android установит ваше приложение только на внутреннюю память. Однако согласно официальной документации лучше явно указать значение «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), исключая, конечно же, запуск самим пользователем. Благодаря такому нововведению немалая часть вирусов и троянцев» перестала работать, т.к. уже нет возможности запуститься автоматом после установки.

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

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


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

Практика: ошибки и особенности эксплуатации

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

  • После установки или force stop приложение ни разу не запускалось.
  • Приложение установлено не на внутренней памяти, или пользователь вручную перенес его на внешнюю память.
  • У некоторых разработчиков приём начинал работать, когда они указывали относительное имя класса приёмника.
  • Также некоторые разработчики в Logcat не видели своих сообщений из ресивера. Используйте Toast для отладки:
    
    Toast toast = Toast.makeText(context.getApplicationContext(),
            context.getResources().getString(R.string.your_message), Toast.LENGTH_LONG);
    toast.show();
    
  • Опечатки или несуществующие сообщения внутри тега ресивера:
  • Неправильное положение элементов в манифесте приложения:
    «uses-permission» должен быть указан только как прямой потомок элемента «manifest», не нужно его указывать/дублировать в теге «receiver»;
    тег «receiver» должен быть указан только как прямой потомок элемента «application».
  • Различные диспетчеры задач, оптимизаторы, приложения безопасности, Startup-менеджеры и т.п. могут отслеживать регистрацию приложения для приема ACTION_BOOT_COMPLETED и запрещать/разрешать его получение при загрузке. Удалите эти приложения или добавьте в исключение вашу программу в их настройках.
  • Как было указано выше, некоторые устройства используют режим Fast boot. Можно попробовать в настройках телефона отключить этот режим.
  • В приложении нет ни одной активности, поэтому после установки у пользователя нет возможности хотя бы один раз запустить ваше приложение. Из-за этого сообщение 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>
    

    Отладка ресивера в эмуляторе и на реальных устройствах

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

    
    adb shell
    

    Далее, чтобы отправить ACTION_BOOT_COMPLETED всем приложениям, наберите в терминале:

    
    am broadcast -a android.intent.action.BOOT_COMPLETED
    

    Или для отправки ACTION_BOOT_COMPLETED конкретному приложению наберите в терминале:

    
    am broadcast -a android.intent.action.BOOT_COMPLETED your.package.name
    

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

    После запуска эмулятора во вкладке Android Monitor укажите запущенный эмулятор и ваше приложение, чтобы просмотреть логи 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);
          // ваш код здесь
        }
      }
    }
    

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

    Реклама