【问题标题】:WakefulBroadcastReceiver intent not starting only in some cases (strange)WakefulBroadcastReceiver 意图仅在某些情况下不启动(奇怪)
【发布时间】:2015-11-13 19:08:25
【问题描述】:

我的应用程序中发生了一个非常奇怪的错误。我正在构建一个警报应用程序,我正在使用 SQLite 来存储警报数据并使用广播接收器来管理警报管理器调用。

奇怪的是,onReceive 的代码在某些情况下的行为方式并不相同。当接收器收到广播时,我尝试启动一个 Activity,几乎 90% 的情况一切顺利,我设法启动了 Activity,但在某些情况下,接收器执行指令“startActivity(i)”但没有任何反应。

重现 BUG 确实很困难,在调试过程中我已经了解了我提到的内容,但除此之外,我真的很难理解在大多数情况下调用 startActivity() 是如何工作的,并且在某些情况下确实如此不行。我已经搜索过 Stack 社区,但没有人遇到这种问题,每个人都只是在开始活动时遇到了问题,因为他们没有设置标志或因为他们没有在清单中注册接收者。下面我贴出代码。

public class AlarmReceiver extends WakefulBroadcastReceiver {
    // The app's AlarmManager, which provides access to the system alarm services.
    private AlarmManager alarmMgr;
    // The pending intent that is triggered when the alarm fires.
    private PendingIntent alarmIntent;

    @Override
    public void onReceive(Context context, Intent intent) {
        Utils.logToFile("Received Alarm ,I am in onReceive(), ALARM ID: "+intent.getExtras().getInt(Constants.ALARM_ID));
        Intent intent = new Intent(context, StopAlarm.class);
        Bundle b = new Bundle();
        b.putInt(Constants.ALARM_ID, intent.getExtras().getInt(Constants.ALARM_ID));
        if(intent.getExtras().containsKey(Constants.SNOOZE_ALARM)){
            b.putString(Constants.SNOOZE_ALARM, intent.getExtras().getString(Constants.SNOOZE_ALARM));
        }
        i.putExtras(b);
        //this flag is needed to start an Activity from a BroadcastReceiver
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(intent);


        //this method reads from the DB and sets the next alarm
        //I tried commenting this method so that no DB action is 
        //performed and still the bug happened
        setAlarm(context.getApplicationContext());
        //this method just logs data into a file that I have created to keep track of events 
        //since not always the device is connected with LogCat
        Utils.logToFile("Received Alarm, Intent(context, StopAlarm.class);");
    }

我是否需要设置任何其他标志?在某些情况下 startActivity(intent) 行为如何可能不正确?

编辑

<activity
    android:label="@string/app_name"
    android:name="package.activity.StopAlarm"
    android:windowSoftInputMode="stateAlwaysHidden" 
    android:screenOrientation="sensorPortrait">
</activity>

<receiver android:name="package.receivers.AlarmReceiver" />

【问题讨论】:

  • 请在您的 Manifest 中发布您的活动声明代码,以便更好地了解问题。
  • 刚刚添加了清单。代码在 90% 的情况下都有效,因此意图启动了我的活动,但奇怪的是在某些情况下它并没有启动它。
  • 您的代码看起来不错。我可以推荐的唯一更改是使用 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 而不是 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)。此外,当它失败的 10% 的时候,是当你通过按下主页按钮将 Activity 置于后台时。
  • 我将尝试通过将标志添加到意图中来设置它,而不只是设置它。尽管很可能它会产生很大的不同,但我仍然会尝试它,希望能解决问题。发生错误的那 10% 不仅在您提到的情况下。即使奇怪的是,在相同的用例中,应用程序在 90% 的情况下表现良好,但有时在这 10% 的情况下,即使用例始终相同,我也会遇到该错误。
  • 现在我将尝试 google 的方式来处理 wakefulBroadcasts :developer.android.com/reference/android/support/v4/content/….

标签: android android-intent broadcastreceiver


【解决方案1】:

