连接 Wifi 时收到两次 CONNECTIVITY_ACTION 意图
Posted
技术标签:
【中文标题】连接 Wifi 时收到两次 CONNECTIVITY_ACTION 意图【英文标题】:CONNECTIVITY_ACTION intent received twice when Wifi connected 【发布时间】:2011-07-13 15:49:31 【问题描述】:在我的应用程序中,我有一个BroadcastReceiver
,它通过<receiver>
标签作为组件启动,过滤android.net.conn.CONNECTIVITY_CHANGE
意图。
我的目标只是知道何时建立了 Wifi 连接,所以我在 onReceive()
中所做的是:
NetworkInfo networkInfo = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if(networkInfo.getType() == ConnectivityManager.TYPE_WIFI && networkInfo.isConnected())
// Wifi is connected
它工作正常,但是当建立 Wifi 连接时,我似乎总是在大约一秒钟内得到两个相同的意图。我试图查看我可以从意图中获得的任何信息,ConnectivityManager
和 WifiManager
,但我找不到任何可以区分这两种意图的信息。
查看日志,至少还有另外一个BroadcastReceiver
也接收到两个相同的意图。
它在装有 Android 2.2 的 HTC Desire 上运行
知道为什么当 Wifi 连接时我似乎得到了“重复”的意图,或者两者之间的区别可能是什么?
【问题讨论】:
youtube.com/playlist?list=PLrnPJCHvNZuBqr_0AS9BPXgU6gvNeai5S 对最近实现广播接收器的方法有很好的理解 【参考方案1】:注意:有关最新的最新答案,请参阅下面的this one!
经过大量的谷歌搜索和调试,我相信这是确定 Wifi 是否连接或断开的正确方法。
BroadcastReceiver 中的onReceive()
方法:
public void onReceive(final Context context, final Intent intent)
if(intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION))
NetworkInfo networkInfo =
intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
if(networkInfo.isConnected())
// Wifi is connected
Log.d("Inetify", "Wifi is connected: " + String.valueOf(networkInfo));
else if(intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION))
NetworkInfo networkInfo =
intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if(networkInfo.getType() == ConnectivityManager.TYPE_WIFI &&
! networkInfo.isConnected())
// Wifi is disconnected
Log.d("Inetify", "Wifi is disconnected: " + String.valueOf(networkInfo));
与 AndroidManifest.xml 中的以下接收器元素一起
<receiver android:name="ConnectivityActionReceiver"
android:enabled="true" android:label="ConnectivityActionReceiver">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
<action android:name="android.net.wifi.STATE_CHANGE"/>
</intent-filter>
</receiver>
一些解释:
仅考虑 ConnectivityManager.CONNECTIVITY_ACTION
时,当 Wifi 连接时,我总是得到两个包含相同 NetworkInfo 实例(getType() == TYPE_WIFI 和 isConnected() == true)的意图 - 这个问题中描述的问题。
仅使用WifiManager.NETWORK_STATE_CHANGED_ACTION
时,Wifi断开时不会广播intent,而是两个intent包含不同的NetworkInfo实例,允许在Wifi连接时确定一个事件。
注意:我收到了一份崩溃报告 (NPE),其中 intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO)
返回 null。因此,即使这种情况看起来极为罕见,添加空检查也是一个好主意。
干杯, 托斯滕
【讨论】:
我想在这里提一下,至少在某些情况下,WifiManager.NETWORK_STATE_CHANGED_ACTION
意图在数据连接完全建立之前的一小段时间内被广播(DHCP 配置未完成),并且在那一刻, ConnectivityManager.getActiveNetworkInfo()
可能仍会返回类型为 TYPE_WIFI
的 NetworkInfo
。
我可以亲自确认 Torsten 所写的所有内容。感谢您的解决方案。我发现没有更清洁和更简单的解决方案令人沮丧。最令人惊讶的是,当 wifi 断开连接时,这种行为不会发生。我并不是说它一定是一个错误,但它可以得到更好的记录。
从 Intent 中获取 NetworkInfo 现在已被贬低。然而,尚不清楚为什么“他们”不希望这种情况保持稳定。是否有人发现从意图中获取 Networkinfo 不稳定?
@BrillPappin 从 ConnectivityManager 获取它已被弃用,但这个答案的讨论集中在从 WifiManager 获取它仍然很好。但是,和其他人一样,我仍然会收到奇怪的通知序列。
intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO) 已弃用。【参考方案2】:
如果您正在收听WifiManager.NETWORK_STATE_CHANGED_ACTION
,您将收到两次,因为NetworkInfo
中有两种方法
isConnectedOrConnecting()
isConnected()
第一次isConnectedOrConnecting()
返回true
和isConnected()
false
第二次isConnectedOrConnecting()
和isConnected()
返回true
干杯
【讨论】:
对我来说这两种方法都为这两个事件返回 true :) 这个答案不正确,isConnected()
可以返回true
两次。【参考方案3】:
这是在 API 21 及更高版本上注册连接更改的正确方法。以下代码可以放置在基础活动中,这样您就可以期望应用程序中的每个屏幕(从该活动继承)都能获得这些回调。
首先,创建一个网络回调来监控连接变化。
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private val networkCallback: ConnectivityManager.NetworkCallback = object : ConnectivityManager.NetworkCallback()
// Implement the callback methods that are relevant to the actions you want to take.
// I have implemented onAvailable for connecting and onLost for disconnecting.
override fun onAvailable(network: Network?)
super.onAvailable(network)
override fun onLost(network: Network?)
super.onLost(network)
然后,在相关位置注册和注销这个回调。
override fun onResume()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
cm?.registerNetworkCallback(NetworkRequest.Builder().build(), networkCallback)
并在适当的时候取消注册。
override fun onPause()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
cm?.unregisterNetworkCallback(networkCallback)
请注意,检查了Build.VERSION_CODES.LOLLIPOP
。此功能仅在 Lollipop 及更高版本中可用。如果您的应用支持低于 API 21,请务必制定如何处理 Pre-Lollipop 设备中的网络状态变化的计划。
【讨论】:
谢谢@mike 这是我要找的:)【参考方案4】:更新了 Torsten 的代码,使得当 WIFI 断开连接时,仅对单个适当的广播进行操作。
使用 NetworkInfo.getDetailedState() ==DetailedState.DISCONNECTED 进行检查。
public void onReceive(final Context context, final Intent intent)
if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION))
NetworkInfo networkInfo = intent
.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
if (networkInfo.isConnected())
// Wifi is connected
Log.d("Inetify","Wifi is connected: " + String.valueOf(networkInfo));
else if (intent.getAction().equals(
ConnectivityManager.CONNECTIVITY_ACTION))
NetworkInfo networkInfo = intent
.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if (networkInfo.getDetailedState() == DetailedState.DISCONNECTED)
// Wifi is disconnected
Log.d("Inetify","Wifi is disconnected: "+String.valueOf(networkInfo));
【讨论】:
想知道变化的人是if(networkInfo.getType() == ConnectivityManager.TYPE_WIFI && ! networkInfo.isConnected())
to if (networkInfo.getDetailedState() == DetailedState.DISCONNECTED)
Wifi 和蜂窝数据怎么样?我相信两者都会导致 BroadcastReceiver 触发导致更多调用。【参考方案5】:
如果您将活动注册为意图侦听器,那么您将收到两次相同的消息。具体来说,您需要选择是要在包级别 (XML) 还是程序级别进行侦听。
如果您为广播接收器设置一个类并将监听附加到它,并且您将一个意图过滤器附加到 Activity,那么该消息将被复制两次。
希望这能解决您的问题。
【讨论】:
这是正确的,您可以在类或清单中创建广播侦听器,但不能同时创建。 终于可以评论这个了……你的回答当然是正确的,但是我只在清单中注册了BroadcastReceiver。【参考方案6】:我通过使用带时间的 SharedPref 解决了两次调用。
private static final Long SYNCTIME = 800L;
private static final String LASTTIMESYNC = "DATE";
SharedPreferences sharedPreferences;
private static final String TAG = "Connection";
@Override
public void onReceive(Context context, Intent intent)
Log.d(TAG, "Network connectivity change");
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
final ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
final NetworkInfo ni = connectivityManager.getActiveNetworkInfo();
if (ni != null && ni.isConnected())
if(System.currentTimeMillis()-sharedPreferences.getLong(LASTTIMESYNC, 0)>=SYNCTIME)
sharedPreferences.edit().putLong(LASTTIMESYNC, System.currentTimeMillis()).commit();
// Your code Here.
else if (intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, Boolean.FALSE))
Log.d(TAG, "There's no network connectivity");
因为 1.call 和 2.call 之间有小的延迟(大约 200 毫秒)。 因此,如果有时间,第二次呼叫将停止,而第一次呼叫将继续。
【讨论】:
虽然这个解决方案听起来很糟糕,但它对我有用。我已经尝试过信号量和同步块,但没有一个对我有用。【参考方案7】:我解决了如果 在
onCreate()
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
intentFilter.addAction("android.net.wifi.WIFI_STATE_CHANGED");
intentFilter.addAction("android.net.wifi.STATE_CHANGE");
ctx.registerReceiver(outgoingReceiver, intentFilter);
在
BroadcastReceiver
public void onReceive(Context context, Intent intent)
if(intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION))
NetworkInfo networkInfo =
intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if(networkInfo.getType() == ConnectivityManager.TYPE_WIFI &&
networkInfo.isConnected())
// Wifi is connected
Log.d("Inetify", "Wifi is connected: " + String.valueOf(networkInfo));
Log.e("intent action", intent.getAction());
if (isNetworkConnected(context))
Log.e("WiFi", "is Connected. Saving...");
try
saveFilesToServer("/" + ctx.getString(R.string.app_name).replaceAll(" ", "_") + "/Temp.txt");
catch (IOException e)
e.printStackTrace();
boolean isNetworkConnected(Context context)
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = cm.getActiveNetworkInfo();
if (ni != null)
Log.e("NetworkInfo", "!=null");
try
//For 3G check
boolean is3g = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE)
.isConnectedOrConnecting();
//For WiFi Check
boolean isWifi = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI)
.isConnected();
Log.e("isWifi", "isWifi="+isWifi);
Log.e("is3g", "is3g="+is3g);
if (!isWifi)
return false;
else
return true;
catch (Exception er)
return false;
else
Log.e("NetworkInfo", "==null");
return false;
【讨论】:
【参考方案8】:我通过对 NetworkInfo 使用 Intent extra 解决了这个问题。 在下面的示例中,onReceive 事件仅在 wifi 连接或移动时触发一次。
if (intent.getAction().equalsIgnoreCase(ConnectivityManager.CONNECTIVITY_ACTION))
NetworkInfo networkInfo = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
boolean screenIsOn = false;
// Prüfen ob Screen on ist
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
screenIsOn = pm.isInteractive();
else
screenIsOn = pm.isScreenOn();
if (Helper.isNetworkConnected(context))
if (networkInfo.isConnected() && networkInfo.isAvailable())
Log.v(logTAG + "onReceive", "connected");
if (networkInfo.getType() == ConnectivityManager.TYPE_MOBILE)
Log.v(logTAG + "onReceive", "mobile connected");
else if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI)
Log.v(logTAG + "onReceive", "wifi connected");
和我的助手:
public static boolean isNetworkConnected(Context ctx)
ConnectivityManager cm = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = cm.getActiveNetworkInfo();
return ni != null;
【讨论】:
【参考方案9】:如果你只想接收一次,你可以简单地通过变量控制它。
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction()))
NetworkInfo activeNetwork = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if (activeNetwork != null) // connected to the internet
if (activeNetwork.isConnected() && !isUpdated)
if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI)
// connected to wifi
else if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE)
// connected to the mobile provider's data plan
isUpdated = true;
else
isUpdated = false;
【讨论】:
【参考方案10】:打开 WIFI 时,
-
开启 MOBILE 数据时,会发送两个广播:
广播#1:移动数据断开,并且
广播 #2:WIFI 已连接
在 MOBILE 数据关闭的情况下,仅发送一个广播:
广播 #1:WIFI 已连接
在上述两种情况下关闭 WIFI 时可以观察到类似的行为。
要区分两者,请遵循以下#2和#3:
@Override
public void onReceive(Context context, Intent intent)
Log.d(TAG, "*** Action: " + intent.getParcelableExtra("networkInfo"));
NetworkInfo netInfo = intent.getParcelableExtra("networkInfo");
if(intent.getAction().equalsIgnoreCase("android.net.conn.CONNECTIVITY_CHANGE"))
ConnectivityManager connectivityManager
= (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo();
if (activeNetInfo != null)
if (netInfo.getType() == ConnectivityManager.TYPE_WIFI)
if (netInfo.getState().name().contains("DISCONNECTED")
&& activeNetInfo.getType() == ConnectivityManager.TYPE_MOBILE)
Log.d(TAG, "WIFI disconnect created this broadcast. MOBILE data ON."); // #1
else if (netInfo.getState().name().contains("CONNECTED")
&& activeNetInfo.getType() == ConnectivityManager.TYPE_WIFI)
Log.d(TAG, "WIFI connect created this broadcast."); // #2
else if (netInfo.getType() == ConnectivityManager.TYPE_MOBILE)
if (netInfo.getState().name().contains("DISCONNECTED")
&& activeNetInfo.getType() == ConnectivityManager.TYPE_WIFI)
Log.d(TAG, "MOBILE data disconnect created this broadcast. WIFI ON."); // #3
else if (netInfo.getState().name().contains("CONNECTED")
&& activeNetInfo.getType() == ConnectivityManager.TYPE_MOBILE)
Log.d(TAG, "MOBILE data connect created this broadcast."); // #4
else
Log.d(TAG, "No network available");
【讨论】:
【参考方案11】:我的处理方式是简单地保存网络状态,然后比较它以查看是否有变化。
public class ConnectivityChangedReceiver extends BroadcastReceiver
@Override
public void onReceive(Context context, Intent intent)
boolean previouslyConnected = MyApp.getInstance().isNetworkPreviouslyConnected();
boolean currentlyConnected = MyApp.getInstance().isNetworkConnected();
if (previouslyConnected != currentlyConnected)
// do something and reset
MyApp.getInstance().resetNetworkPreviouslyConnected();
如果这是您采用的方法,请务必在片段或活动的 onResume
中重置它,以便它保持当前值:
@Override
public void onResume()
super.onResume();
MyApp.getInstance().resetNetworkPreviouslyConnected();
我在我的BaseFragment
中做到了这一点,这是我应用中所有片段的父级。
【讨论】:
【参考方案12】:从意图中检查网络类型 并比较 activeNetworkInfo.getType()
Bundle bundle = intent.getExtras();
ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = manager.getActiveNetworkInfo();
if(ni != null && ni.getState() == NetworkInfo.State.CONNECTED)
if(bundle.getInt("networkType") == ni.getType())
// active network intent
【讨论】:
【参考方案13】:发现了一个网络连接的特殊情况,说没有互联网,但实际上有。事实证明,getActiveNetworkInfo
总是会在特定情况下返回DISCONNECTED/BLOCKED,即在电池电量低且应用刚刚切换时更改网络
查看this post
【讨论】:
【参考方案14】:只听动作“android.net.conn.CONNECTIVITY_CHANGE”。每当建立或破坏连接时都会广播它。
“android.net.wifi.STATE_CHANGE”将在连接建立时广播。所以你得到了两个触发器。
享受吧!
【讨论】:
问题是它为每个状态变化发送两个事件。以上是关于连接 Wifi 时收到两次 CONNECTIVITY_ACTION 意图的主要内容,如果未能解决你的问题,请参考以下文章
打开/关闭wifi或GPS时,我的BroadcastReceiver被调用两次?