如何在安卓设备上检测无线网络连接是否建立?

147

我需要检测我是否通过WIFI连接到网络。用于建立有效的网络连接的广播是什么?我需要验证HTTP的有效网络连接是否存在。我应该监听什么,还需要进行哪些额外的测试以确定存在有效的连接。


这个问题的部分已经在这里得到了回答:https://dev59.com/j2855IYBdhLWcg3woVw_ - Androider
1
但是仍然存在一个问题,就是何时检查这些条件? - Androider
1
我想了解一下是否有可能接收到广播接收器可以捕获到的广播? - Androider
1
在Android O上,由于隐式广播接收器(如android.net.wifi.STATE_CHANGE)将不再允许在清单中注册(请参见https://developer.android.com/guide/components/broadcast-exceptions.html),因此我该如何处理呢?如果我们在应用程序活动中注册它(比如onCreate),那么它将必须在onStop()中注销,否则我们将不再接收与wifi相关的事件。 - zafar142003
16个回答

130

您可以注册一个 BroadcastReceiver,以便在 WiFi 连接建立时(或连接更改时)收到通知。

注册 BroadcastReceiver

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

然后在你的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)) {
            //do stuff
        } else {
            // wifi connection was lost
        }
    }
}

更多信息请参见BroadcastReceiverWifiManager文档。

当然,在此之前,您应该检查设备是否已连接到WiFi。

编辑:感谢ban-geoengineering,以下是一种检查设备是否已连接的方法:

private boolean isConnectedViaWifi() {
     ConnectivityManager connectivityManager = (ConnectivityManager) appObj.getSystemService(Context.CONNECTIVITY_SERVICE);
     NetworkInfo mWifi = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);     
     return mWifi.isConnected();
}

1
为什么是SUPPLICANT_CONNECTION_CHANGE_ACTION?我以为只有CONNECTION_CHANGE广播。为什么是SUPPLICANT?谢谢。 - Androider
2
哎?我没看到叫做connection_change的动作...?我只看到wifi状态改变,但这个动作只表示wifi是否启用或禁用(或正在启用/禁用),而不是它是否连接上了...supplicant_connection_change_action不是你需要的吗? - jpm
9
对我而言,当连接到已知的Wifi基站时/失去连接时,WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION不起作用。但WifiManager.NETWORK_STATE_CHANGED_ACTION可以正常使用。 - Yar
1
"android.net.wifi.STATE_CHANGE" 对我有用。请查看我的回答。 - M. Usman Khan
1
当然,在此之前,您应该检查设备是否已连接到WiFi。因此,要获取初始的Wi-Fi状态,您可以使用以下方法...private boolean isConnectedViaWifi() { ConnectivityManager connectivityManager = (ConnectivityManager) appObj.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo mWifi = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); return mWifi.isConnected(); } - ban-geoengineering
显示剩余7条评论

107

对我最有效的方法:

AndroidManifest

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

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

权限

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

1
我认为这是关于WiFi状态更改的最佳答案。谢谢。 - Mikel
4
为了方便以后的参考,那个硬编码的操作是 WifiManager.NETWORK_STATE_CHANGED_ACTION - Anonsage
3
如果(info != null && info.isConnected()),则意味着没有意外情况发生。请注意,这句话的含义与意大利面条无关。 - gswierczynski
1
在主活动中发送广播,我们需要编写任何代码吗? - Nisari Balakrishnan
2
在Android O上,由于隐式广播接收器(如android.net.wifi.STATE_CHANGE)将不再允许在清单中注册(请参见https://developer.android.com/guide/components/broadcast-exceptions.html),因此我该如何处理呢?如果我们在应用程序活动中注册它(比如onCreate),那么它将必须在onStop()中注销,这样我们就无法再接收到与wifi相关的事件了。 - zafar142003
显示剩余2条评论

18

只有WifiManager.NETWORK_STATE_CHANGED_ACTION 对我有用。

注册一个广播接收器:

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

并且收到:

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

1
对我来说,只有WifiManager.NETWORK_STATE_CHANGED_ACTION起作用,为什么只有这个会起作用的原因有解释吗? - benchuk

10

用户@JPM和@usman提供的答案真的非常有用。它能够很好地工作,但在我的情况下,它在 onReceive 中出现多次,在我的情况下 4次,因此我的代码会多次执行。

我进行了一些修改,根据我的要求进行了调整,现在它只会出现1次。

这是Broadcast的Java类。

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

    }
}
}
</code></strong>

