连接Wifi时,CONNECTIVITY_ACTION意图收到两次

时间:2011-03-11 17:11:16

标签: android android-wifi android-broadcastreceiver

在我的应用中,我有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连接时,我似乎总是在大约一秒钟内得到两个相同的意图。我试图查看我可以从意图中获得的任何信息,ConnectivityManagerWifiManager,但我找不到区分这两种意图的任何信息。

查看日志,至少还有一个BroadcastReceiver也会收到两个相同的意图。

它在Android 2.2的HTC Desire上运行

任何想法为什么我在Wifi连接时似乎得到“重复”的意图或者两者之间可能存在什么差异?

12 个答案:

答案 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()返回trueisConnected() 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 ,当电池电量低且应用刚刚切换时更改网络

查看this post

答案 11 :(得分:-2)

只听动作“android.net.conn.CONNECTIVITY_CHANGE”。无论何时建立或销毁连接,它都会被广播。

连接建立后,将播放“android.net.wifi.STATE_CHANGE”广播。所以你得到两个触发器。

享受!