Jak wykryć, czy połączenie WIFI zostało ustanowione w systemie Android?

140

Muszę wykryć, czy mam łączność sieciową przez WIFI. Jakie rozgłoszenie jest wysyłane w celu ustalenia, czy nawiązano prawidłowe połączenie sieciowe. Muszę sprawdzić, czy istnieje prawidłowe połączenie sieciowe dla protokołu HTTP. Czego powinienem słuchać i jakie dodatkowe testy muszę wykonać, aby wiedzieć, że istnieje prawidłowe połączenie.

Androider
źródło
Tutaj znalazłem odpowiedź na to pytanie: stackoverflow.com/questions/4238921/…
Androider
1
Pozostaje jednak pytanie KIEDY sprawdzać te warunki?
Androider
1
Chciałbym otrzymać informację zwrotną na temat tego, czy wystąpią transmisje, które mogłyby zostać przechwycone przez odbiornik transmisji?
Androider
1
Jak mogę to zrobić na Androidzie O, ponieważ niejawne odbiorniki transmisji, takie jak android.net.wifi.STATE_CHANGE, nie będą już mogły być zarejestrowane w manifeście (patrz developer.android.com/guide/components/ ... ). Jeśli zarejestrujemy go w aktywności aplikacji (powiedzmy onCreate), to będzie musiał zostać wyrejestrowany w onStop () i nie będziemy już otrzymywać zdarzeń związanych z wifi
zafar142003

Odpowiedzi:

126

Możesz zarejestrować, BroadcastReceiveraby otrzymywać powiadomienia o ustanowieniu połączenia Wi-Fi (lub zmianie połączenia).

Zarejestruj BroadcastReceiver:

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
registerReceiver(broadcastReceiver, intentFilter);

A potem BroadcastReceiverzrób coś takiego:

@Override
public void onReceive(Context context, Intent intent) {
    final String action = intent.getAction();
    if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)) {
        if (intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false)) {
            //do stuff
        } else {
            // wifi connection was lost
        }
    }
}

Aby uzyskać więcej informacji, zapoznaj się z dokumentacją dotyczącą BroadcastReceiveriWifiManager

Oczywiście należy wcześniej sprawdzić, czy urządzenie jest już podłączone do WiFi.

EDYCJA: Dzięki zakazowi geoinżynierii, oto metoda sprawdzenia, czy urządzenie jest już podłączone:

private boolean isConnectedViaWifi() {
     ConnectivityManager connectivityManager = (ConnectivityManager) appObj.getSystemService(Context.CONNECTIVITY_SERVICE);
     NetworkInfo mWifi = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);     
     return mWifi.isConnected();
}
jpm
źródło
1
Dlaczego SUPPLICANT_CONECTION_CHANGE_ACTION? Myślałem, że to transmisja JUST CONNECTION_CHANGE o zmianie. Dlaczego SUPPLCANT ??? dzięki
Androider
2
co? Nie widzę akcji o nazwie zmiana_połączenia ...? Widzę tylko zmianę stanu Wi-Fi, ale ta czynność wskazuje tylko, czy Wi-Fi jest włączone, czy nie (lub włącza / wyłącza), a nie to, czy jest podłączone ... czy suplikant_łączenie_zmiana nie robi tego, czego potrzebujesz?
JPM
9
U mnie WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION nie działa w przypadku, gdy połączenie ze znaną stacją wifi zostało nawiązane / utracone. Ale WifiManager.NETWORK_STATE_CHANGED_ACTION działa.
Yar
1
„android.net.wifi.STATE_CHANGE” działał dla mnie. sprawdź moją odpowiedź poniżej
M. Usman Khan
1
„Oczywiście należy wcześniej sprawdzić, czy urządzenie jest już podłączone do WiFi”. - więc, aby uzyskać początkowy stan Wi-Fi, możesz użyć tej metody ...private boolean isConnectedViaWifi() { ConnectivityManager connectivityManager = (ConnectivityManager) appObj.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo mWifi = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); return mWifi.isConnected(); }
ban-geoengineering
106