我终于通过创建一个 IntentService 并从 IntentService 启动活动并为 Intent 设置两个标志来解决了这个问题。完成此操作后,我将从数据库读取的代码放在从 IntentService 启动的活动中。我已经测试了将近 60 次的行为,并且在所有测试中应用程序的行为都是正确的。我在下面发布代码。

    public class MyAlarmReceiver extends WakefulBroadcastReceiver {
        // The app's AlarmManager, which provides access to the system alarm services.
        private static AlarmManager alarmMgr;
        // The pending intent that is triggered when the alarm fires.
        private static PendingIntent alarmIntent;

        @Override
        public void onReceive(Context context, Intent intent) {
            Intent i = new Intent(context, AlarmIntentService.class);
            Bundle b = new Bundle();
            b.putInt(Constants.ALARM_ID, intent.getExtras().getInt(Constants.ALARM_ID));
            if(intent.getExtras().containsKey(Constants.SNOOZE_ALARM)){
                b.putString(Constants.SNOOZE_ALARM, intent.getExtras().getString(Constants.SNOOZE_ALARM));
            }
            i.putExtras(b);
            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startWakefulService(context, i);
        }

这是我需要实现的 IntentService

    public class AlarmIntentService extends IntentService {
        public AlarmIntentService() { 
            super("AlarmIntentService"); 
        } 

        @Override 
        protected void onHandleIntent(Intent intent) {
            Intent i = new Intent(this, StopAlarm.class);
            Bundle b = new Bundle();
            b.putInt(Constants.ALARM_ID, intent.getExtras().getInt(Constants.ALARM_ID));
            if(intent.getExtras().containsKey(Constants.SNOOZE_ALARM)){
                b.putString(Constants.SNOOZE_ALARM, intent.getExtras().getString(Constants.SNOOZE_ALARM));
            }
            i.putExtras(b);
            //THESE ARE THE FLAGS NEEDED TO START THE ACTIVITY AND TO PREVENT THE BUG
            //(CLEAR_TASK is crucial for the bug and new task is needed to start activity from outside of an activity)
            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK);

            startActivity(i);

            MyAlarmReceiver.completeWakefulIntent(intent);
        } 
    } 

这是由 IntentService 启动的活动。这里我设置了下一个闹钟。

public class StopAlarm extends Activity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.stop_alarm);
        //this method reads from the DB and sets the next alarm
        MyAlarmReceiver.setAlarm(getApplicationContext());
        ...

【讨论】:

  • 但是 Intent.FLAG_ACTIVITY_CLEAR_TASK 破坏了我的应用业务逻辑(可能有多个警报活动)。怎么办?
  • P.S. : 有没有必要:Intent i = new Intent(context, AlarmIntentService.class); i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 必要的??? startWakefulService(context, i);
  • 这就是删除标志后得到的结果。现在我检查当从 AndroidMonitor 的崩溃日志中删除 Clear_Task 时会发生什么“android.util.AndroidRuntimeException:从 Activity 上下文外部调用 startActivity() 需要 FLAG_ACTIVITY_NEW_TASK 标志。这真的是你想要的吗?”
  • i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);我试过只有这个标志,它看起来可以作为第一个测试,但你必须更多地测试它,看看它是否真的没问题,或者你是否会遇到我的旧问题。尽管如此,我认为只有这个标志应该没问题。顺便说一句,它破坏了你的业务逻辑是什么意思
  • 1.当我使用 FLAG_ACTIVITY_CLEAR_TASK 时。它清除所有打开的活动实例。这种情况与我的情况相冲突(可能打开了多个警报活动)。 2. i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 必要的??? -> 在那里,您正在启动一项服务而不是一项活动。我认为在这种情况下,这个标志是没有必要的。
【解决方案2】:

我遇到了类似的问题。根据我的经验,问题是 startActivity(i) 是异步的。所以在这种情况下程序会同时调用activity onCreate()、onStart()等,还会调用QlokAlarmReceiver.completeWakefulIntent(intent)(不等待activity关闭),这会释放wakeLock。因此,设备可以在执行 onCreate() 或 onStart() 活动期间进入睡眠状态。

鲁宾,我知道,我的回答与您的解决方案相矛盾,但我的日志清楚地表明了这样的事件顺序: - 调用 startActivity - 活动的 onCreate 调用 - 完成WakefulIntent(意图);在活动的 onStart 的日志之间调用

我的解决方法是在调用 startActivity 之前启动一个超时时间为 20 秒的唤醒锁,然后在 onCreate 活动中启动另一个唤醒锁,这将在 onDestroy 方法中释放。 我不确定我的解决方案是否符合最佳实践,但到目前为止我还没有找到更好的解决方案。

【讨论】:

    猜你喜欢
    • 2020-05-04
    • 1970-01-01
    • 1970-01-01
    • 2012-08-17
    • 2014-01-17
    • 2020-06-20
    • 2015-05-02
    • 1970-01-01
    • 2016-11-04
    相关资源
    最近更新 更多