web-dev-qa-db-ja.com

Wifi接続時にCONNECTIVITY_ACTIONインテントを2回受信しました

私のアプリには、_<receiver>_タグを通じてコン​​ポーネントとして起動され、_Android.net.conn.CONNECTIVITY_CHANGE_インテントをフィルタリングするBroadcastReceiverがあります。

私の目標は、Wi-Fi接続がいつ確立されたかを知ることです。したがって、onReceive()で行うことは次のとおりです。

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

正常に動作しますが、Wifi接続が確立されると、約1秒以内に2つの同一のインテントを常に取得するようです。 ConnectivityManagerWifiManagerというインテントから得られる情報を調べようとしましたが、2つのインテントを区別するものは見つかりません。

ログを見ると、2つの同一のインテントも受け取る少なくとも1つのBroadcastReceiverがあります。

Android 2.2のHTC Desireで実行されています

Wifiが接続したときに「重複した」意図を取得しているように見える理由や、2つの違いは何ですか?

49
Torsten Römer

注:最近の最新の回答については、下記の 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の両方)を含む2つのインテントを取得します-この質問で説明されている問題。

  • _WifiManager.NETWORK_STATE_CHANGED_ACTION_のみを使用する場合、Wifiの切断時にブロードキャストされるインテントはありませんが、異なるNetworkInfoインスタンスを含む2つのインテントがあり、Wifiの接続時に1つのイベントを判別できます。

注:intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO)がnullを返した単一のクラッシュレポート(NPE)を受け取りました。そのため、非常にまれにしか発生しないと思われる場合でも、nullチェックを追加することをお勧めします。

乾杯、トルステン

62
Torsten Römer

_WifiManager.NETWORK_STATE_CHANGED_ACTION_をリッスンしている場合、NetworkInfoには2つのメソッドがあるため、これを2回受け取ります。

  • isConnectedOrConnecting()
  • isConnected()

初めてisConnectedOrConnecting()trueisConnected()falseを返します
2回目isConnectedOrConnecting()およびisConnected() return true

乾杯

12
Dominic Bartl

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

これは、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デバイスでネットワークステータスの変更を処理する方法を計画してください。

3
Mike

アクティビティをインテントリスナーとして登録した場合、同じメッセージを2回受信します。具体的には、パッケージレベル(XML)またはプログラムレベルのどちらでリッスンするかを選択する必要があります。

ブロードキャストレシーバーのクラスを設定してリッスンをアタッチし、アクティビティにインテントフィルターをアタッチすると、メッセージは2回複製されます。

これで問題が解決することを願っています。

3
JoxTraex

SharedPrefとTimeを使用して2回の呼び出しを解決しました。

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ミリ秒)。そのため、IFが時間とともに2番目の呼び出しが停止し、最初の呼び出しが続行されます。

2
Lalson

で解決したら

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

NetworkInfoの追加のインテントを使用して、この問題を解決しました。次の例では、wifiが接続されているかモバイルの場合、onReceiveイベントが1回だけ発生します。

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;
}
1
Scrounger

一度だけ受信したい場合は、変数を介して単純に制御できます。

 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

WIFIをオンにすると、

  1. モバイルデータがオンの場合、2つのブロードキャストが送信されます。ブロードキャスト#1:モバイルデータが切断され、ブロードキャスト#2:WIFIが接続されました
  2. モバイルデータがオフの場合、1つのブロードキャストのみが送信されます:ブロードキャスト#1:WIFI接続

上記の2つの条件下でWIFIをオフにすると、同様の動作が見られます。

2つを区別するには、以下の#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");
                }
            }
        }
0
FNordy Tuba68

インターネットはないが、実際にはあるというネットワーク接続の特殊なケースを見つけました。バッテリーレベルが低くアプリがあったときにネットワークが変更された特定の場合に、getActiveNetworkInfoは常にあなたを返しますDISCONNECTED/BLOCKEDちょうど切り替えた

この投稿 をご覧ください

0
Phil

私がそれを処理した方法は、単にネットワークの状態を保存し、それを比較してchangeがあるかどうかを確認することでした。

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で行いました。

0
Oleksiy

インテントから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
                     }
                 }
0
Wooyeol Jung