【问题标题】:Keep broadcast receiver running after application is closed在应用程序关闭后保持广播接收器运行
【发布时间】:2013-05-29 21:40:08
【问题描述】:

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

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

    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");
        }
     }
}

【问题讨论】:

  • 您可能想创建一个service
  • 但是接收事件呢?
  • 您真的需要监控屏幕开/关事件吗?他们强迫您注册不在清单中的接收器的原因是他们不希望人们总是监视这些事件并减慢设备速度。

标签: java android broadcastreceiver


【解决方案1】:

你可以使用服务

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

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);
 }
}

【讨论】:

  • 所以当应用程序被强制关闭时,服务仍在运行?我不这么认为。
  • 我认为这与 Android "O" 不兼容
  • 是否可以从前台服务注册广播接收器,即使在应用关闭后也会运行??
  • 当应用程序从最近的屏幕中删除时这不起作用
  • @Bhavesh,你的意思是当应用关闭时广播接收器将无法工作?
【解决方案2】:

接受的答案不是我认为的实际答案。我会解释什么问题。我认为您正在 Huawie、Oppo、Vivo、小米、华硕...... 或某些设备上测试您的应用程序。使用这些设备,如果我们关闭应用程序,它们也会关闭我们的广播接收器。这就是问题所在。(检查是否使用了像素关联模拟器)。我将解释如何解决这个问题。``

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

    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;

【讨论】:

  • 实际上当应用的服务在运行时,它被认为是在运行。而这个特定的接收器 ACTION_SCREEN_ON 是在应用程序不运行时无法接收事件的接收器,即使它在清单中声明并且必须在永久运行的服务中创建。因此,您的答案是对已接受答案的很好补充,但本身并不是完整的答案。
  • 在小米红米6A上试过。应用程序被检查为“自动启动”,但当我关闭我的应用程序时服务会继续终止。我的服务在“普通”设备上运行良好,但在小米设备上无法运行
  • 此时他们改变了他们的机制。我会根据您的需要更新我的答案。
  • 您能解释一下您提供的代码/解决方案吗?真的很难理解为什么会显示对话框以及事情将如何运作。
【解决方案3】:

如果您使用的是 Android 4.4.x,请小心,因为在关闭应用程序时存在会终止后台服务的错误。我在 Android 4.4.2 中测试我的应用程序,我遇到了同样的问题。这里有详细解释:

http://www.androidpolice.com/2014/03/07/bug-watch-stopping-apps-on-android-4-4-2-can-silently-kill-related-background-services-a-fix-is-on-the-way/

【讨论】:

    【解决方案4】:

    您无法通过清单中声明的​​组件接收某些广播事件。

    这些事件是

    • 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

    所以在您的特定活动中, 您必须创建一个服务,并且必须使用 Context.registerReceiver() 在服务 onCreate() 中显式注册您的事件。

    其他活动, 清单中的条目就足够了。

    【讨论】:

      【解决方案5】:

      如果您在 Manifest 中声明 BroadcastReceiver,则即使应用程序关闭/停止,它也将始终处于活动状态并被调用

      【讨论】:

      • SCREEN_ON 和 SCREEN_OFF 不能在 Manifest 文件中声明。
      • @alfoks : 为什么不能在 AndroidManifest 中声明 SCREEN_ON 和 SCREEN_OFF?你能解释一下吗
      • @Kushal 这就是他们选择实现它的方式。我不记得这背后的理由。
      • @Kushal 因为它在documentation :)
      • 我在清单中声明它但如果我不动态创建接收器也不起作用。我正在尝试接收 ACTION_POWER_CONNECTED、ACTION_POWER_DISCONNECTED。
      【解决方案6】:

      您可以启动在前台运行的服务。这是确保(大多数情况下)您的应用程序将获得事件的唯一方法。在来自操作系统的疯狂内存压力时,您的前台服务仍有可能被杀死(因此它不是万无一失的)。如果您在前台启动服务,用户将看到一个持久通知,知道它一直在运行。

      所以故事的寓意是,您真的需要随时监控屏幕关闭/开启事件吗?他们强迫您注册不在清单中的接收器的原因是他们不希望人们总是监视这些事件并减慢设备速度。你想完成什么?

      【讨论】:

        【解决方案7】:

        我找到的最好方法是Foreground Services。我只在onStartCommand() 下注册了我的服务中的广播接收器,因为我希望我的服务需要始终运行,我返回了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);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-12-14
          • 1970-01-01
          相关资源
          最近更新 更多