繁体   English   中英

以编程方式将Android设备连接到Hotspot(没有互联网)切换回带有互联网的wifi

[英]Programatically connected android device to Hotspot (no internet) switches back to wifi with internet

对不起我的英语不好。 我正在编写一个代码来连接到另一个Android设备热点。 它连接起来了。 但是,在我的情况下,热点将没有互联网。 现在连接的设备,切换回与互联网的另一个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;
        }
    }
};

由于Android中WiFi处理的异步特性,我将专注于寻找解决方案的4条线路

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

disconnectreconnect呼叫是错误的,它们只会混淆内部wifi状态机。 使用参数boolean attemptConnect设置为true调用enableNetwork足以以编程方式选择wifi。

所以只需使用:

    int netId = manager.addNetwork(wifiConfig);
    manager.enableNetwork(netId, true);

说实话,我已经尝试过浏览内部资源,但内部状态机非常复杂。 如果您有兴趣,可以在这里查看状态机,并在这里查看如何处理wifi配置数据库。

此外,为避免多个配置条目,在调用addNetwork之前,请检查已创建的配置以查找要连接的SSID,并仅调用enableNetwork

    List<WifiConfiguration> networks = manager.getConfiguredNetworks();
    for(WifiConfiguration c : networks) {
        if (isThisWifiAppSpecific(c.SSID)) {
            manager.enableNetwork(c.networkId, true);
            return;
        }
    }

我在生产中使用它并且工作正常。 从未在API级别低于24进行测试。

由于您可以暂时连接到热点,最终的问题是将手机切换到“更好”的替代网络。 虽然答案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反射启用了不良的网络避免(自动切换),但它很麻烦,所以我认为在这里发布示例是不值得的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM