在我的应用中,我有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连接时似乎得到“重复”的意图或者两者之间可能存在什么差异?
答案 0 :(得分:61)
注意:有关最近的最新答案,请参阅下面的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
时,我总是得到两个包含相同NetworkInfo实例的意图(getType()== TYPE_WIFI和isConnected()== true)当Wifi连接时 - 此问题中描述的问题
仅使用WifiManager.NETWORK_STATE_CHANGED_ACTION
时,Wifi断开连接时没有广播意图,但两个意图包含不同的NetworkInfo实例,允许在连接Wifi时确定一个事件。
注意:我收到一个崩溃报告(NPE),其中intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO)
返回null。因此,即使它似乎极少发生,也可以添加一个空检查。
干杯, 托
答案 1 :(得分:12)
如果你正在聆听WifiManager.NETWORK_STATE_CHANGED_ACTION
,你会收到两次,因为NetworkInfo
isConnectedOrConnecting()
isConnected()
第一次isConnectedOrConnecting()
返回true
和isConnected()
false
第二次isConnectedOrConnecting()
和isConnected()
返回true
干杯
答案 2 :(得分: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));
}
}
}
答案 3 :(得分:3)
如果您将活动注册为目标侦听器,那么您将收到两次相同的消息。具体来说,您需要选择是否要在程序包级别(XML)或程序级别上进行侦听。
如果您为广播接收者设置了一个类并附加了侦听它并且您将一个intent过滤器附加到该活动,那么该消息将被复制两次。
我希望这能解决你的问题。
答案 4 :(得分:2)
我使用SharedPref with Time解决了两次调用。
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 milisec)。 因此,在有时间的IF中,第二次呼叫将停止,并且第一次呼叫将继续。
答案 5 :(得分:2)
这是在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数量少于API 21,则一定要制定一个计划,以处理Lollipop前设备中的网络状态更改。
答案 6 :(得分:1)
我通过使用NetworkInfo的intent额外解决了这个问题。 在下面的示例中,如果连接了wifi或移动了wifi,则仅触发onReceive事件一次。
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;
}
答案 7 :(得分:1)
如果您只想接收一次,您可以通过变量简单地控制它。
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;
}
}
}
答案 8 :(得分:0)
我处理它的方式只是保存网络状态,然后比较它以查看是否有更改。
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
中做到的,这是我应用中所有片段的父级。
答案 9 :(得分:0)
从意图中检查networkType 并比较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
}
}
答案 10 :(得分:0)
找到一个特殊的网络连接案例,说没有互联网但实际上有。事实证明InnerModelToPost
会在特定情况下返回 DISCONNECTED / BLOCKED ,当电池电量低且应用刚刚切换时更改网络
答案 11 :(得分:-2)
只听动作“android.net.conn.CONNECTIVITY_CHANGE”。无论何时建立或销毁连接,它都会被广播。
连接建立后,将播放“android.net.wifi.STATE_CHANGE”广播。所以你得到两个触发器。享受!