【问题标题】:BroadcastReceiver dies with appBroadcastReceiver 死于应用程序
【发布时间】:2017-01-15 15:27:32
【问题描述】:

如果我让手机长时间放置 15 分钟,我会丢失我的接收器,但我认为它会像服务一样在被杀死记忆后继续存在。

清单:

<receiver
    android:name=".WearableReceiver"
    android:enabled="false">
    <intent-filter>
        <action android:name="com.example.johnbravado.MESSAGE_PROCESSED"/>
    </intent-filter>
</receiver>

在Activity中启动接收器

ComponentName component = new ComponentName(CounterActivity.this, WearableReceiver.class);
getPackageManager()
    .setComponentEnabledSetting(component,
        PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
        PackageManager.DONT_KILL_APP);

接收者

@Override
public void onReceive(Context context, Intent intent) {
    // TODO: This method is called when the BroadcastReceiver is receiving
    // an Intent broadcast.
    //MyConstants.getInstance().showToast("Message Rcvd");
    PowerManager powerManager = (PowerManager) context.getSystemService(POWER_SERVICE);
    PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
            "com.example.johnbravado");
    wakeLock.acquire();
    // Do Work
    MyConstants.getInstance().msgReqAction(intent.getIntExtra(MyConstants.BROADCAST_DATA_REQ, 0));

    wakeLock.release();
}

广播发送者

String BROADCAST_ACTION_RESP = "com.example.johnbravado.MESSAGE_PROCESSED"
@Override
public void onMessageReceived(final MessageEvent messageEvent) {
    nodeId = messageEvent.getSourceNodeId();
    String incomingPath = messageEvent.getPath();
    int incomingReq = Integer.parseInt(new String(messageEvent.getData()));

    if(incomingPath.equalsIgnoreCase(MyConstants.MSG_COUNTER_REQ_PATH)) {
        Intent broadcastIntent = new Intent();
        broadcastIntent.setAction(BROADCAST_ACTION_RESP);
        broadcastIntent.putExtra(MyConstants.BROADCAST_DATA_REQ, incomingReq);
        sendBroadcast(broadcastIntent);

    }else if(incomingPath.equalsIgnoreCase(MyConstants.MSG_DEFAULT_PATH)){

    }
}

让这种情况持续很长时间的唯一方法是调用服务

wearableReceiverIntent = new Intent(this, WearableReceiverService.class);
if(!WearableReceiverService.isRunning())
    startService(wearableReceiverIntent);

服务

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    // Let it continue running until it is stopped.
    IntentFilter filter = new IntentFilter(MyConstants.BROADCAST_ACTION_RESP);
    filter.addCategory(Intent.CATEGORY_DEFAULT);
    receiver = new WearableReceiver();
    registerReceiver(receiver, filter);

Notification notification = new NotificationCompat.Builder(this)
        .setSmallIcon(R.drawable.ic_notif_bible)
        .setContentText("Preaching").build();
startForeground(MyConstants.NOTIF_COUNTING_SERVICE, notification);

    isRunning = true;
    return START_STICKY;
}

如果我运行该服务,它会持续很长时间,但考虑到我每 10 分钟只交互一次,它会不必要地耗尽电池。我的印象是广播接收器会像服务一样工作,除了短时间的工作。如果您需要执行较长的操作,请调用该服务。

【问题讨论】:

  • 详细解释,“我失去了我的接收者”是什么意思,以及您是如何确定这一点的。
  • 它不再执行接收者的 onReceived 内的行为。
  • 您何时/如何通过PackageManager 禁用该组件?另外,您确定在 15 分钟后不会调用 onReceive() 吗?你是如何验证的?
  • 我有一个 start() 和一个 stop()。在 start() 中,我启用了接收器和我想使用上面的代码运行的服务。在 stop() 中,我禁用它。我相信它没有被调用,因为直到 android 为内存而杀死它之前它工作正常。更好的测试是输出到日志文件,看看我是否输入了它。但我不知道如何访问这些日志文件。除非这就是 logcat 应用程序的用途?

标签: android android-service android-broadcastreceiver


【解决方案1】:

BroadcastReceiver 处理一个意图,然后再次停止。这种对意图的处理应该很快。如果你想做很多事情,你应该从BroadcastReceiver 开始一个Service 并从那里处理它。

BroadcastReceiver 对象仅在通话期间有效 到 onReceive(上下文,意图)。一旦您的代码从此返回 功能,系统认为对象已完成,不再 活跃。

使用sendBroadcast 意图启动广播接收器。 所以删除android:enabled="false"并使用sendBroadcast,这将通过Android启动接收器。

http://www.vogella.com/tutorials/AndroidBroadcastReceiver/article.html

