0

我有一个应用程序,我想每隔 15 分钟在其中查找一些数据。我有一项以警报开始的服务,但我还想在开始查找之前确保有网络连接。

为此,我认为我应该使用 aBroadcastReceiver来观察网络状态的变化。我已经包装了一个广播接收器来帮助解决这个问题:

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?我猜如果我能弄清楚为什么在完成NetworkMonitor后没有自行清理,onReceive()那么我可能不再需要它了?
  4. 这是一种明智的做法还是我在自找麻烦?

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

4

1 回答 1

1

这有点工作

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

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

因为您永远不会取消注册接收器。它会继续运行——并且像筛子一样泄漏内存——直到你的进程被终止。

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

您需要有一个实例,以便稍后取消注册。接收者的注册和注销应由服务完成;您的 register-the-receiver-in-its-constructor 是使您的代码可怕的恕我直言的一部分。

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

清单注册BroadcastReceiver的单次广播。一个BroadcastReceiver注册的 viaregisterReceiver()一直存在到unregisterReceiver().

为什么需要 NetworkMonitor.mDoingStuff?

你有更大的问题。

这是一种明智的做法吗

并不真地。

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

其次,请参阅上述未能取消注册的问题。

第三,如果你想要一个服务做一件事,然后离开,使用IntentService.

所以,让我们一直滚动到顶部:

我有一个应用程序,我想每隔 15 分钟在其中查找一些数据。我有一项以警报开始的服务,但我还想在开始查找之前确保有网络连接。

正确的做法是:

  • 将您的AlarmManager活动路线设置为BroadcastReceiver. 如果您使用_WAKEUP警报类型,这一点尤其重要,因为此类事件仅在您使用BroadcastReceiver PendingIntent.

  • 在那BroadcastReceiver,在onReceive(),如果你有一个网络连接,发送一个命令IntentService来做这项工作(如果你使用的是_WAKEUP警报类型,请考虑我的WakefulIntentService,所以当你这样做时设备保持清醒)。

  • 相反,如果似乎没有网络连接,请BroadcastReceiver启用另一个清单注册BroadcastReceiver设置以监视CONNECTIVITY_ACTION事件 - 使用PackageManagerand setComponentEnabledSetting()

  • CONNECTIVITY_ACTION BroadcastReceiver, in 中onReceive(),如果您确定您现在有网络连接,请启动您的(与如果您已经有连接,IntentService您将从接收器执行的操作相同)。AlarmManager

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

  • IntentService/ WakefulIntentService、 in 中onDestroy(),禁用 CONNECTIVITY_ACTION BroadcastReceiverviaPackageManagersetComponentEnabledSetting(),让您回到原来的状态。

这边走:

  1. 你不会泄漏内存,就像你在这里做的那样。

  2. 您不必像在此处那样处理线程代码。

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

  4. 如果连接被阻塞了一段时间(例如,飞行模式),您不需要注册 N 个接收器并设置 N AsyncTasks,就像您在此处所做的那样。取而代之的是,当警报响起后,将来发生连接变化时,您将再次获得控制权。

于 2013-04-29T22:25:36.690 回答