Выполнить код один раз после каждой загрузки на Android

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

  • Сохранение SharedPreference, которое указывает, что запуск кода не подходит, поскольку он выживает при перезагрузке.
  • Опознавание ACTION_SHUTDOWN, чтобы очистить значение SharedPreference недостаточно, потому что бывают случаи, когда это не будет отправлено (например, батарея удалена).
  • Использование статического поля для указания запуска кода не подходит, поскольку оно будет reset, если мое приложение будет убито.
  • Использование некоторого кода инициализации в моем классе приложения не подходит, потому что это будет работать снова, если мое приложение будет убито.
  • Прием ACTION_BOOT_COMPLETED почти достаточно хорош, но этому могут предшествовать другие широковещательные передачи, на которые реагирует мое приложение (например, ACTION_TIME_CHANGED), и его можно запустить после того, как я уже запустил приложение из панели запуска. Мне нужен этот одноразовый установочный код для запуска до этого.
  • Я не могу полагаться на System.currentTimeMillis, чтобы вычислить время загрузки, потому что изменения часов изменили явное время загрузки.

Один из вариантов заключается в том, чтобы получить последнее время загрузки устройства и посмотреть, изменилось ли это (System.elapsedTime() недостаточно). Я пробовал выполнять команды типа who -b и last reboot, но разрешение было отклонено для обоих.

Другим вариантом является сохранение настроек/предпочтений где-нибудь, когда он будет получать reset ТОЛЬКО, если устройство перезагружается, но не если мое приложение будет убито.

Есть ли другой вариант или способ реализовать одно из указанных выше?

Ответ 1

Я нашел решение, которое кажется надежным и отвечает моим требованиям. Я прочитал содержимое файла /proc/sys/kernel/random/boot_id, которое дает уникальный идентификатор загрузки reset каждый раз, когда устройство перезагружается.

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

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

К сожалению, ни один из других ответов не предоставил надежный способ сделать это, но наконечник приоритета фильтра Хардик Чаухан был полезен, потому что я получил ACTION_BOOT_COMPLETED намного раньше с этим набором.

Ответ 2

Итак, вы хотите запустить код при запуске системы до того, как начнется выполнение любого другого приложения?

Если да, то здесь идея, Создайте приемник для ACTION_BOOT_COMPLETED и придайте приоритет 999, который является самым высоким, поэтому он будет срабатывать всегда сначала при запуске устройства.

Ответ 3

  • Слушайте ACTION_BOOT_COMPLETED, как вы делаете, вместе с другими трансляциями.

  • Я верю, что Android создаст экземпляр и вызовет onCreate вашего класса Application, если вы указали его в своем AndroidManifest.xml, прежде чем называть любой своих приемников, поэтому приклеивая код там, вы будете охватывать все случаи.

  • Или, без класса Application, придерживайте код ниже в каждом из ваших индивидуальных BroadcastReceivers.

Используйте это, чтобы определить, является ли это уникальной загрузкой:

public static boolean hasRunSinceBoot(Context context) {
    long bootTime = System.currentTimeMillis() - SystemClock.elapsedRealtime();
    SharedPreferences prefs = context.getSharedPreferences("my_prefs_file", Context.MODE_PRIVATE);
    if (prefs.getLong("last_boot_time", 0) == bootTime) {
        return true;
    }
    prefs.edit().putLong("last_boot_time", bootTime).apply();
    return false;
}

затем выполните что-то вроде:

if (!hasRunSinceBoot(context)) {
    //do whatever you need to do
}

Ответ 4

Поймать ACTION_USER_INITIALIZE, это выполнит задание.

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

Примером может быть то, что вы храните подтверждение начала вашего процесса в SharedPreference и обязательно удаляете его, когда система закрывается.

Это может быть не самый элегантный способ, но вы будете читать назначения.

Проверьте описание! http://developer.android.com/reference/android/content/Intent.html#ACTION_USER_INITIALIZE

[...] (Это не будет видно сторонним приложениям, поскольку новый инициализированный пользователь не имеет сторонних приложений установленный для него.) Это отправляется на ранней стадии запуска пользователя, вокруг время запуска домашнего приложения, прежде чем ACTION_BOOT_COMPLETED будет отправлено. [...]

К сожалению, это доступно только для системных приложений; на защищенных системах. Вы можете найти рабочий стол.

Но это то, что вам нужно в хронологическом потоке.

Если это действительно не работает.

Сделайте проверку качества на ACTION_BOOT_COMPLETED, что вы действительно поймаете его всего за один раз.

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

То же самое, при загрузке, вы также должны reset флаг, который мы установили раньше.

Если действительно все упомянутое здесь не работает.

Извлечь /proc/uptime так что вы можете справиться с этим именно так. Не требуется никаких разрешений.