Najlepsze, które zadziałały dla mnie:

AndroidManifest

<receiver android:name="com.AEDesign.communication.WifiReceiver" >
   <intent-filter android:priority="100">
      <action android:name="android.net.wifi.STATE_CHANGE" />
   </intent-filter>
</receiver>

Klasa BroadcastReceiver

public class WifiReceiver extends BroadcastReceiver {

   @Override
   public void onReceive(Context context, Intent intent) {

      NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
      if(info != null && info.isConnected()) {
        // Do your work. 

        // e.g. To check the Network Name or other info:
        WifiManager wifiManager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);
        WifiInfo wifiInfo = wifiManager.getConnectionInfo();
        String ssid = wifiInfo.getSSID();
      }
   }
}

Uprawnienia

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
M. Usman Khan
źródło
1
Myślę, że jest to najlepsza odpowiedź na zmiany stanu Wi-Fi. Dzięki
Mikel
4
Dla ułatwienia w przyszłości, ta zakodowana akcja to WifiManager.NETWORK_STATE_CHANGED_ACTION .
Anonsage
3
if (info! = null && info.isConnected ()) = brak spaghetti.
gswierczynski
1
czy musimy pisać jakikolwiek kod do wysyłania transmisji w głównym działaniu?
Nisari Balakrishnan
1
Jak mogę to zrobić na Androidzie O, ponieważ niejawne odbiorniki transmisji, takie jak android.net.wifi.STATE_CHANGE, nie będą już mogły być zarejestrowane w manifeście (patrz developer.android.com/guide/components/ ... ). Jeśli zarejestrujemy go w działaniu aplikacji (powiedzmy onCreate), to będzie musiał zostać wyrejestrowany w onStop () i nie będziemy już otrzymywać zdarzeń związanych z Wi-Fi.
zafar142003
18

U mnie WifiManager.NETWORK_STATE_CHANGED_ACTIONdziała tylko .

Zarejestruj odbiornik transmisji:

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
registerReceiver(broadcastReceiver, intentFilter);

i otrzymaj:

@Override
public void onReceive(Context context, Intent intent) {

    final String action = intent.getAction();

    if(action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)){
        NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
        boolean connected = info.isConnected();

        //call your method
    }      
}
Yar
źródło
1
U mnie również działał tylko WifiManager.NETWORK_STATE_CHANGED_ACTION, jakieś wyjaśnienia, dlaczego tylko to zadziała?
benchuk
11

Odpowiedzi udzielone przez użytkownika @JPM i @usman są naprawdę bardzo przydatne. Działa dobrze, ale w moim przypadku pojawia się onReceivewielokrotnie w moim przypadku 4 razy, więc mój kod jest wykonywany wiele razy.

Robię trochę modyfikacji i robię zgodnie z moimi wymaganiami, a teraz przychodzi tylko 1 raz

Oto klasa java dla Broadcast.

public class WifiReceiver extends BroadcastReceiver {

String TAG = getClass().getSimpleName();
private Context mContext;

@Override
public void onReceive(Context context, Intent intent) {

    mContext = context;


    if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {

        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = cm.getActiveNetworkInfo();

        if (networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_WIFI &&
                networkInfo.isConnected()) {
            // Wifi is connected
            WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
            WifiInfo wifiInfo = wifiManager.getConnectionInfo();
            String ssid = wifiInfo.getSSID();

            Log.e(TAG, " -- Wifi connected --- " + " SSID " + ssid );

        }
    }
    else if (intent.getAction().equalsIgnoreCase(WifiManager.WIFI_STATE_CHANGED_ACTION))
    {
        int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN);
        if (wifiState == WifiManager.WIFI_STATE_DISABLED)
        {
            Log.e(TAG, " ----- Wifi  Disconnected ----- ");
        }

    }
}
}

W AndroidManifest

<receiver android:name=".util.WifiReceiver" android:enabled="true">
        <intent-filter>
            <action android:name="android.net.wifi.WIFI_STATE_CHANGED" />
            <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
        </intent-filter>
    </receiver>


<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
Yog Guru
źródło
1
co to jest wifiState?
behelit
1
Chciałbym również wiedzieć, czym jest stan wifiState, jak został wygenerowany
Evan Parsons
1
@behelit teraz „wifiState” jest w edytowanej odpowiedzi.
Yog Guru
8

Państwo może nawiązać połączenie wifi jeśli dać użytkownikowi możliwość wyboru, aby zastąpić normalne zachowanie prosząc za każdym razem.

Wybieram trzy metody ...

public boolean isOnline() 
{
 ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
 NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
 return (networkInfo != null && networkInfo.isConnected());
}  

To szybkie sprawdzenie, czy jest połączenie internetowe Wi-Fi lub CellData. Tutaj możesz wybrać, jakie działanie chcesz podjąć. Należy również sprawdzić, czy jest w trybie samolotowym.

W osobnym wątku. Ustawiłem zmienną IpAddress na = "" I sonduj, dopóki nie będę miał prawidłowego adresu IP.

  WifiManager wifi;
  wifi = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
  WifiInfo wifiInfo = wifi.getConnectionInfo();
  int ipAddress = wifiInfo.getIpAddress();
  String ip = null;
  ip = String.format("%d.%d.%d.%d",
  (ipAddress & 0xff),
  (ipAddress >> 8 & 0xff),
  (ipAddress >> 16 & 0xff),
  (ipAddress >> 24 & 0xff));
  Log.e(" >>IP number Begin ",ip);

Kolejny fragment kodu ... Jeśli nie jest włączony, włącz go (za wcześniejszą zgodą użytkowników)

   if(wifi.isWifiEnabled()!=true)wifi.setWifiEnabled(true);  
user1445716
źródło
7

Aby wykryć stan połączenia WIFI, użyłem CONNECTIVITY_ACTION z klasy ConnectivityManager, więc:

    IntentFilter filter=new IntentFilter();
    filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
    registerReceiver(receiver, filter);

i z Twojego BroadCastReceiver:

    if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
        int networkType = intent.getIntExtra(
                android.net.ConnectivityManager.EXTRA_NETWORK_TYPE, -1);
        if (ConnectivityManager.TYPE_WIFI == networkType) {
            NetworkInfo networkInfo = (NetworkInfo) intent
                    .getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
            if (networkInfo != null) {
                if (networkInfo.isConnected()) {

                    // TODO: wifi is connected
                } else {
                    // TODO: wifi is not connected
                }
            }
        }

    }

ps: u mnie działa dobrze :)

amina
źródło
1
fyi, w Androidzie 6, gdy masz prawidłowe połączenie danych komórkowych, zmiany stanu Wi-Fi nie wywołują CONNECTIVITY_ACTION. Jedynym powodem, dla którego to zrobili, jest wpływ na stan łączności, teraz tak nie jest.
Bruce
5

Ten kod w ogóle nie wymaga pozwolenia. Ogranicza się tylko do zmian stanu łączności sieci Wi-Fi (żadna inna sieć nie jest brana pod uwagę). Odbiornik jest statycznie opublikowany w pliku AndroidManifest.xml i nie musi być eksportowane, jak to będzie wywoływana przez system protected broadcast, NETWORK_STATE_CHANGED_ACTIONprzy każdej zmianie stanu sieci łączności.

AndroidManifest:

<receiver
    android:name=".WifiReceiver"
    android:enabled="true"
    android:exported="false">

    <intent-filter>
        <!--protected-broadcast: Special broadcast that only the system can send-->
        <!--Corresponds to: android.net.wifi.WifiManager.NETWORK_STATE_CHANGED_ACTION-->
        <action android:name="android.net.wifi.STATE_CHANGE" />
    </intent-filter>

</receiver>

Klasa BroadcastReceiver:

