[英]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()。
这种方法有效,但是:
NetworkMonitor.doStuff()
调用比我期望的要多得多。 看来,即使服务已停止(在异步任务正确完成之后), NetworkMonitor
实例仍在接收有关网络状态更改的意图。 为什么是这样? NetworkMonitor()
实例存储在服务中,还是可以只拥有一个这样的匿名实例? 查看文档, BroadcastReceiver
应该在onReceive()
完成后清除自身。 NetworkMonitor.mDoingStuff
? 我猜想,如果我能弄清楚为什么在onReceive()
完成后NetworkMonitor
无法清除自身,那么我可能不再需要它了吗? 如果您需要更多信息,请告诉我,我们将很乐意提供。
这种作品
这是可怕的代码,恕我直言。
看来,即使服务已停止(在异步任务正确完成之后),NetworkMonitor实例仍在接收有关网络状态更改的意图。 为什么是这样?
因为您从不注销接收器。 它将继续进行-并像筛子一样泄漏内存-直到您的进程终止。
我是否需要一个变量来将NetworkMonitor()实例存储在服务中,还是可以只拥有一个这样的匿名实例?
您需要有一个实例,以便以后注销。 接收方的注册和注销应由服务部门完成; 您的构造器中的接收器是使您的代码可怕的恕我直言的一部分。
查看文档,广播接收器应在onReceive()完成后清除自身
清单注册的BroadcastReceiver
用于单个广播。 通过registerReceiver()
注册的BroadcastReceiver
一直存在,直到unregisterReceiver()
为止。
为什么需要NetworkMonitor.mDoingStuff?
你有更大的问题。
这是明智的做法吗
并不是的。
首先,由于您无法多次execute()
AsyncTask
实例,因此您将在第二次广播时崩溃。
其次,请参阅上述未能注销的问题。
第三,如果您想要一种服务做一件事情,然后走了,请使用IntentService
。
因此,让我们一直回到顶部:
我有一个应用程序,我想在其中每15分钟执行一次查找一些数据。 我有一项从警报开始的服务,但我也想确保在开始查找之前已建立网络连接。
正确的方法是:
使您的AlarmManager
事件路由到BroadcastReceiver
。 如果您使用的是_WAKEUP
警报类型,这尤其重要,因为只有使用BroadcastReceiver
PendingIntent
此类事件才是可靠的。
在该BroadcastReceiver
的onReceive()
,如果您具有网络连接,请向IntentService
发送命令以完成工作(并且,如果使用的是_WAKEUP
警报类型,请考虑我的WakefulIntentService
,以便设备在您处于睡眠状态时保持清醒状态)重新这样做)。
相反,如果似乎没有网络连接,请让您的BroadcastReceiver
启用另一个清单注册的BroadcastReceiver
来监视CONNECTIVITY_ACTION
事件-为此,请使用PackageManager
和setComponentEnabledSetting()
。
在CONNECTIVITY_ACTION
BroadcastReceiver
的onReceive()
,如果确定现在已建立网络连接,请启动IntentService
(与已经建立连接时从AlarmManager
接收器执行的操作相同)。
在IntentService
/ WakefulIntentService
,在onHandleIntent()
完成您的工作。 它已经有一个后台线程,并且在没有更多工作要做时已经调用了stopSelf()
。
在IntentService
/ WakefulIntentService
的onDestroy()
,通过PackageManager
和setComponentEnabledSetting()
禁用CONNECTIVITY_ACTION
BroadcastReceiver
,使您返回到原始状态。
这条路:
您不会像在这里那样泄漏内存。
您不必像在这里所做的那样弄乱线程代码。
您不必担心在警报和获得连接之间您的进程是否被踢出内存。
如果连接被阻止了一段时间(例如,飞行模式),则不会像在此处那样注册N个接收器并设置N个AsyncTasks
。 取而代之的是,在您的警报响起以后,只要将来发生连接更改,您都将再次获得控制。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.