在Android上尝试在启动时启动服务

344

我一直在尝试在Android设备启动时启动一个服务,但我无法使其工作。我查看了许多在线链接,但其中没有任何代码起效。我是否遗漏了什么?

AndroidManifest.xml

<receiver
    android:name=".StartServiceAtBootReceiver"
    android:enabled="true"
    android:exported="false"
    android:label="StartServiceAtBootReceiver" >
    <intent-filter>
        <action android:name="android.intent.action._BOOT_COMPLETED" />
    </intent-filter>
</receiver>

<service
    android:name="com.test.RunService"
    android:enabled="true" />

BroadcastReceiver

public void onReceive(Context context, Intent intent) {
    if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {
        Intent serviceLauncher = new Intent(context, RunService.class);
        context.startService(serviceLauncher);
        Log.v("TEST", "Service loaded at start");
    }
}

2
我不太清楚自己做了什么,但我认为现在它可以工作了。可能是接收器的android:permission="android.permission.RECEIVE_BOOT_COMPLETED"的问题。 - Alex
你检查过 <action android:name="android.intent.action._BOOT_COMPLETED"/> 中的额外 "_" 了吗? - OneWorld
导出必须为true,这样系统才能调用接收器,对吧?还是默认就是true? - Eugen Pechanec
针对Oreo,查看此处:https://dev59.com/eVcP5IYBdhLWcg3wXI7Z - Andy Weinstein
16个回答

612

其他答案看起来不错,但我想把所有东西都包含在一个完整的答案中。