public class WifiReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
/*
 Tested (I didn't test with the WPS "Wi-Fi Protected Setup" standard):
 In API15 (ICE_CREAM_SANDWICH) this method is called when the new Wi-Fi network state is:
 DISCONNECTED, OBTAINING_IPADDR, CONNECTED or SCANNING

 In API19 (KITKAT) this method is called when the new Wi-Fi network state is:
 DISCONNECTED (twice), OBTAINING_IPADDR, VERIFYING_POOR_LINK, CAPTIVE_PORTAL_CHECK
 or CONNECTED

 (Those states can be obtained as NetworkInfo.DetailedState objects by calling
 the NetworkInfo object method: "networkInfo.getDetailedState()")
*/
    /*
     * NetworkInfo object associated with the Wi-Fi network.
     * It won't be null when "android.net.wifi.STATE_CHANGE" action intent arrives.
     */
    NetworkInfo networkInfo = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);

    if (networkInfo != null && networkInfo.isConnected()) {
        // TODO: Place the work here, like retrieving the access point's SSID

        /*
         * WifiInfo object giving information about the access point we are connected to.
         * It shouldn't be null when the new Wi-Fi network state is CONNECTED, but it got
         * null sometimes when connecting to a "virtualized Wi-Fi router" in API15.
         */
        WifiInfo wifiInfo = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
        String ssid = wifiInfo.getSSID();
    }
}
}

Uprawnienia:

None
Codegateway
źródło
1
Działa na urządzeniach z <API 26. Jak zasugerował @Isham, akcja nie jest już obsługiwana w systemie Android O
tiagocarvalho92
3

Oto przykład mojego kodu, który uwzględnia preferencje użytkowników dotyczące zezwalania na komunikację tylko po podłączeniu do Wi-Fi.

Wywołuję ten kod od wewnątrz IntentServiceprzed próbą pobrania rzeczy.

Zauważ, że NetworkInfobędzie, nulljeśli nie będzie żadnego połączenia sieciowego.

private boolean canConnect()
{
    ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);

    boolean canConnect = false;
    boolean wifiOnly = SharedPreferencesUtils.wifiOnly();

    NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
    if(networkInfo != null)
    {
        if(networkInfo.isConnected())
        {
            if((networkInfo.getType() == ConnectivityManager.TYPE_WIFI) ||
               (networkInfo.getType() != ConnectivityManager.TYPE_WIFI && !wifiOnly))
            {
                canConnect = true;
            }
        }
    }

    return canConnect;
}
Justin Phillips
źródło
3

Android O usunął możliwość odbierania niejawnych transmisji dla zmiany stanu Wi-Fi. Jeśli więc Twoja aplikacja jest zamknięta, nie możesz ich otrzymać. Nowa WorkManagerma możliwość działania, gdy twoja aplikacja jest zamknięta, więc trochę z nią eksperymentowałem i wydaje się, że działa całkiem dobrze:

Dodaj to do swoich zależności:

implementation "android.arch.work:work-runtime:1.0.0-alpha08"

WifiConnectWorker.kt

class WifiConnectWorker : Worker() {

    override fun doWork(): Result {
        Log.i(TAG, "I think we connected to a wifi")
        return Result.SUCCESS
    }
}

MainActivity.kt

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_activity)

        val workManager = WorkManager.getInstance()

        // Add constraint to start the worker when connecting to WiFi
        val request = OneTimeWorkRequest.Builder(WifiConnectWorker::class.java)
            .setConstraints(Constraints.Builder()
                .setRequiredNetworkType(UNMETERED)
                .build())
            .build()

        // The worker should be started, even if your app is closed
        workManager.beginUniqueWork("watch_wifi", REPLACE, request).enqueue()
    }
}

Pamiętaj, że to był tylko szybki test jednorazowego powiadomienia. Jest więcej do zrobienia, aby zawsze otrzymywać powiadomienia o włączeniu i wyłączeniu Wi-Fi.

PS: Kiedy aplikacja wymusza zamknięcie , pracownik nie jest uruchamiany, wygląda na to, że WorkManageranuluje żądania.

