【问题标题】:Programatically connected android device to Hotspot (no internet) switches back to wifi with internet以编程方式将 android 设备连接到热点(无互联网)切换回带互联网的 wifi
【发布时间】:2019-03-11 07:14:01
【问题描述】:

对不起我的英语。 我正在编写代码以连接到另一个 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;
        }
    }
};

【问题讨论】:

  • 你以后如何使用你的网络连接?您是否正在使用一些需要互联网访问的 API?你见过这个note吗?此外,此网络切换是立即发生还是在一段时间后发生?如果是后者,我们谈论的时间是多少?
  • @JakubPiskorz 切换发生在 4-5 秒内,有时更短。如果已知网络没有互联网,则会特别发生这种情况。对于未知网络 wifi 连接并弹出显示“wifi 无法访问互联网”,那很好.. 至少发生连接。但我担心的是没有互联网的已知网络。

标签: android


【解决方案1】:

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

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

disconnectreconnect 调用在这里是错误的,它们只是混淆了内部 wifi 状态机。调用enableNetwork 并将参数boolean attemptConnect 设置为true 足以以编程方式选择wifi。

所以只需使用:

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

说实话,我尝试过扫描内部资源,但内部状态机相当复杂。如果你有兴趣,你可以看看here 的状态机和here 看看wifi config 数据库是如何处理的。

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

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

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

【讨论】:

  • 有用但不能回答我的问题
【解决方案2】:

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

可以在高级 WiFi 设置活动中禁用智能网络开关。它没有在 Android 中作为 Java API 公开,并且第 3 方应用程序的设计目的不是为了获得相关权限,而且不同的 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 反射启用了不良网络避免(自动切换),但它很麻烦,所以我认为在这里发布示例不值得。

【讨论】:

    猜你喜欢
    • 2017-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多