4

デバイスがアクセス ポイントの範囲内にあるとすぐに、画面をオンにせずに Wi-Fi を自動的にオンにすることに苦労しています。特にデバイスが異なれば結果がまったく異なるため、テストして解決策を見つけるのは非常にイライラします。

基本テスト
このテスト中は画面をオフにしておいてください。アプリケーションは WifiLock を保持する必要があります。

  1. WiFi のカバレッジから出て、そこに 1 分間とどまります。
  2. カバレッジに戻ります。

結果 : Wifi がすぐに再接続されないため、アプリはサーバーに再接続しません。デバイスや設定によっては、画面をオンにするまでまったく再接続しない場合がありました。

Wi-Fi を強制的に再接続する
さて、今回は、Wifi が切断された場合に、アプリケーションが間隔を置いて WifiManager.Reconnect() を呼び出します。

テストを繰り返しました。結果 : S3 では機能し、他のデバイスでは失敗しました。

他のいくつかの呼び出しを追加
しようとしました WifiManager.Scan()、WifiManager.Reassociate()、...などのさまざまな組み合わせを試しました。最終的には、S4 を除くほとんどのデバイス (HTC、S3) で機能していました。

すべてのデバイスで動作するように見えるコード

NetworkInfo wifiInfo = _androidConnectivityMgr.GetNetworkInfo(ConnectivityType.Wifi);
if (!_wifiManager.IsWifiEnabled || _wifiManager.WifiState == WifiState.Disabled || _wifiManager.WifiState == WifiState.Disabling)
{
    // Make sure the Wi-Fi is enabled, required for some devices when enable WiFi does not occur immediately
    _wifiManager.SetWifiEnabled(true);
}

if (!wifiInfo.IsConnectedOrConnecting)
{
    // Do not wait for the OS to initiate a reconnect to a Wi-Fi router
    _wifiManager.PingSupplicant();
    if (_wifiManager.WifiState == WifiState.Enabled)
    {
        try
        {
            // Brute force methods required for some devices
            _wifiManager.SetWifiEnabled(false);
            _wifiManager.SetWifiEnabled(true);
        }
        catch (Java.Lang.SecurityException)
        {
            // Catching exception which should not occur on most devices. OS bug details at :
            // https://code.google.com/p/android/issues/detail?id=22036
        }
    }
    _wifiManager.Disconnect();
    _wifiManager.StartScan();
    _wifiManager.Reassociate();
    _wifiManager.Reconnect();
}

オンラインで多くの情報を見つけることができなかったため、このコードがすべて必要かどうかさえわかりません。WifiFixerはいくつか役に立ちました。しかし、これは私がテストしたデバイスではうまくいくようです。

質問

  • これを行うより良い方法はありますか?
  • メーカーは、これほど大きな違いが見られる基本的な Android を本当に変更していますか?
  • これはこれにアプローチする完全に間違った方法ですか?

これをすべて読んでくれてありがとう:)

その他の注意事項

  1. コードは、AlarmManager から開始された 10 秒以上の間隔で実行されます。WakeLock は、この呼び出しの間だけ保持されます。
  2. この最終的な恐ろしく見えるソリューション/ハッキングの前に、「Wifi スリープ ポリシー」が結果に影響を与えました。私はずっとWifiLockを保持していたので、これは私を混乱させました.
  3. 「Wifiスリープポリシー」をプログラムで変更してもS4では機能しません。他の誰かが確認できますか?
  4. はい、これを行う必要があり、バッテリーの影響を認識しています。
4

3 に答える 3

2

私のシナリオは少し異なります-最初はwifiロックを保持していません(そして、私は通常のAndroidを使用しているため、メソッドを翻訳する必要がありました)。

画面オフ、CPUオフ、ラジオ停止。アラームが (ウェイクフル) サービスを起動します - (部分的な) ウェイク ロックを保持しています。

私が欲しいのは、無線が切れる前に接続されていたアクセスポイントにwifiが接続できるようになっている場合-wifiロックを取得し、あなたの関数を呼び出します- wakeWifiUp(). 無線が停止すると (!wifiInfo.IsConnectedOrConnecting本当です) 、接続しようとするとネットワークに到達できなくなります。次のように回避します:

