簡體   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