Android Q,编程方式连接不同的WiFi AP以获取互联网

23
与 Android Q 一样,有几个 WiFi API 受限。我正在尝试使用替代 API 连接到不同的 WiFi 接入点进行互联网访问。
以下是我的代码:
    WifiNetworkSpecifier.Builder builder = new WifiNetworkSpecifier.Builder();
    builder.setSsid("wifi-ap-ssid");
    builder.setWpa2Passphrase("wifi-ap-password");

    WifiNetworkSpecifier wifiNetworkSpecifier = builder.build();

    NetworkRequest.Builder networkRequestBuilder1 = new NetworkRequest.Builder();
    networkRequestBuilder1.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
    networkRequestBuilder1.setNetworkSpecifier(wifiNetworkSpecifier);

    NetworkRequest nr = networkRequestBuilder1.build();
    ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    cm.requestNetwork(nr, callback);

这使我能够连接,但互联网被禁用。这符合Android文档中定义的工作方式。

我尝试的另一种方法如下:

    WifiNetworkSuggestion.Builder wifiNetworkSuggestionBuilder1 = new WifiNetworkSuggestion.Builder();
    wifiNetworkSuggestionBuilder1.setSsid("wifi-ap-ssid");
    wifiNetworkSuggestionBuilder1.setWpa2Passphrase("wifi-ap-password");
    WifiNetworkSuggestion wifiNetworkSuggestion = wifiNetworkSuggestionBuilder1.build();
    List<WifiNetworkSuggestion> list = new ArrayList<>();
    list.add(wifiNetworkSuggestion);
    wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
    wifiManager.removeNetworkSuggestions(new ArrayList<WifiNetworkSuggestion>());
    wifiManager.addNetworkSuggestions(list);

在清单文件中声明的权限:

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

使用此功能并未改变任何行为。

请告知连接到不同可上网的Wifi AP所需的API序列。


3
这方面已经向谷歌提出了一张公开的工单。我建议你们在这张工单上留言和表态,这样可以引起谷歌的关注。https://issuetracker.google.com/issues/138335744 - Anand Khinvasara
@AnandKhinvasara:由于这些API没有提供互联网功能,我正在显示一个弹出窗口,让用户手动转到设置并连接到AP。希望这种替代方式可以适用于您的用例。 - Vinodh
1
我已经让它正常工作了。请检查我的答案。 - Anand Khinvasara
那个对你有用吗? - Anand Khinvasara
@AnandKhinvasara,这个方法对你有用吗?你的答案在哪里? - R.singh
显示剩余2条评论
5个回答

14

尝试在onAvailable()回调中调用bindProcessToNetwork()以恢复网络连接,这对我很有效。

连接到网络:

    WifiNetworkSpecifier.Builder builder = new WifiNetworkSpecifier.Builder();
    builder.setSsid("wifi-ap-ssid");
    builder.setWpa2Passphrase("wifi-ap-password");

    WifiNetworkSpecifier wifiNetworkSpecifier = builder.build();

    NetworkRequest.Builder networkRequestBuilder1 = new NetworkRequest.Builder();
    networkRequestBuilder1.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
    networkRequestBuilder1.setNetworkSpecifier(wifiNetworkSpecifier);

    NetworkRequest nr = networkRequestBuilder1.build();
    ConnectivityManager cm = (ConnectivityManager)
            context.getSystemService(Context.CONNECTIVITY_SERVICE);
    ConnectivityManager.NetworkCallback networkCallback = new 
        ConnectivityManager.NetworkCallback() {
        @Override
        public void onAvailable(Network network) {
            super.onAvailable(network);
            Log.d(TAG, "onAvailable:" + network);
            cm.bindProcessToNetwork(network);
        }
    });
    cm.requestNetwork(nr, networkCallback);

断开与绑定网络的连接:

cm.unregisterNetworkCallback(networkCallback);

这个答案没有解决问题。这种连接方法无法访问互联网。 - mono
你救了我的一天,非常感谢!祝好! - FABiO

5

WifiNetworkSuggestion API 用于向用户推荐加入一个AP(系统会为用户发布通知以加入)。

使用 WifiNetworkSpecifier 发送您的请求。在 onAvailable() 中提供的网络对象。

WifiNetworkSpecifier.Builder builder = new WifiNetworkSpecifier.Builder();
builder.setSsid("wifi-ap-ssid");
builder.setWpa2Passphrase("wifi-ap-password");

WifiNetworkSpecifier wifiNetworkSpecifier = builder.build();

NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();
networkRequestBuilder1.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
networkRequestBuilder1.setNetworkSpecifier(wifiNetworkSpecifier);

NetworkRequest networkRequest = networkRequestBuilder.build();
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
cm.requestNetwork(networkRequest, networkCallback);
networkCallback = new ConnectivityManager.NetworkCallback() {
            @Override
            public void onAvailable(@NonNull Network network) {
                //Use this network object to Send request. 
                //eg - Using OkHttp library to create a service request
                 //Service is an OkHttp interface where we define docs. Please read OkHttp docs
                 Service service = null;

                 OkHttpClient.Builder okHttpBuilder = new OkHttpClient.Builder();
                okHttpBuilder.socketFactory(network.getSocketFactory());

                service = new Retrofit.Builder()                                    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                        .addConverterFactory(GsonConverterFactory.create(gson))
                         .client(okHttpBuilder.build())
                         .build()
                         .create(Service.class);


               Observable<Object> observable = null;
               try {
                  if (service != null) {
                     observable = service.yourRestCall();
                  }
                  Subscriber<Object> sub = new Subscriber< Object >() {
                     @Override
                     public void onError(Throwable e) {
                        //Do on error
                     }

                     @Override
                     public void onNext(Object logs) {
                        //Do on next
                     }
                  };
                 if(observable != null) {
                     observable.subscribeOn(Schedulers.io())
                                          .observeOn(AndroidSchedulers.mainThread()).subscribe(sub);
                 }

                super.onAvailable(network);
            }
        };

在使用Wifi接入点后,执行以下操作:

connectivityManager.unregisterNetworkCallback(networkCallback);

来自谷歌工程师在谷歌的问题跟踪器中的说明:

网络建议API流程要求用户批准应用程序(平台发布通知以请求用户批准)。一旦应用程序得到批准,平台将在将来的自动连接尝试中考虑该应用程序的所有网络。但是,此API不能保证设备何时会连接到您的AP以进行配置。因此,WifiNetworkSuggestion不是提供的用例(点对点即时连接)的正确API表面。

使用WifiNetworkSpecifier可以像上面提到的那样与Wifi接入点建立本地连接。在这种情况下,默认网络仍然是蜂窝网络(我们不会干扰其他应用程序的互联网连接)。发出请求的应用程序应使用多网络API将其流量路由到已建立的连接上。在请求的onAvailable()回调中提供的| Network |对象是应用程序需要用于在该本地网络上打开套接字的句柄(请参阅https://developer.android.com/reference/android/net/Network.html#bindSocket(java.net.DatagramSocket)和在| Network |对象表面可用的其他API)。

希望这可以帮助。


使用这段代码,我可以得到正确的AP授权窗口...但是当我按下连接按钮时它不起作用。会获取IP地址然后重新连接到正常网络。 有什么想法吗? 谢谢 - Aristide13
嗨,@Anand Khinvasara,你能否发布完整的代码吗?我在通过编程连接时遇到了很多没有互联网的问题。谢谢! - Russell C.
1
好的,我已经尝试了WifiNetworkSpecifier...我有一个问题,就是当选择一个网络后,设备无法连接到该网络。我也尝试了文档中的代码,有没有解决没有互联网连接的本地网络的方案? - Faizan Mir

3
此处所述,Android 10有意使WifiNetworkSpecifier阻止实际的互联网连接。它适用于点对点连接。
然而,WifiNetworkSuggestion API提供了互联网连接,并且类似于WifiNetworkSpecifier API的行为。只要设备当前没有连接到任何Wifi网络,WifiNetworkSuggestion API将自动连接到指定的网络。当设备第一次使用它时,会出现一个通知询问应用程序是否可以建议网络。用户必须接受此通知才能使WifiNetworkSuggestion API正常工作。
我发现Android在WifiNetworkSuggestion文档中提供的代码存在一些编译错误。以下是我发现可行的代码:
final WifiNetworkSuggestion suggestion1 = new WifiNetworkSuggestion.Builder()
.setSsid("SSID here")
.setWpa2Passphrase("password here")
.setIsAppInteractionRequired(true) // Optional (Needs location permission)
.build();

// Optional extra suggesstion, you can delete this or add more
final WifiNetworkSuggestion suggestion2 = new WifiNetworkSuggestion.Builder()
.setSsid("SSID here 2")
.setWpa2Passphrase("password here 2")
.setIsAppInteractionRequired(true) // Optional (Needs location permission)
.build();

final List<WifiNetworkSuggestion> suggestionsList = new ArrayList<WifiNetworkSuggestion>();
suggestionsList.add(suggestion1);
suggestionsList.add(suggestion2); // Optional extra suggestion
final WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
final int status = wifiManager.addNetworkSuggestions(suggestionsList);

if (status != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) {
     // Error handling
}

final IntentFilter intentFilter = new IntentFilter(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION);

final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
    @Override public void onReceive(Context context, Intent intent) {
        if (!intent.getAction().equals(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION)) {
              return;
        }
        // Post connection
    }
};
getApplicationContext().registerReceiver(broadcastReceiver, intentFilter);

这是否意味着我被迫使用WiFiNetworkSuggestion来连接WiFi并实际使用互联网?我能够使用WiFiNetworkSpecifier连接WiFi,但无法实际访问互联网。 - Inception
1
是的,您需要使用WifiNetworkSuggestion。WifiNetworkSpecifier是用于连接到网络以实现除互联网访问之外的某些目的。 - Russell C.

1

在 Q 中,您应该使用 Wi-Fi 网络建议 API

final WifiNetworkSuggestion suggestion1 =
  new WifiNetworkSuggestion.Builder()
  .setSsid("test111111")
  .setIsAppInteractionRequired() // Optional (Needs location permission)
  .build()

final WifiNetworkSuggestion suggestion2 =
  new WifiNetworkSuggestion.Builder()
  .setSsid("test222222")
  .setWpa2Passphrase("test123456")
  .setIsAppInteractionRequired() // Optional (Needs location permission)
  .build()

final WifiNetworkSuggestion suggestion3 =
  new WifiNetworkSuggestion.Builder()
  .setSsid("test333333")
  .setWpa3Passphrase("test6789")
  .setIsAppInteractionRequired() // Optional (Needs location permission)
  .build()

final List<WifiNetworkSuggestion> suggestionsList =
  new ArrayList<WifiNetworkSuggestion> {{
    add(suggestion1);
    add(suggestion2);
    add(suggestion3);
  }};

final WifiManager wifiManager =
  (WifiManager) context.getSystemService(Context.WIFI_SERVICE);

final int status = wifiManager.addNetworkSuggestions(suggestionsList);
if (status != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) {
// do error handling here…
}

// Optional (Wait for post connection broadcast to one of your suggestions)
final IntentFilter intentFilter =
  new IntentFilter(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION);

final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
  @Override
  public void onReceive(Context context, Intent intent) {
    if (!intent.getAction().equals(
      WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION)) {
      return;
    }
    // do post connect processing here..
  }
};
context.registerReceiver(broadcastReceiver, intentFilter);

它能在使用WifiNetworkSpecifier建立的连接上工作吗? - Vinodh
2
谢谢您的回复。但是,我发现网络建议没有提供互联网功能。您有其他的解决方法吗? - Vinodh
3
对我来说,status!= WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS 总是为真。因此我无法让它工作。 - Anand Khinvasara

0
在设置完WifiNetworkSpecifier.Builder()NetworkRequest.Builder()后,在ConnectivityManager.NetworkCallback()onAvailable(Network)回调中,调用ConnectivityManager.bindProcessNetwork将您的流量定向到已连接的网络上,如Network API文档所述:https://developer.android.com/reference/android/net/Network

标识一个网络。通过ConnectivityManager.NetworkCallback响应活动的ConnectivityManager#requestNetwork或被动的ConnectivityManager#registerNetworkCallback调用,将其提供给应用程序。它用于将流量定向到给定的网络上,可以通过有针对性的SocketFactory或进程范围内的ConnectivityManager#bindProcessToNetwork进行。

....
private inner class MyCallback : ConnectivityManager.NetworkCallback() {
    override fun onAvailable(network: Network) {
        // Call this method once this callback is triggered after
        // your call to mConnectivityManager.requestNetwork()
        mConnectivityManager.bindProcessToNetwork(network)
    }
    ....
}
....

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