Android:在屏幕关闭后进入覆盖区域后重新连接到Wi-Fi

时间:2013-10-03 00:06:40

标签: android xamarin.android android-wifi wifimanager

一旦设备在接入点的范围内而不打开屏幕,我一直在努力自动打开Wi-Fi。测试和找出解决方案非常令人沮丧,特别是因为不同的设备有完全不同的结果。

基础测试
在此测试期间保持屏幕关闭。该应用程序应该持有WifiLock。

  1. 走出WiFi覆盖范围并在那里停留一分钟。
  2. 走回报道。
  3. 结果: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. 是的,我们特别需要这样做,并了解电池含义。

3 个答案:

答案 0 :(得分:2)

我的情况略有不同 - 我没有开始使用 的wifi锁(我在普通的android上,所以我不得不翻译你的方法)。< / p>

屏幕关闭,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(不要射击我已经给它进行测试)。

会让你发布

答案 1 :(得分:1)

在Android 4.4 KitKat(Nexus 4)下,Alex和Mr_and_Mrs_D的方法很接近,但并不完全一致。这可能与谷歌从KitKat开始的更积极的节电政策有关。我使用了他们的方法和修改的组合。

一般的想法是,在定期WiFi检查期间,显式启动扫描,然后在扫描结果处理程序中调用reassociate()和reconnect()。此外,在NETWORK_STATE_CHANGED_ACTION回调中,检查在释放唤醒锁之前是否建立了连接。关键是要保持唤醒锁定的时间足够长,以便WiFi连接正确建立(显然不会超过必要)。

周期性无线网络检查可以解决问题:

public static void CheckWiFi() {  
    mWakeLock.acquire();
    if (!WiFi_Mgr.isWifiEnabled()) {
        WiFi_Mgr.setWifiEnabled(true);
    }
    WiFi_Mgr.startScan();

    //Set an alarm to fire after N seconds to release wake lock & shut off WiFi if no connection is available.
    // ... 
}

注册WiFi广播

//Setup WiFi connection status receiver
IntentFilter WiFiFilters = new IntentFilter();
WiFiFilters.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
WiFiFilters.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
this.registerReceiver(WiFiReceiver, WiFiFilters);

和WiFi事件处理程序

private final BroadcastReceiver WiFiReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        if(WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
            NetworkInfo netInfo = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
            // This is the magic bullet. The connection usually establishes after 
            // the scan results callback so release the wake lock here.
            if(netInfo.isConnected()) {
                mWakeLock.release();
            }
        }
        //Search the scan results for saved WiFi APs.
        else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action))
        {
            boolean foundMatch = false;
            if (!IsWiFiConnected()) {
                Map<String, Integer> savedNetworks = new HashMap<String, Integer>();
                for (WifiConfiguration config : WiFi_Mgr.getConfiguredNetworks()) {
                    String escapedSSID = config.SSID.replaceAll("\"", "");
                    savedNetworks.put(escapedSSID, config.networkId);
                }
                List<ScanResult> scanResults = WiFi_Mgr.getScanResults();
                for (ScanResult ap : scanResults) {
                    Integer networkId = savedNetworks.get(ap.SSID);
                    if (networkId != null) {
                        savedNetworks.remove(ap.SSID);
                        WiFi_Mgr.enableNetwork(networkId, false);
                        foundMatch = true;
                    }
                }
            }
            if(foundMatch) {
                WiFi_Mgr.reassociate();
                WiFi_Mgr.reconnect();
            }
            if (IsWiFiConnected())
                mWakeLock.release();
        }
    }
};

您需要声明必要的变量(例如,mWakeLock是部分非参考计数的唤醒锁; WiFi_Mgr是WifiManager的实例;等等......)。

答案 2 :(得分:0)

该地区有第二次出行。虽然上述解决方案适用于我们所有合格的设备,但是有太多的呼叫可能是不必要的。另外,我们得到了解决方案不起作用的新设备。这是一个更好的解决方案:

在每个时间间隔都会调用此代码

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中处理

完成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始终为空,不能用于与ScanResult的BSSID进行唯一比较
  • 这是核心代码,显然你不得不担心两个相同的SSID和其他优化的情况