Wakelock и wifilock не работают

Я прочитал много руководств и сообщений здесь на SO относительно использования WakeLock и WifiLock, но до сих пор не нашел решения моей проблемы.

Я пишу приложение, которое при запуске имеет единственный эффект создания и запуска службы (переднего плана). Эта служба запускает два потока, которые являются прослушивателем широковещательной передачи UDP (с использованием java.io) и сервером TCP (с использованием java.nio). В onCreate службы я приобретаю wakelock и wifilock и отпускаю их в onDestroy.

Пока телефон не спит, все работает нормально, но когда дисплей выключается, широковещательный приемник UDP перестает принимать широковещательные сообщения и не будет их получать, пока я снова не включу дисплей. Практически замки вообще не работают и разницы их ставить нет ... где я не прав? Я уверен, что где-то делаю что-то глупое, но сам не могу этого найти.

Вот код:

Вот что делает Activity:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    onlyStartService = true;
}@Override
protected void onStart() {
    super.onStart();
    Intent bIntent = new Intent(this, FatLinkService.class);
    getApplicationContext().startService(bIntent);
    getApplicationContext().bindService(bIntent, flConnection, BIND_AUTO_CREATE);
} 
// service connection to bind idle service
private ServiceConnection flConnection = new ServiceConnection() {
    public void onServiceConnected(ComponentName className, IBinder binder) {
       linkService.MyLinkBinder flBinder = (LinkService.MylinkBinder) binder;
       flServiceInstance = flBinder.getService();
       if (onlyStartService) {
           condLog("Service bound and finishing activity...");
           finish();
       }
    }
    public void onServiceDisconnected(ComponentName className) {
        flServiceInstance = null;
    }
}; 
@Override
protected void onStop() {
    super.onStop();

    if (fatFinish) {
        Intent bIntent = new Intent(this, FatLinkService.class);
        flServiceInstance.stopServices();
        flServiceInstance.stopForeground(true);
        flServiceInstance.stopService(bIntent);
        condLog("Service stop and unbound");
        flServiceInstance = null;
    }
    getApplicationContext().unbindService(flConnection);
}

Вот как это сервис:

public class LinkService extends Service {
    InetAddress iaIpAddr, iaNetMask, iaBroadcast;
    private final IBinder mBinder = new MyLinkBinder();
    private linklistenBroadcast flBroadServer = null;
    private linkTCPServer flTCPServer = null;
    private linkUDPClient flBroadClient = null;
    List<String> tokens = new ArrayList<String>();
    private PowerManager.WakeLock wakeLock;
    private WifiManager.WifiLock wifiLock;

public class MylLinkBinder extends Binder {
    lLinkService getService() { return LinkService.this; }
}

@Override
public IBinder onBind(Intent intent) {
    return mBinder;
}

@Override
public void onCreate() {
    super.onCreate();
    getLocks();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId)
{        
    instantiateServices();
    // notifies presence to other fat devices
    condLog("Service notifying fat presence...");
    flBroadClient = new LinkUDPClient();
    flBroadClient.startSending(LinkProtocolConstants.BRCMD_PRESENCE + String.valueOf(LinkProtocolConstants.tcpPort), iaBroadcast, LinkProtocolConstants.brPort);
    return START_STICKY;
}

public void getLocks() {
    // acquire a WakeLock to keep the CPU running
    condLog("Acquiring power lock");
    WifiManager wm = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
    wifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL , "MyWifiLock");
    wifiLock.acquire();
    PowerManager pm = (PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE);
    wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock");
    wakeLock.acquire();
}

public void stopServices() {
    if (flTCPServer != null)
        flTCPServer.stopServer();
    if (flBroadServer != null)
        flBroadServer.stopSelf();
}

private void instantiateServices() {
    populateAddresses(); // just obtain iaIpAddr  
    if (flTCPServer == null) {
        condLog("Instantiating TCP server");
        flTCPServer = new LinkTCPServer(iaIpAddr, FatLinkProtocolConstants.tcpPort);
        flTCPServer.execute();
    }
    if (flBroadServer == null) {
        condLog("Instantiating UDP broadcast server");
        Intent notifyIntent = new Intent(this, LinkMain.class); // this is the main Activity class
        notifyIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
        notifyIntent.setAction("FROM_NOTIFICATION");
        PendingIntent notifyPIntent = PendingIntent.getActivity(this, 0, notifyIntent, 0);

        Notification fixNotification = new Notification.Builder(getApplicationContext())
                .setContentTitle("Link")
                .setSmallIcon(R.mipmap.imgLink)
                .setContentIntent(notifyPIntent)
                .build();
        startForeground(1234, fixNotification);
        flBroadServer = new LinklistenBroadcast();
        flBroadServer.start();
    }
}

private final class LinklistenBroadcast extends Thread {
    private boolean bStopSelf = false;
    DatagramSocket socket;
    public void stopSelf() {
        bStopSelf = true;
        socket.close();
    }

