当应用程序关闭时,Android BOOT_COMPLETED未收到

36

我知道这个问题在网站上已经被问了很多次,然而,我似乎找不到解决方案。当应用程序没有运行时,我的BOOT_COMPLETED接收器没有被调用。

清单:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.startuptest"
    android:versionCode="1"
    android:versionName="1.0"
    android:installLocation="internalOnly">
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.startuptest.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="com.example.startuptest.StartUpBootReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

启动引导接收器:

public class StartUpBootReceiver  extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

        Log.d("startuptest", "StartUpBootReceiver " + intent.getAction());

        if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {
            Log.d("startuptest", "StartUpBootReceiver BOOT_COMPLETED");
        }
    }
}
如果应用程序正在运行,我模拟了一个电话。
adb shell
am broadcast -a android.intent.action.BOOT_COMPLETED

事件接收正确,但如果应用程序被关闭,则不会接收到事件,并且在启动时也不会接收到。

我已安装该应用程序,然后启动了几次以确保其已注册。对于这个问题,我感到非常困惑,因此非常感谢任何建议。

编辑:我可以在日志中看到所有其他已关闭的应用程序(Youtube、FileObserver等)都接收到了启动完成事件,只有我的应用程序没有接收到。

9个回答

110
Android 3.1开始,所有应用在安装后都处于"stopped"状态(这与用户从设置应用程序中强制停止应用后的状态相同)。
在"stopped"状态下,应用不会以任何原因运行,除非手动启动某个活动。这意味着,不管注册了哪个事件的BroadcastReceviers(ACTION_PACKAGE_INSTALLED, BOOT_COMPLETED等),直到用户手动运行该应用程序,它们都不会被调用。
这是谷歌的反恶意软件措施。谷歌主张用户应该先从启动器中启动一个活动,然后应用才能做更多事情。阻止BOOT_COMPLETED在启动活动之前传递是该论点的一个合理结果。
更多详情请查看:
http://developer.android.com/about/versions/android-3.1.html#launchcontrols
http://commonsware.com/blog/2011/07/05/boot-completed-regression.html
http://devmaze.wordpress.com/2011/12/05/activating-applications/

2
如果您的应用程序仅包含“BroadcastReceiver”和“Service”,则必须阅读此答案。 - Akhil Jain
1
这个链接也涵盖了相关主题。 - stdout
1
但是,首先注册启动完成广播有什么意义呢?在我看来,如果我不能在设备启动后并在用户启动应用程序之前采取任何行动/做任何事情的时间内进行任何操作,那么注册启动完成接收器有什么作用呢? - Bluesir9
3
@Bluesir9 我和你一样感到困惑,但是经过几个小时的阅读,我得出以下结论。 当应用程序被首次安装但尚未启动或者在用户手动停止(在管理应用程序中)时,应用程序处于停止状态。这意味着用户应该在安装后至少启动一次应用程序以激活它,然后应用程序就可以像正常情况下一样接收所有操作系统的隐式广播。 - Phong Nguyen
1
请查看我的答案以获取更多详细信息:https://dev59.com/5GIj5IYBdhLWcg3wRTL3#46294732 - Phong Nguyen
1
可能不会影响到你们中的许多人,但它可能会引起其他人的兴趣:在我的情况下,编译我的应用程序作为Android ROM中的系统应用程序是一个选项。似乎系统应用程序默认情况下不处于“停止状态”,所以我能够将一个没有活动的应用程序烘焙到Android构建中,并且因为它可以接收BOOT_COMPLETED意图,因此其服务会自动启动。使用AOSP API版本29进行了测试。 - Nico Feulner

41

BOOT_COMPLETED启动时,我启动我的应用程序,所以我知道它正在工作。我添加了Log.d,但不会显示。我添加了Toast,它会显示。Manifest.xml中存在细微差异。

<receiver android:name="com.example.startuptest.StartUpBootReceiver">
    <intent-filter>
         <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>            
</receiver>

1
我忘记添加默认标签了。 - user346443
4
谢谢,这个解决了问题。android:enabled="true" android:exported="true"也是必需的,这样安卓系统才能识别这个接收器。 - Ketan
6
这些不是默认设置吗?http://developer.android.com/guide/topics/manifest/receiver-element.html - MZB
1
@Ketan 这两个默认都是真的 - 如果某些东西开始工作了,很可能你只是在魅族Flyme上打开了“后台运行”,或者在华为上将应用程序添加到了受保护列表中,或者在当代手机上可能会发生一些令人惊讶的事情。除了priority=999之外。 - Boris Treukhov
1
<category android:name="android.intent.category.DEFAULT"/>android:enabled="true" android:exported="true" 不需要。 - user924
显示剩余4条评论