public final class NetworkService extends WakefulIntentService {

    // this is an intent service - runs on its own thread - otherwise it would
    // deadlock as I am using it. Moreover it holds a wakelock and woken up by
    // an AlarmManager's Receiver - works reliably
    private BroadcastReceiver mConnectionReceiver;
    private volatile static CountDownLatch latch;

    @Override
    protected void doWakefulWork(Intent intent) {
        WifiLock _wifiLock = null;
        WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
        boolean failedToConnect = true;
        if (wm != null && wm.isWifiEnabled()) {// Don't want to enable it myself
            _wifiLock = wm.createWifiLock(
            /* WifiManager.WIFI_MODE_FULL_HIGH_PERF */0x3, this.getClass()
                .getName() + ".WIFI_LOCK");
            _wifiLock.acquire();
            failedToConnect = !wakeWifiUp();
        }
        if (failedToConnect) {
            if (_wifiLock != null) _wifiLock.release();
            w("No connection !");
            return;
        }
        HttpURLConnection connection = null;
        try {
            connection = connection(); 
        } catch (IOException e) {/* won't throw - it doesn't do much*/}
        OutputStream serverOutputStream = null;
        try {
            serverOutputStream = connection.getOutputStream(); // now
            // this is really where the connection might seriously throw
            // .... Work ....
        } catch (IOException e) {
            w("IOException sending data " + e.getMessage());
            // I get here : Network unreachable when radio dies
        } finally {
            if (_wifiLock != null) _wifiLock.release();
            if (connection != null) connection.disconnect();
        }
    }

    private HttpURLConnection connection() throws MalformedURLException,
            IOException {
        HttpURLConnection connection = (HttpURLConnection) new URL("localhost")
            .openConnection();
        connection.setDoOutput(true); // triggers POST
        connection.setRequestProperty("Connection", "Keep-Alive");
        connection.setRequestProperty("User-Agent",
            "Android Multipart HTTP Client 1.1");
        return connection;
    }

    private boolean wakeWifiUp() {
        ConnectivityManager _androidConnectivityMgr = (ConnectivityManager)
                getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo wifiInfo = _androidConnectivityMgr
            .getNetworkInfo(ConnectivityManager.TYPE_WIFI);
        WifiManager _wifiManager = (WifiManager)
                getSystemService(Context.WIFI_SERVICE);
        final int wifiState = _wifiManager.getWifiState();
        if (!_wifiManager.isWifiEnabled()
            || wifiState == WifiManager.WIFI_STATE_DISABLED
            || wifiState == WifiManager.WIFI_STATE_DISABLING) {
            // Make sure the Wi-Fi is enabled, required for some devices when
            // enable WiFi does not occur immediately
            d("!_wifiManager.isWifiEnabled()");
            _wifiManager.setWifiEnabled(true);
            // do not enable if not enabled ! FIXME
            return false;
        }
        if (!wifiInfo.isConnectedOrConnecting()) {
            d("Wifi is NOT Connected Or Connecting - "
                + "wake it up and wait till is up");
            // Do not wait for the OS to initiate a reconnect to a Wi-Fi router
            _wifiManager.pingSupplicant();
            if (wifiState == WifiManager.WIFI_STATE_ENABLED) {
                try {
                    // Brute force methods required for some devices
                    _wifiManager.setWifiEnabled(false);
                    _wifiManager.setWifiEnabled(true);
                } catch (SecurityException e) {
                    // Catching exception which should not occur on most
                    // devices. OS bug details at :
                    // https://code.google.com/p/android/issues/detail?id=22036
                }
            }
            _wifiManager.disconnect();
            _wifiManager.startScan();
            _wifiManager.reassociate();
            _wifiManager.reconnect();
            // THIS IS WHAT I DO TO WAIT FOR A CONNECTION
            try {
                mConnectionReceiver = new WifiConnectionMonitor();
                startMonitoringConnection();
                latch = new CountDownLatch(1);
                w("I wait");
                latch.await();
                w("Woke up");
                return true; // made it
            } catch (InterruptedException e) {
                w("Interrupted while waiting for connection", e);
                return false;
            } finally {
                stopMonitoringConnection();
            }
        }
        return true;
    }