【讨论】:

  • 我可以正确发送广播,因为只要手机处于唤醒状态且应用程序正在运行且未被终止,应用程序就会按预期工作。它不需要在前台只是没有死。我还通过电源管理器打开和关闭接收器,这样它就不会一直运行,因为我不需要那个功能。
【解决方案2】:

屏幕关闭时,Greenify 正在杀死我的应用程序。我正在与一些我没有希望用代码防御的东西作斗争。在我明确告诉 Greenify 不要杀死我的应用程序之后,我从来没有告诉它一开始就杀死我的应用程序,一切都按预期工作。

【讨论】:

    【解决方案3】:

    我的 Asus ZenPad 遇到了同样的问题,因为华硕移动管理器应用程序,特别是“自动启动管理器”阻止了我的应用程序的意图。

    停用应用程序(无法卸载)一文不值,解决方案是让应用程序保持安装状态,但将我正在开发的应用程序列入白名单,以便它可以接收像 PACKAGE_REPLACE 这样的广播。 (请注意开关令人困惑,您实际上必须触摸“阻止”才能将其变为“允许”才能启用它。

    我认为另一种选择是更新或更改 ROM(选择一个没有所有臃肿软件的)。

    【讨论】:

      【解决方案4】:

      如果您的 BroadcastReceiver 已在您的清单中设置,则无需尝试为您的包调整 PackageManager 组件信息。只要你去掉enabled="false" 部分。

      您的BroadcastReceiver 的作用应该非常简短:通常更新一些内部数据或启动另一个组件,这些组件可以完成您的应用程序操作的繁重工作。您可以使用它触发Service 在后台执行此类操作。但是,请注意,在这种情况下,“背景”意味着没有用户交互。它并不意味着执行的背景上下文,例如辅助线程。您可以自行管理Service 中的线程。您的 BroadcastReceiverService 回调入口点(onReceive()onStartIntent())在您应用的主线程上下文中运行。

      电源管理肯定在这一切中发挥了重要作用。您的广播Intent 是否实际上是以唤醒设备的方式发送和完成的?如果它确实唤醒了设备并发送了Intent,那么设备将只保持唤醒足够长的时间,让BroadcastReceiver 运行它的onReceive();之后返回没有任何保证。该设备将积极睡眠,这就是为什么唤醒锁是一回事。但是,除非使用得当,否则使用唤醒锁会导致过度消耗电池电量。如果您在 Marshmallow 或更新版本上运行,Doze 功能也会破坏您的计划。在打盹模式下,唤醒锁将被忽略,并且在用户将设备从打盹模式中恢复之前不会被考虑。

      【讨论】:

      • 我设置了 enabled="false" 并使用了电源管理器,因为我不想让它一直听。只有当我在主应用程序中激活它时。因此,与其检查“isItWorking()”,我只想启用/禁用接收器周期。我只是想更新一个共享偏好。我添加了一个代码 sn-p 来显示我如何发送广播并且我知道它可以工作,因为如果应用程序处于活动状态并且没有被杀死,它会按预期工作。只有当设备“休眠”或杀死我的应用程序时,我才会失去功能。所以你可能是对的,我没有以唤醒设备的方式发送意图。
      • 手动操作启用标志的问题是,如果您的应用程序被杀死,或者在设备休眠时将组件标记为禁用,系统无法知道它需要唤醒或启动您的应用程序。在清单中注册接收者后,系统知道它何时需要启动您的应用程序,即使它已被杀死。你所做的大致相当于调用registerReceiver()
      • 很公平。我确实尝试让接收器在 100% 的时间内运行,并在 1 点更改了 enabled="true",试图让这个东西唤醒设备。我测试的手机正在运行 Nougat。所以目前我使用接收器并启用唤醒锁。最好改为执行 startService 命令并运行onStartCommand() 调用msgReqAction() 的服务。可以想象,它接收到广播但由于无法保持唤醒锁而无法执行所请求的功能吗?还是我在接收广播时段时遇到问题
      • 那里发生了很多事情!使用 Nougat,您将面临打瞌睡模式的问题。在 Marshmallow 之前,您可以让您的接收器获取唤醒锁并启动您的 Service,它会完成它的工作然后释放唤醒锁(有关详细信息,请参阅 WakefulReceiver。)但是,使用打盹模式会变得更加复杂。您可能希望尝试使用您的接收器,然后通过JobScheduler 安排工作。在打盹模式的周期性唤醒间隔期间,该工作仍将被“批处理”,但它至少会执行。
      【解决方案5】:

      我遇到了同样的问题,通过授予应用程序的自动启动权限来解决。

      转到 设置->权限->管理自动启动 并允许自动启动您的应用。

      【讨论】:

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