web-dev-qa-db-fra.com

CONNECTIVITY_ACTION intention reçue deux fois lorsque le Wifi est connecté

Dans mon application, j'ai une BroadcastReceiver qui est lancée en tant que composant via une balise <receiver>, filtrant les intentions Android.net.conn.CONNECTIVITY_CHANGE.

Mon objectif est simplement de savoir quand une connexion Wi-Fi a été établie. Voici donc ce que je fais dans onReceive():

NetworkInfo networkInfo = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if(networkInfo.getType() == ConnectivityManager.TYPE_WIFI && networkInfo.isConnected()) {
    // Wifi is connected
}

Cela fonctionne bien, mais il me semble toujours avoir deux intentions identiques en une seconde environ lorsqu'une connexion Wifi est établie. J'ai essayé de regarder toutes les informations que je pouvais obtenir de l'intention, ConnectivityManager et WifiManager, mais je ne trouve rien qui distingue les deux intentions.

En regardant le journal, il y a au moins une autre BroadcastReceiver qui reçoit également les deux intentions identiques.

Il tourne sur un HTC Desire avec Android 2.2.

Avez-vous une idée de la raison pour laquelle je semble avoir une intention «dupliquée» lorsque le Wi-Fi est connecté ou quelle est la différence entre les deux?

48
Torsten Römer

Après beaucoup de recherches sur Google et de débogage, je pense que c'est la bonne façon de déterminer si le Wifi est connecté ou déconnecté.

La méthode onReceive() dans BroadcastReceiver:

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));
    }
}
}

Ensemble avec l'élément récepteur suivant dans 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>

Quelques explications:

  • Lorsque je ne considère que ConnectivityManager.CONNECTIVITY_ACTION, j'ai toujours deux intentions contenant des instances NetworkInfo identiques (toutes deux getType () == TYPE_WIFI et isConnected () == true) lors d'une connexion Wi-Fi - problème décrit dans cette question.

  • Lorsque vous utilisez uniquement WifiManager.NETWORK_STATE_CHANGED_ACTION, il n'y a pas d'intention diffusée lors de la déconnexion du Wifi, mais deux intentions contenant des instances NetworkInfo différentes, permettant de déterminer un événement lorsque le Wifi est connecté.

REMARQUE: j'ai reçu un seul rapport d'incident (NPE) dans lequel intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO) a renvoyé la valeur null. Ainsi, même si cela semble extrêmement rare, il peut être judicieux d’ajouter un chèque nul.

Cordialement, Torsten

58
Torsten Römer

Si vous écoutez sur WifiManager.NETWORK_STATE_CHANGED_ACTION, vous recevrez ceci deux fois car il y a 2 méthodes dans la NetworkInfo

  • isConnectedOrConnecting()
  • isConnected()

La première fois que isConnectedOrConnecting() renvoie true et isConnected()false
Deuxième fois isConnectedOrConnecting() et isConnected() return true

À votre santé

12
Dominic Bartl

Mise à jour du code de Torsten, de sorte que lorsque WIFI est déconnecté, seule la diffusion appropriée soit activée.

Utilisé NetworkInfo.getDetailedState () == DetailedState.DISCONNECTED pour la vérification.

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
Christopher Guray

Si vous avez enregistré l'activité en tant qu'écouteur d'intention, vous recevrez le même message deux fois. Plus précisément, vous devez choisir d’écouter au niveau du paquet (XML) ou au niveau de la programmation.

Si vous configurez une classe pour un récepteur de diffusion et que vous associez son écoute ET que vous associez un filtre d'intention à l'activité, le message sera répliqué deux fois.

J'espère que cela résout votre problème.

3
JoxTraex

J'ai résolu deux appels en utilisant SharedPref avec 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");

    }
}

Parce qu'il y a un petit délai entre 1.call et 2.call (environ 200 millisecondes). Donc, dans IF avec le temps, le deuxième appel s’arrête et le premier seulement continue.

2
Lalson

J'ai résolu si avec Dans

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);

dans 

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;
        }
    }
2
NickUnuchek

J'ai résolu ce problème en utilisant l'intention supplémentaire pour NetworkInfo . Dans l'exemple ci-dessous, l'événement onReceive est déclenché une seule fois si le wifi est connecté ou mobile.

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");
                }
            }
        }

et mon aide:

    public static boolean isNetworkConnected(Context ctx) {
    ConnectivityManager cm = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo ni = cm.getActiveNetworkInfo();

    return ni != null;
}
1
Scrounger

Si vous ne souhaitez le recevoir qu'une seule fois, vous pouvez simplement le contrôler via des variables. 

 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;
                    }
                }
            }
1
zhengcheng wang

Lorsque vous activez le WIFI,

  1. Lorsque les données MOBILE sont activées, deux émissions sont envoyées: Diffusion n ° 1: données MOBILE déconnectées et Diffusion n ° 2: WIFI connecté
  2. Lorsque les données MOBILE sont désactivées, une seule diffusion est envoyée: Diffusion n ° 1: WIFI connecté

Un comportement similaire peut être observé en éteignant le WIFI dans les deux conditions ci-dessus.

Pour distinguer les deux, veuillez suivre les numéros 2 et 3 ci-dessous:

        @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");
                }
            }
        }
0
FNordy Tuba68

Trouvé un cas particulier pour la connectivité réseau en disant qu'il n'y a pas Internet mais en réalité il y en a. getActiveNetworkInfo vous retournera toujours DISCONNECTED/BLOCKED dans un cas particulier, lorsque le réseau est modifié alors que le niveau de la batterie est faible et que l'application vient d'être commutée

Découvrez ce post

0
Phil

C’est le moyen approprié d’enregistrer les modifications de connectivité sur les API 21 et supérieures. Le code suivant peut être placé dans une activité de base. Ainsi, vous pouvez vous attendre à ce que tous les écrans de votre application (hérités de cette activité) obtiennent ces rappels.

Commencez par créer un rappel réseau qui surveillera les changements de connectivité.

@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)
    }
}

Ensuite, enregistrez et annulez l'enregistrement de ce rappel aux endroits appropriés.

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)
    }
}

Et annulez l'enregistrement si nécessaire.

override fun onPause() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Lollipop) {
        val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
        cm?.unregisterNetworkCallback(networkCallback)
    }
}

Notez qu'il y a un contrôle pour Build.VERSION_CODES.Lollipop. Cette fonctionnalité est uniquement disponible dans Lollipop et au-dessus. Assurez-vous de bien planifier comment gérer les changements d'état du réseau dans les appareils Pre-Lollipop si vous prenez en charge moins que l'API 21 dans votre application.

0
Mike

La façon dont je l'ai géré était tout simplement en sauvegardant l'état du réseau et en le comparant ensuite pour voir s'il y avait un changement .

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();
        }
    }

}

Si telle est votre approche, il est important de la réinitialiser dans la variable onResume de votre fragment ou activité, de sorte qu'elle contienne la valeur actuelle:

@Override
public void onResume() {
    super.onResume();
    MyApp.getInstance().resetNetworkPreviouslyConnected();
}

Je l'ai fait dans ma BaseFragment, un parent de tous les fragments de mon application.

0
Oleksiy

vérifiez networkType à partir de l'intention .__ et comparez 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
                     }
                 }
0
Wooyeol Jung