    @Override
    public void run() {
        condLog( "Listening broadcast thread started");
        bStopSelf = false;
        try {
        //Keep a socket open to listen to all the UDP trafic that is destinated for this port
        socket = new DatagramSocket(null);
        socket.setReuseAddress(true);
        socket.setSoTimeout(LinkGeneric.BR_SOTIMEOUT_MILS);
        socket.setBroadcast(true);
        socket.bind(new InetSocketAddress(InetAddress.getByName("0.0.0.0"), FatLinkProtocolConstants.brPort));
        while (true) {
                condLog("Ready to receive broadcast packets...");
                //Receive a packet
                byte[] recvBuf = new byte[1500];

                DatagramPacket packet = new DatagramPacket(recvBuf, recvBuf.length);
                try {
                    socket.receive(packet);
                } catch (InterruptedIOException sException) {
                    condLog(sockExcept.toString());
                    break;
                } catch (SocketException sockExcept) {
                   condLog(sockExcept.toString());
                }
                if (bStopSelf) {
                    condLog("Broadcast server stopped...");
                    break;
                }
                int len = packet.getLength();
                String datarecvd = new String(packet.getData()).trim();
                //datarecvd = datarecvd.substring(0, len);
                //Packet received
                String message = new String(packet.getData()).trim();
                condLog("<<< broadcast packet received from: " + packet.getAddress().getHostAddress() + " on port: " + packet.getPort() + ", message: " + message);
                if (packet.getAddress().equals(iaIpAddr)) {
                    condLog("Ooops, it's me! discarding packet...");
                    continue;
                }
                else
                    condLog("<<< Packet received; data size: " + len + " bytes, data: " +  datarecvd);

                //See if the packet holds the right command (message)

                // protocol decode
                // here do some tuff
        } catch (IOException ex) {
            condLog(ex.toString());
        }

        if (socket.isBound()) {
            condLog( "Closing socket");
            socket.close();
        }
        condLog( "UDP server thread end.");
        flTCPServer = null;
        flBroadServer = null;
    }

    public boolean isThreadRunning() {
        return !bStopSelf;
    };
}

// Utility functions
public boolean checkBroadcastConnection (DatagramSocket socket, int timeOutcycles) {
    int tries = 0;
    while (!socket.isConnected()) {
        tries++;
        if (tries >= timeOutcycles)
            return false;
    }
    return true;
}

@Override
public void onDestroy() {
    super.onDestroy();
    if (wakeLock != null) {
        if (wakeLock.isHeld()) {
            wakeLock.release();                
        }
    }
    if (wifiLock != null) {
        if (wifiLock.isHeld()) {
            wifiLock.release();
        }
    }
}

}

И, наконец, вот манифест:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="xxxxxx.ink" >
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/imgLinkmascotte"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".LinkMain"
            android:label="@string/app_name"
            android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service android:name=".LinkService" />
    </application>
</manifest>
  • Я прочитал это и сомневаюсь, что проблема То же самое, но на самом деле это происходит со мной на всех телефонах, которые я пробовал (Galaxy Tab 7.1, Galaxy tag 10, Galaxy SIII, Galaxy Note 3 Neo, Galaxy SIII mini) с версиями Android от 4.0 до 4.4.

  • Я уже пробовал решение, опубликованное здесь, но все изменилось (опять же ... это немного расстраивает).

  • Я даже пытался установить для параметра «Сохранять Wi-Fi в режиме ожидания» значение «Всегда» на всех телефонах, которые я пробовал, но все равно ничего.

  • Я изучил класс WakeFulIntentService, который должен работать, как все говорят, но я не вижу каких-либо существенных отличий в моем коде.

Я очень надеюсь, что кто-то может мне помочь, я действительно застрял в этом с прошлой недели.

РЕДАКТИРОВАТЬ: после ответа вакаслама я проверил устройство с опцией «Оптимизация Wi-Fi» в настройках Wifi-Advaced, и оно действительно работает, как только я отключил эту опцию. Итак, теперь возникает проблема: могу ли я отключить оптимизацию Wi-Fi на устройствах, на которых эта опция не отображается в расширенном меню? как я писал ниже в своем комментарии, похоже, это не связано с выпуском Android, так как у меня есть два устройства (оба Samsung) с 4.4.2, и они не показывают эту опцию.

Новое РЕДАКТИРОВАНИЕ: из отредактированного ответа вакаслама я попытался добавить многоадресную передачу в свою службу, но снова все изменилось. Это уже раздражает, вряд ли есть что-то простое и понятное, что можно сделать с андроидом.

большое спасибо C.


person Cristiano Zambon    schedule 20.02.2015    source источник
comment
Какая у вас версия Android и какое устройство?   -  person waqaslam    schedule 20.02.2015
comment
Спасибо за ваш комментарий. Я написал их в конце поста. Точнее, я пробовал с 4.0.x (сейчас точно не помню), 4.1.2 и 4.4.2.   -  person Cristiano Zambon    schedule 20.02.2015
comment
Я столкнулся с той же досадной проблемой. Вы нашли решение?   -  person Srikanth    schedule 03.06.2015
comment
Ни за что. С трансляцией у меня просто не получается.   -  person Cristiano Zambon    schedule 11.06.2015