Marius
źródło
Czy istnieje sposób na rozpoczęcie procesu roboczego, nawet jeśli moja aplikacja jest wymuszona? Menedżer pracy pracuje z .setRequiredNetworkType (UNMETERED) tylko wtedy, gdy aplikacja jest otwarta. Czy istnieje sposób na wyzwolenie pracownika, nawet gdy aplikacja jest zabita (wymuszenie stanu zamkniętego). Ponieważ niejawny odbiornik rozgłoszeniowy również jest nieco ograniczony. Jaka będzie najlepsza alternatywa?
Suresh
2

Użyłem tego kodu:

public class MainActivity extends Activity
    {
    .
    .
    .
    @Override
    protected void onCreate(Bundle savedInstanceState)
        {
        super.onCreate(savedInstanceState);
        .
        .
        .
        }

    @Override
    protected void onResume()
        {
        super.onResume();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
        registerReceiver(broadcastReceiver, intentFilter);  
        }

    @Override
    protected void onPause()
        {
        super.onPause();
        unregisterReceiver(broadcastReceiver);
        }

    private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver()
        {
        @Override
        public void onReceive(Context context, Intent intent)
            {
            final String action = intent.getAction();
            if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION))
                {
                if (intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false))
                    {
                    // wifi is enabled
                    }
                else
                    {
                    // wifi is disabled
                    }
                }
            }
        };
    }
Cuarcuiu
źródło
2

Mam dwie metody wykrywania połączenia WIFI odbierającego kontekst aplikacji:

1) moja stara metoda

public boolean isConnectedWifi1(Context context) {
    try {
        ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();           
        if (networkInfo != null) {
            NetworkInfo[] netInfo = connectivityManager.getAllNetworkInfo();
            for (NetworkInfo ni : netInfo) {
                if ((ni.getTypeName().equalsIgnoreCase("WIFI"))
                        && ni.isConnected()) {
                    return true;
                }                   
            }
        }
        return false;
    } catch (Exception e) {
        Log.e(TAG, e.getMessage());
    }
    return false;
}

2) moja nowa metoda (obecnie używam tej metody):

public boolean isConnectedWifi(Context context) {
         ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
         NetworkInfo networkInfo = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);     
         return networkInfo.isConnected();
}
Jorgesys
źródło
Jak mogę to zrobić na Androidzie O, ponieważ niejawne odbiorniki transmisji, takie jak android.net.wifi.STATE_CHANGE, nie będą już mogły być zarejestrowane w manifeście (patrz developer.android.com/guide/components/ ... ). Jeśli zarejestrujemy go w aktywności aplikacji (powiedzmy onCreate), to będzie musiał zostać wyrejestrowany w onStop (), a nie będziemy już otrzymywać zdarzeń związanych z wifi
zafar142003
2

1) Wypróbowałem podejście Broadcast Receiver, chociaż wiem, że CONNECTIVITY_ACTION / CONNECTIVITY_CHANGE jest przestarzałe w API 28 i nie jest zalecane. Jest również powiązany z użyciem jawnego rejestru, nasłuchuje tak długo, jak działa aplikacja.

2) Wypróbowałem także Firebase Dispatcher, który działa, ale nie poza tym, że aplikacja została zabita.

3) Zalecanym sposobem znalezienia jest WorkManager, aby zagwarantować wykonanie poza procesem zabitym i wewnętrznie za pomocą registerNetworkRequest ()

Największe dowody przemawiające za podejściem nr 3 przedstawia sam dokument dotyczący Androida . Szczególnie w przypadku aplikacji działających w tle.

Również tutaj

W Androidzie 7.0 usuwamy trzy powszechnie używane niejawne transmisje - CONNECTIVITY_ACTION, ACTION_NEW_PICTURE i ACTION_NEW_VIDEO - ponieważ mogą one obudzić procesy wielu aplikacji jednocześnie i obciążać pamięć i baterię. Jeśli Twoja aplikacja je otrzymuje, skorzystaj z systemu Android 7.0, aby zamiast tego przeprowadzić migrację do JobScheduler i powiązanych interfejsów API.

Jak dotąd działa to dobrze dla nas, używając okresowego żądania WorkManager.

Aktualizacja: Skończyło się na tym, że napisałem o tym średni post z 2 serii .

Wahib Ul Haq
źródło