    static void downTheLatch() {
        latch.countDown();
    }

    private synchronized void startMonitoringConnection() {
        IntentFilter aFilter = new IntentFilter(
            ConnectivityManager.CONNECTIVITY_ACTION);
        aFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
        registerReceiver(mConnectionReceiver, aFilter);
    }

    private synchronized void stopMonitoringConnection() {
        unregisterReceiver(mConnectionReceiver);
    }

    private final class WifiConnectionMonitor extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent in) {
            String action = in.getAction();
            if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
                NetworkInfo networkInfo = in
                    .getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
                d(networkInfo + "");
                if (networkInfo.isConnected()) {
                    d("Wifi is connected!");
                    NetworkService.downTheLatch(); // HERE THE SERVICE IS WOKEN!
                }
            }
        }
    }
}

ところで、すべてのトリックwakeWifiUp()が必要なわけではなく (私の場合) !_wifiManager.isWifiEnabled()、ユーザーが有効にした場合にのみネットを使用するため、すべてを省略することができます。完全を期すために残します。

要約:私のシナリオでは、あなたの方法は十分ではありませんでした(Javaに正しく変換し、ばかげた間違いを犯さなかった場合、常に適用されます-私のも参照してくださいconnection())。接続が確立されるのを待つ必要がありましたが、その後はすべて問題ありませんでした。あなたがそれをどのように正確に使用していたのかまだわかりません-私がそうするなら、違いはあなたがずっとwifiロックを保持していたということかもしれません

HTC Nexus 1、2.3.7、Cyanogen mod (撮影しないでください。テスト用に提供されています)。

お知らせします

于 2013-11-14T03:01:46.933 に答える
0

その地域で2度目の周回をしました。上記のソリューションは、認定済みのすべてのデバイスで機能しましたが、不要な呼び出しが多すぎました。さらに、ソリューションが機能しない新しいデバイスを入手しました。これははるかに良い解決策です:

間隔ごとにこのコードが呼び出されます

NetworkInfo wifiInfo = _androidConnectivityMgr.GetNetworkInfo(ConnectivityType.Wifi);
if (!wifiInfo.IsConnectedOrConnecting)
{
    // Need to make sure the CPU does not go to sleep before the following async calls are finished
    _wifiScanWakeLock.Acquire();

    // Do not wait for the OS to initiate a reconnect to a Wi-Fi router
    _wifiManager.StartScan();
}
  • _wifiScanWakeLock は、部分的な非参照カウントの WakeLock であり、OnDestroy で Dispose します。

Wi-Fiスキャンが終了したら

private void OnWifiScanResultsReceived(Intent result)
{
    NetworkInfo wifiInfo = _androidConnectivityMgr.GetNetworkInfo(ConnectivityType.Wifi);
    if (!wifiInfo.IsConnectedOrConnecting)
    {
        Dictionary<string, int> savedNetworks = new Dictionary<string, int>();
        foreach (WifiConfiguration config in _wifiManager.ConfiguredNetworks)
        {
            string escapedSsid = Regex.Replace(config.Ssid, "^\"|\"$", String.Empty);
            savedNetworks[escapedSsid] = config.NetworkId;
        }

        foreach (ScanResult ap in _wifiManager.ScanResults)
        {
            int networkId;
            if (savedNetworks.TryGetValue(ap.Ssid, out networkId))
            {
                savedNetworks.Remove(ap.Ssid);
                _wifiManager.EnableNetwork(networkId, false);
            }
        }
    }
    _wifiScanWakeLock.Release();
}
  • WifiConfiguration の BSSID は常に null であり、ScanResult の BSSID と一意に比較するために使用することはできません
  • これはコア コードです。明らかに、2 つの同一の SSID のケースやその他の最適化について心配する必要があります。
于 2014-03-18T15:52:12.800 に答える