Ответы (3)


Думаю, проблема не в WakeLocks, а в настройках Wi-Fi.

В более новых версиях Android есть дополнительный параметр в Настройки -> Wi-Fi -> Advanced под названием Wi-Fi optimization , который (если включен) отключает все низкоприоритетные коммуникации (например, прослушивание UDP-трансляций), когда дисплей выключен.

Отключение этой опции должно позволить вашему устройству прослушивать UDP-трансляции, даже когда дисплей выключен.

Оптимизация Wi-Fi


Вы также можете использовать WifiManager.MulticastLock для приобретения Wi-Fi. замок, который должен слушать эти специальные передачи при выключенном экране.

WifiManager wifi = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
MulticastLock lock = wifi.createMulticastLock("lockWiFiMulticast");
lock.setReferenceCounted(false);
lock.acquire();

и когда закончите с блокировкой, позвоните:

lock.release();

Кроме того, не забудьте добавить в свой манифест следующее разрешение:

<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>

Для получения дополнительной информации вы можете прочитать это.

person waqaslam    schedule 20.02.2015
comment
Спасибо за ответ. В моем Note 3 Neo с Android 4.4.2 этой опции нет, у меня включен только Wi-Fi во время сна, который я поставил на Always. - person Cristiano Zambon; 20.02.2015
comment
А как насчет тех устройств, у которых есть эта опция, вы можете их примерить? Несколько месяцев назад я также столкнулся с аналогичной проблемой, и отключение этой опции помогло мне решить ее. - person waqaslam; 20.02.2015
comment
да, только что попробовал One plus One с android 4.4.2 и этой опцией. Снятие отметки с этой опции действительно работает. Итак, новый вопрос: как позволить устройствам работать без этой опции? - person Cristiano Zambon; 20.02.2015
comment
Технически устройства, у которых нет этой опции, по умолчанию должны работать нормально. Но если они этого не делают, то это классический случай, когда производитель устройства (с пользовательскими ромами) ограничил эту функцию, чтобы компенсировать производительность (например, время автономной работы) и / или безопасность. Однако вы все равно можете получить это, используя MulticastLock. Смотрите мой обновленный ответ. - person waqaslam; 20.02.2015
comment
Еще раз большое спасибо за ваш комментарий и ответ. На этот раз я действительно надеялся, что это было решение, но, к сожалению, нет, служба все еще не отвечает, когда дисплей гаснет. - person Cristiano Zambon; 20.02.2015

Пишу это просто для того, чтобы отметить сообщение как ответ. Я наконец понял, что решения нет, или, по крайней мере, нет решения, хорошо работающего "на всех" телефонах со "всем" дистрибутивом Android (или, по крайней мере, от JB).

Я решил свою личную проблему, просто пересмотрев концепцию своего приложения, учитывая, что: - Отправка UDP-вещания всегда работает даже в режиме ожидания - TCP-серверы не зависят от оптимизации WiFi.

спасибо всем за помощь и предложение C.

person Cristiano Zambon    schedule 23.02.2015
comment
Отправка UDP широковещательной рассылки всегда работает даже в режиме ожидания ..., вы имели в виду TCP? - person waqaslam; 23.02.2015
comment
Моя служба ожидает каждые 2 секунды, отправляя широковещательный пакет UDP, и это работает в режиме ожидания с отключенным дисплеем. Вы действительно можете отправлять / получать широковещательную рассылку по TCP? - person Cristiano Zambon; 23.02.2015
comment
Нет, вы не можете этого сделать с TCP. Но в качестве альтернативы я бы посоветовал вам выбрать модель клиент-сервер, в которой ваш клиент (мобильный) зарегистрирован на сервере с использованием TCP-сокета. Или, что более предпочтительно, ищите решения на основе XMPP, которые являются более сложными и надежными (но немного сложными в реализации) с точки зрения обработки сообщений в сети. - person waqaslam; 23.02.2015
comment
Спасибо вакаслам за ваши ценные предложения. К сожалению, в моем случае модель клиент / сервер неприменима, или лучше, применима, и я использую ее, но только после того, как широковещательная передача используется для уведомления о присутствии для всех узлов сети. - person Cristiano Zambon; 23.02.2015
comment
@CristianoZambon У меня была такая же проблема. Сначала я использовал многоадресную рассылку. Я переключился на трансляцию, чтобы попробовать, но я тестировал, по крайней мере, на некоторых телефонах, транслирующих, это тоже ненадежно. Только одноадресная передача TCP надежна на всех устройствах Android. - person greywolf82; 01.01.2016

Возможно, причина в этом.

Начиная с Android 6.0 (уровень API 23), Android представляет две функции энергосбережения, которые продлевают срок службы батареи для пользователей, управляя поведением приложений, когда устройство не подключено к источнику питания. Doze снижает расход заряда батареи, откладывая фоновую активность ЦП и сети для приложений, когда устройство не используется в течение длительного времени. Режим ожидания приложений откладывает фоновую сетевую активность для приложений, с которыми пользователь в последнее время не взаимодействовал.

person user434    schedule 13.11.2018