Android开机启动服务,如何在设备重启后重新启动服务类?

116

我需要在启动时启动服务。我搜索了很多。他们谈论Broadcastreceiver。由于我是Android开发的新手,我对Android上的服务没有清晰的理解。请提供一些源代码。


25
请不要在启动时启动服务并希望它永久运行,除非它在持续提供价值(例如 VOIP 客户端) 。在这些情况下,请在您的服务中使用 startForeground()。否则,Android 及其用户会将您的服务视为浪费空间,您将在 Android 市场上得到一些不愉快的评论。在大多数情况下,您认为需要在启动时启动服务时,最好使用 AlarmManager,以便您的服务可以周期性地运行,而不是持续运行。 - CommonsWare
2
@CommonsWare:很好的观点。但是请注意,要在重启后通过 AlarmManager 启动定期运行,您需要遵循非常相似的步骤(区别在于 onReceive 方法的内容)。 - stanwise
1
@CommonsWare:非常好的评论,我偶然发现了这个问题,你的提示恰好适合我的情况。如果它是一个答案,我会投票支持它 :-) - chiccodoro
9个回答

197

您的接收器:

public class MyReceiver extends BroadcastReceiver {   

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

     Intent myIntent = new Intent(context, YourService.class);
     context.startService(myIntent);

    }
}

你的 AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.broadcast.receiver.example"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name" android:debuggable="true">

        <activity android:name=".BR_Example"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    <!-- Declaring broadcast receiver for BOOT_COMPLETED event. -->
        <receiver android:name=".MyReceiver" android:enabled="true" android:exported="false">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
            </intent-filter>
        </receiver>

    </application>

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

</manifest>

5
文章链接失效了,但样例代码已经足够,所以点个赞 :) - Alex
3
实际上,它需要一点改进,你必须在接收器中使用wakelock,否则你的服务有一点可能不会启动。 - Vladimir Ivanov
1
不是的,但自Android 3.0以来,您必须至少运行一次该应用程序。 - Vladimir Ivanov
如果应用程序被从设置中强制关闭,这是否有效? 应用程序仍然会唤醒吗? - Srihari Karanth
这个服务没有在清单文件中声明,它是如何工作的? - samsri
显示剩余2条评论

97

2
唤醒锁怎么办?在服务启动时,设备可能会决定进入睡眠状态... - Marian Paździoch
我需要启动我的手机至少一次才能启动服务吗? - pathe.kiran
@MarianPaździoch 是正确的,你需要一个唤醒锁。请查看我的答案:https://dev59.com/z2455IYBdhLWcg3wD_yZ#30970167 - phreakhead
@pathe.kiran 在启动时,是的。请参见 https://commonsware.com/blog/2011/07/05/boot-completed-regression.html - Tim
您的最新URL已过时。 - Prizoff

33

当设备启动时自动启动你的应用程序是可能的。例如,当你想要从HTTP服务器接收推送事件,并在新事件发生时尽快通知用户时,你就需要这样做。在服务开始之前,用户不必手动启动活动...

这很简单。首先,给你的应用程序授予 RECEIVE_BOOT_COMPLETED 权限。接下来,你需要注册一个广播接收器。我们称之为 BootCompletedIntentReceiver。

现在,你的 Manifest.xml 应该像这样:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.jjoe64">
 <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
 <application>
  <receiver android:name=".BootCompletedIntentReceiver">
   <intent-filter>
    <action android:name="android.intent.action.BOOT_COMPLETED" />
   </intent-filter>
  </receiver>
  <service android:name=".BackgroundService"/>
 </application>
</manifest>

作为最后一步,您需要实现接收器。该接收器只需启动您的后台服务。

package com.jjoe64;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;

import com.jjoe64.BackgroundService;

public class BootCompletedIntentReceiver extends BroadcastReceiver {
 @Override
 public void onReceive(Context context, Intent intent) {
  if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {
   Intent pushIntent = new Intent(context, BackgroundService.class);
   context.startService(pushIntent);
  }
 }
}
http://www.jjoe64.com/2011/06/autostart-service-on-device-boot.html获取的内容。

3
可以的,我会尽力为您翻译。请提供需要翻译的内容。 - dbkoren
在清单文件中声明您的服务不仅是正确的,而且是必需的。与活动一样。 - Tim
主活动在哪里?没有活动或android.intent.category.LAUNCHER制作应用程序是不正确的! - nick
@L'Esperanza 当然可以,你可以拥有没有可见活动的应用程序! - appsthatmatter
@L'Esperanza 不对哦。应用可以在没有启动活动的情况下启动 :) - Zarul Izham
显示剩余2条评论

15

这里发布的大多数解决方案都缺少一个重要的部分:如果不使用唤醒锁,则存在服务在处理完成之前被杀死的风险。我在另一个帖子中看到了这个解决方案,也在这里回答。

由于 WakefulBroadcastReceiver 已在API 26中弃用,建议在API级别低于26的设备上使用。

您需要获取唤醒锁。幸运的是,支持库提供了一个类来执行此操作:

public class SimpleWakefulReceiver extends WakefulBroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // This is the Intent to deliver to our service.
        Intent service = new Intent(context, SimpleWakefulService.class);

        // Start the service, keeping the device awake while it is launching.
        Log.i("SimpleWakefulReceiver", "Starting service @ " + SystemClock.elapsedRealtime());
        startWakefulService(context, service);
    }
}

