在应用关闭后保持广播接收器运行

32

我需要在应用启动后一直保持广播接收器运行。

这是注册该接收器在应用程序中的代码。

    IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
    filter.addAction(Intent.ACTION_SCREEN_OFF);
    BroadcastReceiver mReceiver = new ScreenEventsReceiver();
    registerReceiver(mReceiver, filter);

并为接收器编写代码

public class ScreenEventsReceiver extends BroadcastReceiver {
     public static boolean wasScreenOn = true;

     @Override
     public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
            wasScreenOn = false;
            Log.d("ScreenEventReceiver", "ON");
        } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
            wasScreenOn = true;
            Log.d("ScreenEventReceiver", "ON");
        }
     }
}

1
你可能想要创建一个服务 - j.holetzeck
但是接收事件怎么办? - Sergey Pekar
1
你真的需要监控屏幕开/关事件吗?他们强制你在清单之外注册接收器的原因是他们不希望人们始终监视这些事件并减慢设备速度。 - natez0r
7个回答

30

您可以使用一个服务

在主应用程序中启动/停止该服务

Intent service = new Intent(context, MyService.class);
context.startService(service);
...
Intent service = new Intent(context, MyService.class);
context.stopService(service);

服务

public class MyService extends Service
{
 private static BroadcastReceiver m_ScreenOffReceiver;

 @Override
 public IBinder onBind(Intent arg0)
 {
  return null;
 }

 @Override
 public void onCreate()
 {
  registerScreenOffReceiver();
 }

 @Override
 public void onDestroy()
 {
  unregisterReceiver(m_ScreenOffReceiver);
  m_ScreenOffReceiver = null;
 }

 private void registerScreenOffReceiver()
 {
  m_ScreenOffReceiver = new BroadcastReceiver()
  {
   @Override
   public void onReceive(Context context, Intent intent)
   {
     Log.d(TAG, "ACTION_SCREEN_OFF");
     // do something, e.g. send Intent to main app
   }
  };
  IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
  registerReceiver(m_ScreenOffReceiver, filter);
 }
}

6
当应用程序被强制关闭时,服务仍然在运行吗?我不这么认为。 - Someone Somewhere
8
我认为这不兼容Android "O"。 - Someone Somewhere
在应用程序关闭后仍然运行的前台服务是否可以注册广播接收器? - K Pradeep Kumar Reddy
当应用程序从最近的屏幕中移除时,这个功能就无法使用了。 - Bhavesh
@Bhavesh,你的意思是当应用程序关闭时广播接收器将无法工作吗? - K Pradeep Kumar Reddy

17

我认为被接受的答案并不是一个真正的答案。我会解释问题所在。我想你正在测试你的应用程序在华为、Oppo、Vivo、小米、华硕等设备上。使用这些设备,如果我们关闭应用程序,它们也将关闭我们的广播接收器。这就是问题所在。(要检查,请使用pixel nexus模拟器)。我将解释如何解决这个问题。

  • 我们将把我们的应用程序添加到受保护的应用程序列表中。操作系统只允许它们继续进行广播接收器活动。(将此数组声明复制到您的代码中)

