BroadcastReceiver意外调用了onReceive()

时间:2013-04-29 21:54:52

标签: java android service android-asynctask broadcastreceiver

我有一个应用程序,我想在其中执行每个查询一些数据,比如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是一个内部类,它会在服务完成后导致服务停止自动()。

这种方法有效但是:

  1. 我得到(根据logcat)对NetworkMonitor.doStuff()的调用远远超出我的预期。似乎即使服务已停止(异步任务正确完成后),NetworkMonitor实例仍在接收有关网络状态更改的意图。为什么是这样?
  2. 我是否需要有一个变量来存储服务中的NetworkMonitor()实例,或者我可以只拥有这样的匿名实例?查看文档BroadcastReceiver应在onReceive()完成后自行解决。
  3. 为什么我需要NetworkMonitor.mDoingStuff?我猜想如果我能解决为什么在NetworkMonitor完成后onReceive()没有自我清理的话,我可能不再需要了它?
  4. 这是一种明智的做法吗?还是我在寻找麻烦?
  5. 如果您需要更多信息,请告诉我,我很乐意提供。

1 个答案:

答案 0 :(得分:1)

  

这种方式有效

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

  

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

因为您从不取消注册接收器。它将继续 - 并且像筛子一样泄漏记忆 - 直到你的过程终止。

  

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

您需要拥有一个实例,以便稍后取消注册。注册和注销接收者应由服务部门完成;你的注册表接收器在它的构造函数是你的代码可怕的恕我直言的一部分。

  

查看文档,在onReceive()完成后,BroadcastReceiver应该自行清理

明确注册的BroadcastReceiver适用于单个广播。通过BroadcastReceiver注册的registerReceiver()有效期至unregisterReceiver()

  

为什么我需要NetworkMonitor.mDoingStuff?

你有更大的问题。

  

这是一种明智的做法吗

不是。

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

其次,请参阅上述失败的注销问题。

第三,如果你想拥有一件可以完成一件事的服务,那就消失,使用IntentService

所以,让我们一路回到顶部:

  

我有一个应用程序,我想在其中执行每个查询一些数据,比如15分钟。我有一个服务,我开始报警,但我也想确保在我开始寻找之前有网络连接。

正确的方法是:

  • AlarmManager个活动路线设为BroadcastReceiver。如果您使用_WAKEUP警报类型,这一点尤其重要,因为只有使用BroadcastReceiver PendingIntent时此类事件才可靠。

  • BroadcastReceiver中,在onReceive()中,如果您有网络连接,请向IntentService发送命令以执行工作(如果您正在使用_WAKEUP警报类型,请考虑我的WakefulIntentService,以便设备在您执行此操作时保持清醒状态。

  • 如果相反,似乎没有网络连接,请让BroadcastReceiver启用其他已设置清单的BroadcastReceiver来监听CONNECTIVITY_ACTION个事件 - 请使用PackageManagersetComponentEnabledSetting()

  • CONNECTIVITY_ACTION BroadcastReceiver,在onReceive()中,如果您确定现在有网络连接,请启动IntentService(与您相同)如果您已经有连接,我会从AlarmManager接收者那里做。)

  • IntentService / WakefulIntentService中,您可以在onHandleIntent()中完成工作。这已经有一个后台主题,当没有更多的工作要做时,它已经调用了stopSelf()

  • IntentService / WakefulIntentService的{​​{1}}中,通过onDestroy()和{{1}停用CONNECTIVITY_ACTION BroadcastReceiver },让你回到原来的状态。

这样:

  1. 你不会像在这里那样泄漏记忆。

  2. 您不必像在这里那样搞乱线程代码。

  3. 您不必担心您的流程是否会在闹钟和获取连接之间被踢出内存。

  4. 如果连接被阻止一段时间(例如,飞行模式),则不会像在此处那样注册N个接收器并设置N PackageManager。相反,只要在警报响起后将来发生连接变化,您将再次获得控制权。