29

这里每个答案都提供了一小段信息,以下是所有信息的摘要:

为了确保您能接收到BOOT_COMPLETED,请执行以下操作:

  1. 将您的接收器添加到清单文件中(不要忘记flags):

<receiver android:name="com.yourpacakge.BootReceiver" android:exported="true" android:enabled="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</receiver>
  • 添加权限:

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

  • 在安装你的应用后,需要由用户手动至少启动一次,才能接收到开机完成事件。(更多详情


  • 1
    我发现(至少在Marshmallow上),如果我强制关闭我的应用程序,那么BroadcastReceiver onReceive永远不会触发。这是真的吗?如果是这样,有没有什么解决方法? - swooby
    3
    但是为什么需要默认值呢? - JacksOnF1re
    1
    这些内容与引导接收器无关,因此您可以将其删除:<category android:name="android.intent.category.DEFAULT"/>android:enabled="true" android:exported="true"。它们不是必需的。 - user924

    2
    这里是一个C#版本,如果你们需要的话。我的测试表明它基本上没有问题,并且启动非常快。 但请注意,将其添加到C#和AndroidManifest.xml中都会导致破坏(至少对我来说是这样)。
    我还添加了一些不错和有用的示例,我希望我在阅读文档时能从某人那里找到它们,而不是自己学习。
    [BroadcastReceiver(Enabled = true, Exported = true, DirectBootAware = true, Name = "com.nevaran.startup.StartUpBootReceiver")]
    [IntentFilter(new string[] {
        Intent.ActionBootCompleted
        , Intent.ActionLockedBootCompleted
        , Intent.ActionMyPackageReplaced
        , Intent.ActionUserInitialize
        , "android.intent.action.QUICKBOOT_POWERON"
        , "com.htc.intent.action.QUICKBOOT_POWERON"
    })]
    public class BootReceiver : BroadcastReceiver
    {
        public override void OnReceive(Context context, Intent intent)
        {
             if(    intent.Action.Equals(Intent.ActionBootCompleted)
                 || intent.Action.Equals(Intent.ActionLockedBootCompleted)
                 || intent.Action.Equals(Intent.ActionUserInitialize)
                 || intent.Action.Equals("android.intent.action.QUICKBOOT_POWERON")
                 || intent.Action.Equals("com.htc.intent.action.QUICKBOOT_POWERON")
               )
            {
                //run code here only if its started by the chosen actions
            }
            //some code that doesnt care about which action is triggered by
        }
    }
    

    2

    您遇到了相同的问题,原因是您在Logcat中使用Log.d来跟踪应用程序,不幸的是,当您重新启动手机时,应用程序会接收到BOOT_Complete,但由于它没有记录到logcat中,您无法看到它。

    尝试使用一些文本创建一个Toast,而不是Log.d,以确保是否接收到了BOOT_COMPLETED。

    希望这可以帮助您。


    Toast调用也没有显示。 - user346443

    0

    对我来说,添加以下内容似乎与BOOT_COMPLETED意图动作一起工作

            <action android:name="android.intent.action.QUICKBOOT_POWERON" />
    

    0
    为了解决这个问题,您可以使用firebaseJobDispatcher自动调用,firebaseJobDispatcher将具有重新激活服务的代码,是的,在一定时间后,服务可能会被操作系统停止,但是您的firebaseJobDispatcher将再次激活您的服务。FirebaseJobDispatcher有许多属性,您可以从中定义其范围;
    关于它的工作原理,更多细节请参见https://github.com/firebase/firebase-jobdispatcher-android

    firebaseJobDispatcher需要通过编程创建。它不能自动启动,因此我们需要监听BOOT_COMPLETED事件。 - Edijae Crusar

    -2

    如果您想知道BOOT_COMPLETE为什么无法正常工作或未收到的实际原因,我建议您前往官方Android开发网站。他们已经用准确的解决方案进行了解释。

    Android开发者 - BOOT_COMPLETE


    -3

    基本上,您需要在清单文件中添加android:enabled="true" android:exported="true"标志才能接收广播。

    <receiver android:name=".bootReceiver" android:enabled="true" android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </receiver>
    

    这些内容与引导接收器无关,因此您可以将其删除:<category android:name="android.intent.category.DEFAULT"/>android:enabled="true" android:exported="true"。它们不是必需的。 - user924
    @user924,你能解释一下为什么不需要使用category.DEFAULT吗? - Johnny Five
    1
    @JohnnyFive因为它可以在没有的情况下工作,并且您可以在android.com网站上查看一些示例,所以不需要。 - user924

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接