你需要在你的 AndroidManifest.xml 文件中添加以下内容:

  1. 在你的 <manifest> 元素中:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
  • 在你的<application>元素中(确保为你的BroadcastReceiver使用完全限定的[或相对的]类名):

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

    (你不需要android:enabledexported等属性:Android默认值是正确的)

    MyBroadcastReceiver.java 中:

    package com.example;
    
    public class MyBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Intent startServiceIntent = new Intent(context, MyService.class);
            context.startService(startServiceIntent);
        }
    }
    

    原问题如下:

    • 不清楚<receiver>元素是否在<application>元素中。
    • 不清楚是否指定了正确的全限定(或相对)类名以用于BroadcastReceiver
    • <intent-filter>中有一个拼写错误。

    2
    这看起来不错。我会以此为基础,谢谢 :). 没有勾选或点赞或回复,遗憾 :(. 有人可以验证一下吗? - Nanne
    51
    请注意:确保您的应用程序安装在内部存储器中。<manifest xmlns:android="..." package="..." android:installLocation="internalOnly"> - Bao Le
    2
    在Android Jellybean 4.2.2中,在<receiver>标签中,我必须使用类的相对名称而不是完全限定名称来启动服务,如http://stackoverflow.com/questions/16671619/android-intentservice-wont-start-on-boot中所述。 - Piovezan
    6
    如果接收器用于不同的事情: 如果(“android.intent.action.BOOT_COMPLETED”.equals(intent.getAction())){ Intent serviceIntent = new Intent(context,Service_Location.class); // i.putExtra(“KEY1”,“要由服务使用的值”); context.startService(serviceIntent); } - Gunnar Bernstein
    2
    你应该扩展https://developer.android.com/reference/android/support/v4/content/WakefulBroadcastReceiver.html。它是实现BroadcastReceiver的常见模式的帮助程序,该接收设备唤醒事件,然后将工作传递给Service,同时确保设备在过渡期间不会回到睡眠状态。这个类会为您创建和管理部分唤醒锁定;您必须请求WAKE_LOCK权限才能使用它。 - Damian
    显示剩余6条评论

    85
    作为额外信息:在外部存储被挂载之前,BOOT_COMPLETE消息将被发送给应用程序。因此,如果应用程序安装在外部存储中,则不会收到BOOT_COMPLETE广播消息。
    更多详细信息请参见“监听“启动完成”的广播接收器”部分这里

    为了避免上述问题,开发人员可以在应用程序清单中设置"android:installLocation="internalOnly""。这是一个不好的主意吗?对于智能手机应用程序来说,如果99.9%(我猜测)的所有用户通常通过使用内部存储而不是外部存储安装应用程序,那么似乎将"internalOnly"添加到清单中就可以了。我很感谢您对此的任何想法或建议。 - AJW

    70

    如何在设备启动时启动服务(自动运行应用程序等)

    首先,从Android 3.1+版本开始,如果用户从未至少启动过您的应用程序或用户"强制关闭"了应用程序,则您将不会收到BOOT_COMPLETE消息。这是为了防止恶意软件自动注册服务。在较新的Android版本中,这个安全漏洞已经被修复。

    解决方案:

    创建一个带有活动的应用程序。当用户第一次运行应用程序时,应用程序可以接收BOOT_COMPLETE广播消息。

    对于第二个问题:BOOT_COMPLETE在外部存储器挂载之前发送。如果应用程序安装到外部存储器,则它将无法接收BOOT_COMPLETE广播消息。

    在这种情况下,有两种解决方案:

    1. 将应用程序安装到内部存储器中
    2. 在内部存储器中安装另一个小应用程序。该应用程序接收BOOT_COMPLETE并在外部存储器上运行第二个应用程序。

    如果您的应用程序已经安装在内部存储器中,则下面的代码可以帮助您了解如何在设备启动时启动服务。


    在Manifest.xml中

    权限:

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

    注册你的BOOT_COMPLETED接收器:

    <receiver android:name="org.yourapp.OnBoot">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED"/>
        </intent-filter>
    </receiver>
    

    注册您的服务:

    <service android:name="org.yourapp.YourCoolService" />
    
    在接收器 OnBoot.java 中:
    public class OnBoot extends BroadcastReceiver
    {
    
        @Override
        public void onReceive(Context context, Intent intent) 
        {
            // Create Intent
            Intent serviceIntent = new Intent(context, YourCoolService.class);
            // Start service
            context.startService(serviceIntent);
    
        }
    
     }
    

    对于HTC手机,如果设备无法捕获到RECEIVE_BOOT_COMPLETED,请在清单文件中添加以下代码:

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

    接收器现在看起来像这样:

    <receiver android:name="org.yourapp.OnBoot">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED"/>
            <action android:name="android.intent.action.QUICKBOOT_POWERON" />
        </intent-filter>
    </receiver>
    
    如何在不重新启动模拟器或真实设备的情况下测试BOOT_COMPLETED?很简单,试试这个方法:
    adb -s device-or-emulator-id shell am broadcast -a android.intent.action.BOOT_COMPLETED
    

    如何获取设备ID?获取带有ID的已连接设备列表:

    adb devices
    

    默认情况下,你可以在ADT中找到adb:

    adt-installation-dir/sdk/platform-tools
    

    享受!)


    你的第一个段落是大写的。我无法在我的调试器中运行它。 - estornes

    34

    随着

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

    还可以使用,

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

    HTC设备似乎无法捕获 BOOT_COMPLETED 事件。


    需要为HTC设备添加类似的权限吗? - Nanda
    2
    这在某些情况下可能很有用,但我了解HTC快速启动是一种休眠形式,其中系统状态保存到文件系统中,并且只有在从快速启动恢复时才会发送android.intent.action.QUICKBOOT_POWERON。这意味着在从快速启动恢复时不需要执行诸如重置闹钟之类的操作,因为它们已被保留。因此,只有当您想要在用户认为设备已启动时执行某些操作时,才需要使用<action android:name="android.intent.action.QUICKBOOT_POWERON" /> - HexAndBugs
    2
    从应用程序开发人员的角度来看,如果某种行为仅存在于HTC设备上,则我们不应该使用它。因为根据文档,BOOT_COMPLETED将始终在设备开机时发送。其他制造商可能会提出另一种快速启动方法,我们最终会将代码与每个规格混淆。 - C--
    @HexAndBugs 你能确认快速启动是一种休眠形式,系统状态被保存到文件系统中吗?如果系统状态没有被保存,我想在快速启动后重置用于未来通知的闹钟...请给予建议。 - AJW

    20

    请注意,在问题的开头,有一个拼写错误:

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

    应该改为:

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

    只因为一个小小的下划线引起了这么多麻烦 :)


    13

    我认为您的清单文件需要添加:

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

    我遇到了同样的问题。你介意帮我吗?谢谢!http://stackoverflow.com/questions/35373525/starting-my-service - Ruchir Baronia

    13

    我刚刚发现可能是因为在设置 > 电源选项中的快速启动选项导致的。

    当我关闭这个选项时,我的应用程序能够接收到此广播,但如果开启则不能。

    顺便说一下,我的HTC Incredible S上运行着Android 2.3.3

    希望这可以帮助到你。


    肯定是问题的可能原因。在运行Android 4.0.3的HTC Desire C上也观察到了这个问题。 - Zelimir

    7

    经过尝试所有提到的答案和技巧后,我最终发现了为什么代码在我的手机上无法正常工作。一些安卓手机,如“华为荣耀3C 安卓4.2.2”,在其设置中具有启动管理器菜单,你的应用必须在列表中被勾选。


    5

    以下是我的工作内容

    1. 我创建了接收器类(Receiver class)

    public class BootReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            //whatever you want to do on boot
           Intent serviceIntent = new Intent(context, YourService.class);
           context.startService(serviceIntent);
        }
    }
    

    2.在清单文件中

    <manifest...>
        <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
        <application...>
            <receiver android:name=".BootReceiver" android:enabled="true" android:exported="false">
                <intent-filter>
                    <action android:name="android.intent.action.BOOT_COMPLETED" />
                </intent-filter>
            </receiver>
        ...
    

    3. 在所有操作完成后,你需要在MainActivity中“设置”接收器,它可能位于onCreate方法内部。

    ...
     final ComponentName onBootReceiver = new ComponentName(getApplication().getPackageName(), BootReceiver.class.getName());
            if(getPackageManager().getComponentEnabledSetting(onBootReceiver) != PackageManager.COMPONENT_ENABLED_STATE_ENABLED)
            getPackageManager().setComponentEnabledSetting(onBootReceiver,PackageManager.COMPONENT_ENABLED_STATE_ENABLED,PackageManager.DONT_KILL_APP);
    ...
    

    我从 ApiDemos 中学到的最后一步。


    你应该在 onReceive 方法中检查传入的意图(BOOT_COMPLETED),否则你的应用将被安装在设备上的可疑应用程序调用。 - blueware

    5
    我有一个额外的<category>标签,不知道这是否会产生任何影响。
    <receiver android:name="BootIntentReceiver">  
            <intent-filter>  
                <action android:name="android.intent.action.BOOT_COMPLETED" />  
                <category android:name="android.intent.category.HOME" />  
            </intent-filter>  
    </receiver>
    

    您是否尝试省略if子句 "android.intent.action.BOOT_COMPLETED".equals(intent.getAction(),因为接收器可能只接收该意图?


    尝试了这个,但不起作用。顺便提一下,我忘了说我也有以下权限:<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> - Alex
    2
    为了以防万一:在AndroidManifest中的任何标签中添加android.intent.category.HOME将导致三星Galaxy Tab在使用hack关闭兼容模式后仍以兼容模式运行应用程序。不确定其他平板电脑是否也是如此。我建议根本不设置HOME类别,这是不必要的。 - moonlightcheese

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