【问题标题】:Wakelock and wifilock not working唤醒锁和 wifilock 不工作
【发布时间】:2015-02-20 11:30:01
【问题描述】:

我在这里阅读了大量关于使用 WakeLock 和 WifiLock 的教程和帖子,但仍然没有解决我的问题。

我正在编写一个应用程序,当您启动它时,它的唯一效果是创建和启动(前台)服务。该服务运行两个线程,即一个 UDP 广播侦听器(使用 java.io)和一个 TCP 服务器(使用 java.nio)。在服务的 onCreate 中,我获取了一个唤醒锁和一个 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>
  • 我已阅读 this,我怀疑问题是否相同,但它实际上发生在我尝试过的所有手机上(galaxy Tab 7.1、galaxy tag 10、Galaxy SIII、Galaxy Note 3 Neo, Galaxy SIII mini) 安卓版本从 4.0 到 4.4。

  • 我已经尝试过here 发布的解决方案,但一切都发生了变化(再次……有点令人沮丧)。

  • 我什至尝试在我尝试过的所有手机中将“保持 wifi 选项处于待机状态”设置为“始终”,但仍然没有。

  • 我研究了 WakeFulIntentService 类,它应该像每个人所说的那样工作,但我看不出我的代码有任何显着不同。

我真的希望有人能帮助我,从上周开始我就一直坚持这个。

编辑:按照 waqaslam 的回答,我检查了在 Wifi-Advaced 设置中具有“Wifi 优化”选项的设备,一旦我取消该选项,它实际上就可以工作。所以,现在,问题变成了:我可以在高级菜单中没有显示该选项的设备中禁用 Wifi 优化吗?正如我在下面的评论中所写,这似乎与 android 版本无关,因为我有两台设备(均为三星)4.4.2 并且它们没有显示该选项。

新编辑:从编辑的 waqaslam 答案中,我尝试将多播锁添加到我的服务中,但再次发生任何变化。这越来越烦人了,android 几乎没有什么简单明了的东西。

非常感谢 C.

【问题讨论】:

  • 你的安卓版本和设备是什么?
  • 感谢您的评论。我把它们写在了文章的最后。更准确地说,我已经尝试过 4.0.x(现在不记得了)、4.1.2 和 4.4.2。
  • 我也遇到了同样烦人的问题。你找到解决办法了吗?
  • 没办法。广播对我来说根本不可能。

标签: java android android-wifi wakelock


【解决方案1】:

我认为问题不在于唤醒锁,而在于 Wi-Fi 设置。

在较新版本的 android 中,Settings -> Wi-Fi -> Advanced中有一个附加设置> 调用Wi-Fi optimization,它(如果打开)在显示器关闭时禁用所有低优先级通信(如监听 UDP 广播)。

禁用该选项应允许您的设备即使在显示屏关闭时也能监听 UDP 广播。


您也可以使用WifiManager.MulticastLock 来获取 WiFi 锁,以便在屏幕关闭时收听这些特殊广播。

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"/>

更多信息,您可以阅读this

【讨论】:

  • 感谢您的回答。在我的带有 Android 4.4.2 的 Note 3 Neo 中没有这个选项,我只有“在睡眠期间保持 wifi 开启”,我把它设置为“始终”。
  • 那些有这个选项的设备怎么样,你可以试一下吗?几个月前我也遇到了类似的问题,禁用该选项帮助我解决了这个问题。
  • 是的,刚刚尝试了带有 android 4.4.2 的一加一并且该选项存在。取消选中此选项它确实有效。那么,新的问题是,如何让没有此选项的设备也能正常工作?
  • 从技术上讲,没有此选项的设备在默认情况下应该可以正常工作。但如果他们不这样做,那么这是设备制造商(使用自定义 rom)限制此功能以补偿性能(如电池寿命)和/或安全性的经典案例。但是,您仍然可以通过使用MulticastLock 来获得它。请参阅我的更新答案。
  • 再次非常感谢您的评论和asnwer。这次我真的希望这是解决方案,但不幸的是没有,显示关闭时服务仍然没有响应。
【解决方案2】:

写这个只是为了将帖子标记为已回答。我终于意识到没有解决方案,或者至少没有解决方案适用于“所有”具有“所有”android 发行版(或至少来自 JB)的手机。

我已经解决了我的个人问题,只是重新审视了我的应用程序的概念,考虑到: - 即使在空闲模式下,发送 UDP 广播也始终有效 - TCP 服务器不受 WiFi 优化的影响。

感谢大家的帮助和建议 C.

【讨论】:

  • “即使在空闲模式下,发送UDP 广播也始终有效...”,您的意思是TCP
  • 我的服务每 2 秒等待一次,发送一个 UDP 广播数据包,这也在空闲模式下工作,显示也关闭。你真的可以用 TCP 发送/接收广播吗?
  • 不,你不能用 TCP 做到这一点。但作为替代方案,我建议您采用客户端-服务器模型,您的客户端(移动设备)使用 TCP 套接字向服务器注册。或者更理想的是,寻找基于 XMPP 的解决方案,这些解决方案在网络上的消息处理方面更加复杂和可靠(但实现有点复杂)。
  • 感谢 waqaslam 提出的宝贵建议。不幸的是,在我的情况下,客户端/服务器模型不适用,或者更好,适用,我正在使用它,但只有在广播用于通知网络的所有节点存在之后。
  • @CristianoZambon 我遇到了同样的问题。一开始我使用多播。我切换到广播只是为了尝试,但我至少在一些手机上测试过广播它也不可靠。只有 TCP 单播在所有安卓设备上都是可靠的。
【解决方案3】:

可能是this的原因。

从 Android 6.0(API 级别 23)开始,Android 引入了两个 省电功能,通过管理延长电池寿命为用户 设备未连接到电源时应用程序的行为方式。瞌睡 通过延迟后台 CPU 和网络来减少电池消耗 当设备长时间未使用时,应用程序的活动。 App Standby 会延迟应用程序的后台网络活动, 用户最近没有互动过

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多