private static final Intent[] POWERMANAGER_INTENTS = {
    new Intent().setComponent(new ComponentName("com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity")),
    new Intent().setComponent(new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.AutobootManageActivity")),
    new Intent().setComponent(new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity")),
    new Intent().setComponent(new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.appcontrol.activity.StartupAppControlActivity")),
    new Intent().setComponent(new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.permission.startup.StartupAppListActivity")),
    new Intent().setComponent(new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.startupapp.StartupAppListActivity")),
    new Intent().setComponent(new ComponentName("com.oppo.safe", "com.oppo.safe.permission.startup.StartupAppListActivity")),
    new Intent().setComponent(new ComponentName("com.iqoo.secure", "com.iqoo.secure.ui.phoneoptimize.AddWhiteListActivity")),
    new Intent().setComponent(new ComponentName("com.iqoo.secure", "com.iqoo.secure.ui.phoneoptimize.BgStartUpManager")),
    new Intent().setComponent(new ComponentName("com.vivo.permissionmanager", "com.vivo.permissionmanager.activity.BgStartUpManagerActivity")),
    new Intent().setComponent(new ComponentName("com.samsung.android.lool", "com.samsung.android.sm.ui.battery.BatteryActivity")),
    new Intent().setComponent(new ComponentName("com.htc.pitroad", "com.htc.pitroad.landingpage.activity.LandingPageActivity")),
    new Intent().setComponent(new ComponentName("com.asus.mobilemanager", "com.asus.mobilemanager.MainActivity"))};
  • 将这些代码放到您的onCreate方法中。在这里,我使用共享首选项仅检查应用程序首次打开。

  • `

    final SharedPreferences.Editor pref =    getSharedPreferences("allow_notify", MODE_PRIVATE).edit();    pref.apply(); final SharedPreferences sp =    getSharedPreferences("allow_notify", MODE_PRIVATE)`;
    
    
        if(!sp.getBoolean("protected",false)) {
            for (final Intent intent : POWERMANAGER_INTENTS)
                if (getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
    
                AlertDialog.Builder builder  = new AlertDialog.Builder(this);
                builder.setTitle("Alert Title").setMessage("Alert Body")
                        .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialogInterface, int i) {
                                startActivity(intent);
                                sp.edit().putBoolean("protected",true).apply();
    
                            }
                        })
                        .setCancelable(false)
                        .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                            }
                        })
                        .create().show();
                break;
    

    2
    实际上,当应用程序的服务正在运行时,它被视为正在运行。而这个特定的接收器ACTION_SCREEN_ON是一个不能在应用程序不运行时接收事件的接收器,即使它在清单中声明,也必须在永久运行的服务中创建。因此,您的答案是对已接受答案的很好补充,但本身并不完整。 - Sergey Pekar
    尝试在小米Redmi 6A上运行。应用程序被标记为“自动启动”,但是当我关闭我的应用程序时,服务仍然会被杀死。我的服务在“普通”设备上运行得非常完美,但在小米设备上无法工作。 - Vinícius Carvalho
    此时他们改变了他们的机制。我会更新我的答案以适应您的需求。 - Tharuka Lakshan
    你能否解释一下你提供的代码/解决方案?真的很难理解为什么会显示对话框以及事情将如何运作。 - Virat18

    2
    如果您在清单文件中声明了BroadcastReceiver,则即使应用程序已关闭/停止,它仍将保持激活状态并被调用。

    5
    无法在清单文件中声明SCREEN_ON和SCREEN_OFF。 - alfoks
    @alfoks:为什么 SCREEN_ON 和 SCREEN_OFF 不能在 AndroidManifest 中声明?你能解释一下吗? - Kushal
    @Kushal 这只是他们选择实现它的方式。我不记得这背后的理由了。 - alfoks
    3
    因为它在文档中有说明。 - Nikiforos
    我已在清单文件中声明,但如果我不动态创建接收器,那就行不通。我正在尝试接收ACTION_POWER_CONNECTED、ACTION_POWER_DISCONNECTED。 - Burak Kaymakci

    2

    2
    您无法通过在清单中声明的组件接收某些广播事件。
    这些事件包括:
    - ACTION_BATTERY_CHANGED - ACTION_CONFIGURATION_CHANGED - ACTION_SCREEN_OFF (您正在处理此事件) - ACTION_SCREEN_ON (您正在处理此事件) - ACTION_TIME_TICK
    参考 https://developer.android.com/reference/android/content/Intent.html#ACTION_SCREEN_ON 因此,在您特定的事件中,您需要创建一个服务,并在服务的onCreate()中使用Context.registerReceiver()显式地注册您的事件。
    对于其他事件,只需在清单中声明即可。

    1
    您可以启动一个在前台运行的服务。这是确保(大部分)您的应用程序将获得事件的唯一方法。但仍有可能在操作系统内存压力过大时杀死您的前台服务(因此它并不是百分之百可靠的)。如果您在前台启动服务,则用户将看到一个持久通知,以便知道它始终在运行。
    所以故事的寓意是,您真的需要始终监控屏幕开/关事件吗?他们强制要求您在清单中注册接收器的原因是他们不希望人们始终监视这些事件并减慢设备速度。您想要实现什么目标?

    0
    我发现最好的方法是使用“前台服务”。 我在Service的“onStartCommand()”下注册了我的BroadcastReceiver,因为我希望我的服务始终运行,并返回了“START_STICKY”。
    这样,即使从堆栈中终止应用程序,我的广播接收器仍能存活。
    在我的服务中使用以下代码:
        @Override
            public int onStartCommand(Intent intent, int flags, int startId) {
                Log.i("rht", "Received start id " + startId + ": " + intent);
        
                String input = intent.getStringExtra("inputExtra");
                createNotificationChannel();
                Intent notificationIntent = new Intent(this, MainActivity.class);
                PendingIntent pendingIntent = PendingIntent.getActivity(this,
                        0, notificationIntent, 0);
                Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                        .setContentTitle("Foreground Service")
                        .setContentText(input)
                        .setSmallIcon(R.drawable.ic_launcher_background)
                        .setContentIntent(pendingIntent)
                        .build();
                startForeground(1, notification);
    }
    

    这是我开始我的服务的方式

    Intent serviceIntent = new Intent(this, SpeechServiceForeground.class);
    ContextCompat.startForegroundService(this, serviceIntent);
    

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