在AndroidManifest中

<strong><code><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"/>
</code></strong>

1
什么是wifiState? - behelit
1
还想了解一下wifiState是什么,它是如何生成的。 - Evan Parsons
1
@behelit,现在编辑后的答案中已经有了“wifiState”。 - Yog Guru
这个答案在API v26及以上版本中将不再有效。官方文档指出:该广播不会被发送到目标API版本为26或更高版本的应用程序中的清单接收器。 - Amir Hossein

10

如果您允许用户选择覆盖每次询问的正常行为,则可以启动wifi连接。

我选择使用三种方法...

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

这是一次快速检查,以确定是否存在互联网连接,无论是Wi-Fi还是移动数据。在此基础上,您可以选择要采取的操作。还需要检查是否处于飞行模式。

在单独的线程中, 我将一个变量IpAddress设置=“”, 并进行轮询,直到我拥有有效的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);

另一段代码片段...如果它没有被打开,就把它打开(在用户先允许的情况下)

   if(wifi.isWifiEnabled()!=true)wifi.setWifiEnabled(true);  

8
为了检测WIFI连接状态,我使用了ConnectivityManager类中的CONNECTIVITY_ACTION,代码如下:
    IntentFilter filter=new IntentFilter();
    filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
    registerReceiver(receiver, filter);

并且从您的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: 对我来说工作得很好 :)


1
值得注意的是,在Android 6中,当您拥有有效的蜂窝数据连接时,wifi状态的更改不会触发CONNECTIVITY_ACTION。唯一的原因是连接状态受到影响,现在已经不再受到影响。 - Bruce
CONNECTIVITY_ACTION:“此常量在API级别28中已弃用。应用程序应改用更通用的requestNetwork(NetworkRequest,PendingIntent),registerNetworkCallback(NetworkRequest,PendingIntent)或registerDefaultNetworkCallback(ConnectivityManager.NetworkCallback)函数,以获取更快速和更详细的有关它们关心的网络更改的更新。” - cesargastonec

5
这段代码完全不需要权限。它仅限于Wi-Fi网络连接状态的更改(不考虑任何其他网络)。接收器在AndroidManifest.xml文件中静态发布,并且不需要导出,因为系统会在每次网络连接状态更改时调用它:protected broadcastNETWORK_STATE_CHANGED_ACTIONAndroidManifest:
<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>

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

权限:

None

2
该功能仅适用于运行API 26以下版本的设备。 正如@Isham所建议的,此操作在Android O中不再受支持。 - tiagocarvalho92

4

Android O移除了接收wifi状态改变的隐式广播的可能性。因此,如果您的应用程序已关闭,则无法接收它们。新的WorkManager具有在应用程序关闭时运行的能力,因此我进行了一些实验,并且似乎工作得非常好:

将以下内容添加到您的依赖项中:

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

请注意,这只是一次快速测试用于单次通知。还有更多工作需要做,以便在WiFi打开和关闭时始终收到通知。

附注:当应用程序被强制退出时,工作者未启动,似乎WorkManager正在取消请求。


有没有办法在我的应用程序被强制退出的状态下启动工作程序?WorkManager仅在应用程序打开时使用.setRequiredNetworkType(UNMETERED)工作。即使应用程序已经关闭,是否有触发工作程序的方法?因为隐式广播接收器也受到某些限制。最好的替代方案是什么? - Suresh

4

2020年11月:

我经常处理被Google废弃的项目。最终,我找到了一个解决方案来满足我的特定需求,使用了Google目前建议的"registerNetworkCallback"。

我所需要的是一种简单的方式来检测我的设备是否在WIFI网络中分配了IPv4地址。 (我还没有尝试其他情况,我的需求非常特殊,但也许这种方法可以作为其他情况的基础而不使用被废弃的元素)。

已在API 23、24和26(物理设备)以及API 28和29(模拟设备)上进行了测试。

    ConnectivityManager cm 
            = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkRequest.Builder builder = new NetworkRequest.Builder();

    cm.registerNetworkCallback
            (
                    builder.build(),
                    new ConnectivityManager.NetworkCallback()
                    {
                        @Override
                        public void onAvailable(Network network)
                        {
                            //Actions to take with Wifi available.
                        }
                        @Override
                        public void onLost(Network network)
                        {
                            //Actions to take with lost Wifi.
                        }
                    }

            );

(在“MainActivity.Oncreate”中实现)

注意:在清单文件中需要添加“android.permission.ACCESS_NETWORK_STATE”。


我一直在使用已弃用的版本,并注意到一个缺陷 - 我可以检测到wifi是否真正断开,但无法确定wifi是否正常工作以及连接的路由器是否正常。新的回调函数是否指示您是否实际上可以通信互联网?还是像以前一样只能看到SSID? - Martin
@Martin 对不起,我只使用这种方法来检测设备是否分配了IPv4地址(从“onAvailable”调用另一个函数)。我没有检查是否有互联网。在我的项目中,互联网并不是必需的。我相信肯定有其他适当的方法可以验证互联网访问,可能也可以从“onAvailable”中调用。 - cesargastonec
当第一次启动应用程序时,没有网络连接时,onLost()onUnavailable()都不会被调用。有什么建议吗? - Sam Chen
@SamChen 你是对的。如果应用程序启动时Wifi没有连接,则不会调用“onAvailable()”和“onLost()”函数。但是,如果应用程序启动时有建立的Wifi连接,“onAvailable()”实际上会被调用。因此,我将我的应用程序初始化为“Wifi未连接”的状态。如果实际上没有Wifi连接,我的应用程序将保持在“Wifi未连接”的状态。但是,如果在启动我的应用程序时发现已经建立了Wifi连接,则“onAvailable”函数将被调用,并且它将把我的应用程序更改为“Wifi已连接”的状态。 - cesargastonec
@cesargastonec 哇,你太聪明了!使用 Boolean 来指示连接状态并将其初始化为 false,好主意。 - Sam Chen
@cesargastonec 很遗憾,这对我不起作用,但我找到了我的解决方案:https://stackoverflow.com/questions/66573565/android-get-status-of-wifi-connection/66648761 - Sam Chen

3

1) 我尝试了Broadcast Receiver方法,即使我知道在API 28中已弃用CONNECTIVITY_ACTION/CONNECTIVITY_CHANGE并且不推荐使用。此外,它仅绑定到使用显式注册的应用程序,只要应用程序正在运行,它就会监听。

2) 我也尝试了Firebase Dispatcher,它可以工作,但无法在应用程序关闭后继续运行。

3) 推荐的方法是使用WorkManager来保证执行超过进程终止,并在内部使用registerNetworkRequest()

最大的支持#3方法的证据是由Android文档本身提供的。特别是对于后台中的应用程序。

还有在这里

在Android 7.0中,我们正在删除三个常用的隐式广播-- CONNECTIVITY_ACTION,ACTION_NEW_PICTURE和ACTION_NEW_VIDEO--因为这些广播可以同时唤醒多个应用程序的后台进程,并且会消耗内存和电池电量。 如果您的应用正在接收这些广播,请利用Android 7.0迁移到JobScheduler和相关API。

到目前为止,我们使用定期WorkManager请求很好地运行。

更新:最终我写了两篇系列关于此的中等文章


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接