【问题标题】:Run code once after each boot on Android在 Android 上每次启动后运行一次代码
【发布时间】:2015-11-16 16:37:04
【问题描述】:

我需要一些初始化代码在每次重新启动后尽快运行一次,然后在设备运行时不再运行。

  • 存储表示代码已运行的 SharedPreference 并不合适,因为它可以在重新启动后继续存在。
  • 依靠 ACTION_SHUTDOWN 清除 SharedPreference 值是不够的,因为有时它不会被发送(例如,电池被移除)。
  • 使用静态字段来指示代码已运行是不合适的,因为如果我的应用程序被终止,它将被重置。
  • 在我的应用程序类中使用一些初始化代码是不合适的,因为如果我的应用程序被终止,这将再次运行。
  • 接收ACTION_BOOT_COMPLETED 几乎就足够了,但这可以在我的应用响应的其他广播之前(例如ACTION_TIME_CHANGED),并且可以在我已经从启动器启动我的应用后触发。我需要在此之前运行这个一次性设置代码。
  • 我不能依靠System.currentTimeMillis 来计算启动时间,因为时钟变化会改变明显的启动时间。

一种选择是获取设备的最后一次启动时间并查看它是否已更改(System.elapsedTime() 不够好)。我曾尝试执行诸如who -blast reboot 之类的命令,但两者的权限都被拒绝。

另一种选择是将设置/首选项存储在某个位置,仅当设备重新启动时才会重置,但如果我的应用程序被终止则不会。

是否有其他选择,或实现上述之一的方法?

【问题讨论】:

  • 能否请您转述一下,您为什么不能只使用 ACTION_BOOT_COMPLETED?
  • 我认为ACTION_BOOT_COMPLETED 是您最好的选择。如果您的应用在此之前收到任何其他广播,例如ACTION_TIME_CHANGED,您可以在接收到ACTION_BOOT_COMPLETED 广播后进行任何初始化之后,将必要的任务排队运行。
  • 感谢 Mike,但不幸的是,第一时间运行代码很重要,这通常在发送 ACTION_BOOT_COMPLETED 之前。
  • @DaveMorrissey 您可以在您的应用收到的第一个广播上运行初始化,而不管操作如何,但您又遇到了确定上次启动的问题。但是,在启动过程的早期,唯一会启动您的应用的是系统广播。
  • 似乎不能保证。在收到启动完成事件之前,我最多可以在 20 秒内从启动器打开应用程序。我也会为 ACTION_USER_PRESENT 设置一个接收器 - 这会有所帮助。

标签: android


【解决方案1】:

所以您想在系统启动时运行您的代码,然后开始执行任何其他应用程序,对吗?

如果是的话,那就是这个想法, 为 ACTION_BOOT_COMPLETED 创建一个接收器,并将优先级设置为 999,这是最高的,因此它总是在设备启动时首先触发。

【讨论】:

  • 感谢您的建议,我正在尝试,但我不确定它是否会有所作为,但我会继续尝试。遗憾的是,这无法帮助我确定自上次运行代码以来是否有重新启动。
  • 这在较旧且较慢的设备上似乎确实有所作为,谢谢!与其等待 ACTION_BOOT_COMPLETED 长达一分钟,它的发送速度比我启动应用程序的速度还要快。
  • 如果两个接收器具有相同的最高优先级 999 怎么办?
  • 然后它将按照定义两者的顺序执行。表示第一个定义的接收者将首先监听事件,依此类推。
【解决方案2】:

我找到了一个看起来可靠且符合我要求的解决方案。我阅读了文件/proc/sys/kernel/random/boot_id 的内容,它会在每次设备重启时重置一个唯一的启动标识符。

通过读取此文件并将值与存储在 SharedPreferences 中的值进行比较,我可以可靠地确定自上次运行初始化代码以来设备是否已重新启动,而无需依赖设备时钟。这适用于所有用户个人资料,因为他们都有自己的 SharedPreferences。

这种方法的优点是我可以在收到任何广播时以及应用程序启动时检查更改,因此无论首先发生什么,即使 ACTION_BOOT_COMPLETED 广播延迟或错过(常见在慢速设备上)。

不幸的是,其他答案都没有提供可靠的方法来做到这一点,但 Hardik Chauhan 的意图过滤器优先级提示很有用,因为我收到 ACTION_BOOT_COMPLETED 的时间要早​​得多。

【讨论】:

    【解决方案3】:
    1. 一边听ACTION_BOOT_COMPLETED,一边收听其他广播。

    2. 如果您在 AndroidManifest.xml 中指定了一个,我相信 Android 会在调用 any 接收器之前实例化并调用您的 onCreate 类的 onCreate,所以通过将代码粘贴在那里,您将涵盖所有情况。

    3. 或者,如果没有 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
    }
    

    【讨论】:

    • 使用 Application 类将不起作用,因为如果进程被终止,将再次调用相同的代码。任何依赖 System.currentTimeMillis 的东西都可能被用户更改时钟或 NTP 在后台执行此操作所愚弄。
    • 代码可以多次调用,这就是它返回真或假的原因。 :)
    • 是的,但是更改设备上的时间会更改明显的启动时间,因此看起来像是重新启动此代码。
    • 没错,这很难解决。您可以为 NTP 更改构建一个 5 分钟的模糊因子,如果您真的想要安全,则依赖于基于服务器的时间。
    • 实际上,设置基于服务器的时间是这样做的原因。
    【解决方案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要被抓到,你只要看看是不是第一次。

    同样,开机时,你也应该重置我们之前设置的标志。

    如果这里提到的一切都不起作用。

    获取/proc/uptime 所以你可以这样处理。无需权限。

    【讨论】:

    • ACTION_USER_INITIALIZE 不可用。由于我上面描述的原因, ACTION_BOOT_COMPLETED 不合适,因为可以在发送之前打开应用程序,并且我需要在打开任何应用程序之前运行此代码。 /proc/uptime 类似于 System.elapsedTime - 您无法可靠地使用它来确定自上次运行代码以来手机是否已重新启动。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-04
    • 1970-01-01
    • 2018-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多