繁体   English   中英

BroadcastReceiver的onReceive()被意外调用

[英]BroadcastReceiver has onReceive() called unexpectedly

我有一个应用程序,我想在其中每15分钟执行一次查找一些数据。 我有一项从警报开始的服务,但我也想确保在开始查找之前已建立网络连接。

为此,我认为我应该使用BroadcastReceiver来监视网络状态的变化。 我包装了一个广播接收器以帮助解决此问题:

public abstract class NetworkMonitor extends BroadcastReceiver
{
  boolean mDoingStuff;

  public abstract void doStuff();

  public NetworkMonitor()
  {
    mDoingStuff = false;

    IntentFilter networkStateFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
    MyApp.getContext().registerReceiver(this, networkStateFilter);      
  }

  @Override
  public void onReceive(Context context, Intent intent)
  {
    // network state changes, you can process it, information in intent
    ConnectivityManager cn = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo info = ConnectivityManagerCompat.getNetworkInfoFromBroadcast(cn, intent);

    // Only use Wifi Connections for updating this
    if (info.isConnectedOrConnecting() && !mDoingStuff)
    {
      mDoingStuff = true;
      doStuff();
    }
  }
}

然后,我将其用于类似以下的服务中:

public class WidgetUpdateService extends Service
{
  @Override
  public int onStartCommand(Intent intent, int flags, int startId)
  {
    // Build the async task to get the data
    final MyAsyncTask mTask = new MyAsyncTask();

    // Register an interest in  when the network changes
    new NetworkMonitor(false)
    {
      public void doStuff()
      {
        mTask.execute();
      }
    };

    // Make sure that if we get shut down then we get started again correctly.
    return START_REDELIVER_INTENT;
  }


  protected class MyAsyncTask extends AsyncTask<Void, Void, Void>
  {
    public MyAsyncTask()
    {
    }

    @Override
    protected Integer doInBackground(Void... arg0)
    {
      // do work
    }

    @Override
    protected void onPostExecute(Integer result)
    {
      WidgetUpdateService.this.stopSelf();
    }

    @Override
    protected void onCancelled(Integer result)
    {
      WidgetUpdateService.this.stopSelf();
    }
  }
}

其中MyAsyncTask是一个内部类,它将在服务完成后导致stopSelf()。

这种方法有效,但是:

  1. 我(根据logcat)对NetworkMonitor.doStuff()调用比我期望的要多得多。 看来,即使服务已停止(在异步任务正确完成之后), NetworkMonitor实例仍在接收有关网络状态更改的意图。 为什么是这样?
  2. 我是否需要一个变量来将NetworkMonitor()实例存储在服务中,还是可以只拥有一个这样的匿名实例? 查看文档, BroadcastReceiver应该在onReceive()完成后清除自身。
  3. 为什么需要NetworkMonitor.mDoingStuff 我猜想,如果我能弄清楚为什么在onReceive()完成后NetworkMonitor无法清除自身,那么我可能不再需要它了吗?
  4. 这是明智的做法还是我自找麻烦?

如果您需要更多信息,请告诉我,我们将很乐意提供。

这种作品

这是可怕的代码,恕我直言。

看来,即使服务已停止(在异步任务正确完成之后),NetworkMonitor实例仍在接收有关网络状态更改的意图。 为什么是这样?

因为您从不注销接收器。 它将继续进行-并像筛子一样泄漏内存-直到您的进程终止。

我是否需要一个变量来将NetworkMonitor()实例存储在服务中,还是可以只拥有一个这样的匿名实例?

您需要有一个实例,以便以后注销。 接收方的注册和注销应由服务部门完成; 您的构造器中的接收器是使您的代码可怕的恕我直言的一部分。

查看文档,广播接收器应在onReceive()完成后清除自身

清单注册的BroadcastReceiver用于单个广播。 通过registerReceiver()注册的BroadcastReceiver一直存在,直到unregisterReceiver()为止。

为什么需要NetworkMonitor.mDoingStuff?

你有更大的问题。

这是明智的做法吗

并不是的。

首先,由于您无法多次execute() AsyncTask实例,因此您将在第二次广播时崩溃。

其次,请参阅上述未能注销的问题。

第三,如果您想要一种服务做一件事情,然后走了,请使用IntentService

因此,让我们一直回到顶部:

我有一个应用程序,我想在其中每15分钟执行一次查找一些数据。 我有一项从警报开始的服务,但我也想确保在开始查找之前已建立网络连接。

正确的方法是:

  • 使您的AlarmManager事件路由到BroadcastReceiver 如果您使用的是_WAKEUP警报类型,这尤其重要,因为只有使用BroadcastReceiver PendingIntent此类事件才是可靠的。

  • 在该BroadcastReceiveronReceive() ,如果您具有网络连接,请向IntentService发送命令以完成工作(并且,如果使用的是_WAKEUP警报类型,请考虑我的WakefulIntentService ,以便设备在您处于睡眠状态时保持清醒状态)重新这样做)。

  • 相反,如果似乎没有网络连接,请让您的BroadcastReceiver启用另一个清单注册的BroadcastReceiver来监视CONNECTIVITY_ACTION事件-为此,请使用PackageManagersetComponentEnabledSetting()

  • CONNECTIVITY_ACTION BroadcastReceiveronReceive() ,如果确定现在已建立网络连接,请启动IntentService (与已经建立连接时从AlarmManager接收器执行的操作相同)。

  • IntentService / WakefulIntentService ,在onHandleIntent()完成您的工作。 它已经有一个后台线程,并且在没有更多工作要做时已经调用了stopSelf()

  • IntentService / WakefulIntentServiceonDestroy() ,通过PackageManagersetComponentEnabledSetting()禁用CONNECTIVITY_ACTION BroadcastReceiver ,使您返回到原始状态。

这条路:

  1. 您不会像在这里那样泄漏内存。

  2. 您不必像在这里所做的那样弄乱线程代码。

  3. 您不必担心在警报和获得连接之间您的进程是否被踢出内存。

  4. 如果连接被阻止了一段时间(例如,飞行模式),则不会像在此处那样注册N个接收器并设置N个AsyncTasks 取而代之的是,在您的警报响起以后,只要将来发生连接更改,您都将再次获得控制。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM