以编程方式将android设备连接到Hotspot(无互联网)会切换回具有互联网的wifi

时间:2018-10-05 18:26:56

标签: android

对不起,我的英语。 我正在编写代码以连接到另一个Android设备热点。它已连接。但是,就我而言,热点将没有互联网。现在,已连接的设备切换回具有Internet的另一个wifi网络。 除了我以外,还有没有更好的方法可以连接到热点? 我的代码如下:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.layout);
    AppCompatButton btnConnect=findViewById(R.id.btnConnect);
    btnConnect.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            registerReceiver(mWifiBroadcastReceiver , new IntentFilter("android.net.wifi.STATE_CHANGE"));
            EditText eSSID=findViewById(R.id.ssid);
            EditText ePassword=findViewById(R.id.password);
            String ssid = eSSID.getText().toString();
            String key = ePassword.getText().toString();
            WifiConfiguration wifiConfig = new WifiConfiguration();
            wifiConfig.SSID = String.format("\"%s\"" , ssid);
            wifiConfig.preSharedKey = String.format("\"%s\"" , key);
            connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
            wifiManager = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);

            int netId = wifiManager.addNetwork(wifiConfig);
            wifiManager.disconnect();
            wifiManager.enableNetwork(netId , true);
            wifiManager.reconnect();
        }
    });


}

接收者是:

  private BroadcastReceiver mWifiBroadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(final Context context , Intent intent) {
        switch (intent.getAction()) {
            case WifiManager.NETWORK_STATE_CHANGED_ACTION:
                NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
                boolean isConnected = info.isConnected();
                boolean isConnecting = info.isConnectedOrConnecting();

                //TODO: probably better to use the EXTRA_ info here
                String ssid = wifiManager.getConnectionInfo() != null ?
                        wifiManager.getConnectionInfo().getSSID() : null;


                ssid = normalizeAndroidWifiSsid(ssid);

                String stateName = "";
                switch (info.getState()) {
                    case CONNECTED:
                        stateName = "connected";
                        break;

                    case CONNECTING:
                        stateName = "connecting";
                        break;

                    case DISCONNECTED:
                        stateName = "disconnected";
                        break;

                    case DISCONNECTING:
                        stateName = "disconnecting";
                        break;

                    case SUSPENDED:
                        stateName = "suspended";
                        break;

                    case UNKNOWN:
                        stateName = "unknown";
                        break;
                }


                if (Build.VERSION.SDK_INT >= 21) {
                    if (isConnected) {
                        NetworkRequest.Builder builder = new NetworkRequest.Builder();
                        builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
                        builder.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
                        final String connectedSsid = ssid;
                        connectivityManager.registerNetworkCallback(builder.build() , new ConnectivityManager.NetworkCallback() {
                            @Override
                            public void onAvailable(Network network) {
                                super.onAvailable(network);
                                NetworkInfo networkInfo = connectivityManager.getNetworkInfo(network);

                                //This is always the SSID if it's wifi, even though this is *not* documented
                                String networkSsid = networkInfo.getExtraInfo();

                                if (networkSsid.equals(connectedSsid)) {
                                    runOnUiThread(new Runnable() {
                                        @Override
                                        public void run() {
                                            Toast.makeText(context,"Connected Successfully",Toast
                                                    .LENGTH_LONG).show();
                                        }
                                    });

                                    /*
                                     * We can now use network.openURLConnection and network.getSocketFactory()
                                     * to communicate using the wifi network that has no Internet
                                     */
                                    connectivityManager.unregisterNetworkCallback(this);
                                }
                            }
                        });

                    }
                }

                break;
        }
    }
};

2 个答案:

答案 0 :(得分:5)

由于Android中WiFi处理的异步特性,我将重点关注这4条寻求解决方案的线路

        int netId = wifiManager.addNetwork(wifiConfig);
        wifiManager.disconnect();
        wifiManager.enableNetwork(netId , true);
        wifiManager.reconnect();

disconnectreconnect的呼叫在这里是错误的,它们只是使内部wifi状态机混乱。将参数enableNetwork设置为boolean attemptConnect的{​​{1}}足以以编程方式选择wifi。

因此只需使用:

true

说实话,我尝试扫描内部源,但是内部状态机非常复杂。如果您有兴趣,可以在here中查看状态机,并在here中查看wifi配置数据库的处理方式。

此外,为避免有多个配置条目,在调用addNetwork之前,请检查已创建的配置以查找要连接的SSID,而仅调用 int netId = manager.addNetwork(wifiConfig); manager.enableNetwork(netId, true);

enableNetwork

我正在生产中使用它,并且效果很好。从未在低于24的API级别上进行过测试。

答案 1 :(得分:3)

由于您能够暂时连接到热点,因此最终的问题是将电话切换到“更好”的备用网络。虽然答案https://stackoverflow.com/a/53249295/949224将有助于在连接到特定热点时整理代码,但您还应该考虑禁用“智能网络交换”和Wi-Fi自动重新连接。前者也称为不良网络监视程序,正在监视当前网络上的连接,如果可能会改善,将切换到LTE数据,而在可能的情况下,后者将更改为另一个已知的Wi-Fi AP提供更好的连接性。

可以在高级WiFi设置活动中禁用智能网络开关。它并未在Android中公开为Java API,并且未设计为第三方应用提供相关权限,而且不同的OEM供应商具有不同的自定义实现,因此最好的方法是从您的应用启动正确的设置面板。这是一个示例:

public static void requestSmartNetworkSettings(Context ctx){
    final Intent i = new Intent(Settings.ACTION_WIFI_IP_SETTINGS);
    final PackageManager mgr = ctx.getPackageManager();
    if( mgr.resolveActivity(i, PackageManager.MATCH_DEFAULT_ONLY) != null)
    {
        ctx.startActivity(i);
    }
}

在某些设备上,无需通过ConnectivityManager.setProcessDefaultNetwork(Network net)即可在没有外部设置对话框的情况下禁用智能网络切换,但是我发现这是不可靠的。无论如何,我通常都会进行设置,然后再使用设置。

可以通过选择已知的AP并取消选中复选框,在WiFi设置活动中手动禁用Wi-Fi自动重新连接。

一种通过编程方式快速执行此操作的方法是,在WifiManager中迭代您已知的AP ID并调用WifiManager.disableNetwork(int netID)。要结束Hotspot连接后,您始终可以通过编程方式再次启用这些功能。这样,用户之后应将手机恢复正常。

顺便说一句,有很多方法可以检查是否通过Java反射启用了不良的网络避免(自动切换)功能,但它很麻烦,因此我认为在此处发布示例并不值得。