然后,在您的服务中,请确保释放唤醒锁:

    @Override
    protected void onHandleIntent(Intent intent) {
        // At this point SimpleWakefulReceiver is still holding a wake lock
        // for us.  We can do whatever we need to here and then tell it that
        // it can release the wakelock.

...
        Log.i("SimpleWakefulReceiver", "Completed service @ " + SystemClock.elapsedRealtime());
        SimpleWakefulReceiver.completeWakefulIntent(intent);
    }

不要忘记添加WAKE_LOCK权限并在清单文件中注册您的接收器:

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

...

<service android:name=".SimpleWakefulReceiver">
    <intent-filter>
        <action android:name="com.example.SimpleWakefulReceiver"/>
    </intent-filter>
</service>

2
在清单文件中,SimpleWakefulReceiver不是一个服务。 - Desmond Lua
2
WakefulBroadcastReceiver已被弃用。 - Amin Pinjari

5

您需要注册BOOT_COMPLETE和REBOOT事件

<receiver android:name=".Services.BootComplete">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED"/>
            <action android:name="android.intent.action.REBOOT"/>
        </intent-filter>
    </receiver> 

3
文献提到,'android.intent.action.REBOOT' 只能被有特权的应用程序/代码使用。否则,这有什么优势呢? - XMAN

1

要在Android O或更高版本(即操作系统>28)中重新启动服务,请使用以下代码KOTLIN VERSION 1)在清单中添加权限

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

2) 创建一个Class并扩展它以使用BroadcastReceiver

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
import android.util.Log
import androidx.core.content.ContextCompat



class BootCompletedReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, arg1: Intent?) {
        Log.d("BootCompletedReceiver", "starting service...")
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            ContextCompat.startForegroundService(context, Intent(context, YourServiceClass::class.java))
        } else {
            context.startService(Intent(context, YourServiceClass::class.java))
        }
    }
}

3) 在应用程序标签下,像这样在清单文件中声明:

<receiver android:name=".utils.BootCompletedReceiver" >
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
            <action android:name="android.intent.action.QUICKBOOT_POWERON" />
        </intent-filter>
    </receiver>

1

同时在清单文件中注册您创建的服务,并使用以下权限:

<application ...>
   <service android:name=".MyBroadcastReceiver">
        <intent-filter>
            <action android:name="com.example.MyBroadcastReciver"/>
        </intent-filter>
   </service>
</application>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

然后在广播接收器中调用您的服务。
public class MyBroadcastReceiver extends BroadcastReceiver 
{
    @Override
    public void onReceive(Context context, Intent intent)
    {
        Intent myIntent = new Intent(context, MyService.class);
        context.startService(myIntent);
    }
}

为什么服务中要使用意图过滤器? - Joaquin Iurchuk
因为当启动完成后,MyService 将被调用。 - SoftEye
在这种情况下,您的服务类将扩展服务和广播接收器。我说得对吗? - Joaquin Iurchuk
该类将继承Service类。 - SoftEye
2
这里有些问题。该服务应该从广播接收器中调用。但是你说你的服务就是广播接收器,然后告诉我服务类没有扩展广播接收器。因此,它将无法接收启动完成广播。当你声明onReceive方法时,你覆盖了什么? - Joaquin Iurchuk
是的,您说得对,该服务将从广播接收器调用,并且接收器将在启动时被调用。 - SoftEye

0

首先在你的 manifest.xml 文件中注册一个接收器:

    <receiver android:name="com.mileagelog.service.Broadcast_PowerUp" >
        <intent-filter>
            <action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
            <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
        </intent-filter>
    </receiver>

然后为此接收器编写一个广播,如下所示:

public class Broadcast_PowerUp extends BroadcastReceiver {

  @Override
  public void onReceive(Context context, Intent intent) {
    String action = intent.getAction();

    if (action.equals(Intent.ACTION_POWER_CONNECTED)) {
        Toast.makeText(context, "Service_PowerUp Started",
                Toast.LENGTH_LONG).show();


    } else if (action.equals(Intent.ACTION_POWER_DISCONNECTED)) {



        Toast.makeText(context, "Service_PowerUp Stoped", Toast.LENGTH_LONG)
        .show();
    }
  }
}

-1

请查看 JobScheduler 获取26以上的API

WakeLock 是最佳选择,但已在api level 26中过时。 如果您考虑使用26以上的api,请查看this link
https://developer.android.com/reference/android/support/v4/content/WakefulBroadcastReceiver.html#startWakefulService(android.content.Context,%20android.content.Intent)

它说:

从Android O开始,后台检查限制使得这个类不再普遍有用。(从广播接收器启动服务通常是不安全的,因为此时您无法保证您的应用程序在前台,因此允许这样做。)相反,开发人员应该使用android.app.job.JobScheduler来调度作业,这不需要应用程序在执行此操作时持有唤醒锁(系统将负责为作业持有唤醒锁)。
因此,建议考虑使用JobScheduler
https://developer.android.com/reference/android/app/job/JobScheduler 如果要做某些事情而不是启动和保持它,则可以接收ACTION_BOOT_COMPLETED广播。
如果不涉及前台,请检查是否可以使用辅助功能服务。
另一个选项是从广播接收器启动活动,并在onCreate()中启动服务后完成它,因为新版本的Android不允许从接收器启动服务。

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