简体   繁体   English

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

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

Sorry for my english. 对不起我的英语不好。 I am writing a code to connect to another android device hotspot. 我正在编写一个代码来连接到另一个Android设备热点。 It gets connected. 它连接起来了。 But, the hotspot will have no internet in my case. 但是,在我的情况下,热点将没有互联网。 Now the connected device, switches back to another wifi network with internet. 现在连接的设备,切换回与互联网的另一个wifi网络。 Is there any better way to connect to hotspot other than my way? 除了我的方式,有没有更好的方法连接到热点? My code is below: 我的代码如下:

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


}

And the receiver is: 接收器是:

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

Due to the asynchronous nature of WiFi handling in Android, I would focus on those 4 lines looking for a solution 由于Android中WiFi处理的异步特性,我将专注于寻找解决方案的4条线路

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

disconnect and reconnect calls are wrong here, they are only confusing internal wifi state machine. disconnectreconnect呼叫是错误的,它们只会混淆内部wifi状态机。 Invoking enableNetwork with parameter boolean attemptConnect set to true is enough to programmatically select wifi. 使用参数boolean attemptConnect设置为true调用enableNetwork足以以编程方式选择wifi。

So just use: 所以只需使用:

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

To be honest I've tried scan through the internal sources, but internal state machine is quite complex. 说实话,我已经尝试过浏览内部资源,但内部状态机非常复杂。 If you are interested, you may look here for state machine and here to see how wifi config database is handled. 如果您有兴趣,可以在这里查看状态机,并在这里查看如何处理wifi配置数据库。

Also, to avoid having multiple configuration entries, before calling addNetwork, check already created configs to look for SSID you want to connect and only call enableNetwork . 此外,为避免多个配置条目,在调用addNetwork之前,请检查已创建的配置以查找要连接的SSID,并仅调用enableNetwork

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

I'm using it in production and works fine. 我在生产中使用它并且工作正常。 Never tested in API level lower than 24. 从未在API级别低于24进行测试。

Since you are able to connect to the hotspot, temporarily, the end problem is the switching on the phone to a "better" alternative network. 由于您可以暂时连接到热点,最终的问题是将手机切换到“更好”的替代网络。 While the answer https://stackoverflow.com/a/53249295/949224 will help tidy up your code when connecting to your specific hotspot, you should also look into disabling "smart network switching" and Wi-Fi auto-reconnect. 虽然答案https://stackoverflow.com/a/53249295/949224将有助于在连接到您的特定热点时整理您的代码,但您还应该考虑禁用“智能网络切换”和Wi-Fi自动重新连接。 The former is also referred to as poor network watchdog, and is monitoring the connectivity on your current network and will switch to LTE data if it is likely to improve, and the latter will change to another known Wi-Fi AP in the case that can offer better connectivity. 前者也被称为差的网络监视器,并且正在监视当前网络上的连接,并且如果可能改进则将切换到LTE数据,并且后者将在可以改变的情况下改变为另一个已知的Wi-Fi AP提供更好的连接。

Smart network switch can be disabled in the advanced WiFi settings activity. 可以在高级WiFi设置活动中禁用智能网络交换机。 It's not exposed as a Java API in Android and 3rd party apps are not designed to be given a relevant permission, plus different OEM suppliers have different custom implementations, therefore the best approach is to launch the right settings panel from your application. 它不是作为Android中的Java API公开的,第三方应用程序并未设计为获得相关权限,而且不同的OEM供应商具有不同的自定义实现,因此最佳方法是从您的应用程序启动正确的设置面板。 Here is an example: 这是一个例子:

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

On some devices, Smart network switching can be disabled without the external settings dialog via ConnectivityManager.setProcessDefaultNetwork(Network net) but I have seen this is unreliable. 在某些设备上,可以通过ConnectivityManager.setProcessDefaultNetwork(Network net)在没有外部设置对话框的情况下禁用智能网络切换,但我看到这是不可靠的。 I normally set this anyway and then also use the settings. 我通常设置它然后也使用设置。

Wi-Fi auto-reconnect can be disabled manually in the WiFi settings activity by selecting known APs and unchecking the check-box. 通过选择已知的AP并取消选中复选框,可以在WiFi设置活动中手动禁用Wi-Fi自动重新连接。

A quick way to do this programmatically is to iterate through your known AP IDs in WifiManager and call WifiManager.disableNetwork(int netID) . 以编程方式执行此操作的快速方法是在WifiManager中迭代已知的AP ID并调用WifiManager.disableNetwork(int netID) You can always enable these again programmatically after you want to end the Hotspot connection. 在您想要结束Hotspot连接之后,您始终可以以编程方式再次启用它们。 That way the user should get their phone back as normal afterwards. 这样,用户之后应该恢复正常的手机。

By the way, there are ways to check whether poor network avoidance (auto switching) is enabled via Java reflection, but it is cumbersome and so I don't think its worth it to post examples here. 顺便说一句,有办法检查是否通过Java反射启用了不良的网络避免(自动切换),但它很麻烦,所以我认为在这里发布示例是不值得的。

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

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