在 BroadcastReceiver 中获取 WakeLock 并在 Service 中释放它的正确模式

发布于 2024-12-01 12:32:03 字数 1300 浏览 0 评论 0原文

即使经过大量研究,我仍然不能完全确定为 BroadcastReceiver 启动的 Service 实现 WakeLock 的方式是否正确- 尽管它看起来工作正常。广播接收器获取从警报发送给它的意图,因此首先从 AlarmManager 的 API 文档开始:

如果您的警报接收器调用了 Context.startService(),则有可能 在启动请求的服务之前手机将进入睡眠状态。到 防止这种情况发生,您的广播接收器和服务将需要 实施单独的唤醒锁定策略以确保手机 继续运行,直到服务可用。

因此,在 onReceive() 中我这样做:

    Intent serviceIntent = new Intent(context, SomeService.class);
    context.startService(serviceIntent);

    if(SomeService.wakeLock == null) {
        PowerManager powerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
        SomeService.wakeLock = powerManager.newWakeLock(
                PowerManager.PARTIAL_WAKE_LOCK, 
                SomeService.WAKE_LOCK_TAG);
    }
    if(! SomeService.wakeLock.isHeld()) {
        SomeService.wakeLock.acquire();
    }

在服务中我这样做:

    try {
        // Do some work
    } finally {
        if(wakeLock != null) {
            if(wakeLock.isHeld()) {
                wakeLock.release();
            }
            wakeLock = null;
        }
    }

SomeService.wakeLock 字段是包私有的、静态的和易失性的。

我不确定的是使用 isHeld() 进行的检查 - 它是否真的告诉我是否获取了 WakeLock,以及我是否需要进行此检查?

Even after a lot of research I am still not completely sure if the way how I implement a WakeLock for a Service started by a BroadcastReceiver is correct - even though it seems to work fine. The broadcast receiver gets intents sent to it from an alarm, so to start with, from the API docs of AlarmManager:

If your alarm receiver called Context.startService(), it is possible
that the phone will sleep before the requested service is launched. To
prevent this, your BroadcastReceiver and Service will need to
implement a separate wake lock policy to ensure that the phone
continues running until the service becomes available.

So, in onReceive() I do:

    Intent serviceIntent = new Intent(context, SomeService.class);
    context.startService(serviceIntent);

    if(SomeService.wakeLock == null) {
        PowerManager powerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
        SomeService.wakeLock = powerManager.newWakeLock(
                PowerManager.PARTIAL_WAKE_LOCK, 
                SomeService.WAKE_LOCK_TAG);
    }
    if(! SomeService.wakeLock.isHeld()) {
        SomeService.wakeLock.acquire();
    }

and in the service I do:

    try {
        // Do some work
    } finally {
        if(wakeLock != null) {
            if(wakeLock.isHeld()) {
                wakeLock.release();
            }
            wakeLock = null;
        }
    }

The SomeService.wakeLockfield is package private, static and volatile.

What I am unsure about is the check using isHeld() - does it really tell me if a WakeLock is acquired or not, and do I need to do this check at all?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

撩动你心 2024-12-08 12:32:03

我不确定的是使用 isHeld() 进行的检查 - 它是否真的告诉我是否获取了 WakeLock,以及我是否需要这样做检查一下吗?

其实回答起来有点棘手。查看 PowerManagerPowerManager.WakeLock 的源代码 此处 WakeLock.acquire()WakeLock.acquireLocked()方法如下...

public void acquire(long timeout) {
    synchronized (mToken) {
        acquireLocked();
        mHandler.postDelayed(mReleaser, timeout);
    }
}

private void acquireLocked() {
    if (!mRefCounted || mCount++ == 0) {
        // Do this even if the wake lock is already thought to be held (mHeld == true)
        // because non-reference counted wake locks are not always properly released.
        // For example, the keyguard's wake lock might be forcibly released by the
        // power manager without the keyguard knowing.  A subsequent call to acquire
        // should immediately acquire the wake lock once again despite never having
        // been explicitly released by the keyguard.
        mHandler.removeCallbacks(mReleaser);
        try {
            mService.acquireWakeLock(mToken, mFlags, mTag, mWorkSource);
        } catch (RemoteException e) {
        }
        mHeld = true;
    }
}

...mService 是一个 IPowerManager 接口,并且它的源代码不可用,因此很难判断什么时候可能会出现问题,也可能不会出现问题尝试调用acquireWakeLock(...)

无论如何,唯一可以捕获的异常是 RemoteException 并且 catch 块不执行任何操作。在 try/catch 之后,mHeld 立即被设置为 true

简而言之,如果您在 acquire() 之后立即调用 isHeld(),结果将始终为 true

进一步查看 PowerManager.WakeLock 的源代码,可以看到 release() 的类似行为,它调用 release(int flags),其中 mHeld无论发生什么情况, 成员始终设置为 false

总之,我建议检查 isHeld() 始终是一个好主意,作为最佳实践,以防更高版本的 Android 更改 WakeLock 方法的这种行为。

What I am unsure about is the check using isHeld() - does it really tell me if a WakeLock is acquired or not, and do I need to do this check at all?

Actually slightly tricky to answer. Looking at the source for PowerManager and PowerManager.WakeLock here the WakeLock.acquire() and WakeLock.acquireLocked() methods are as follows...

public void acquire(long timeout) {
    synchronized (mToken) {
        acquireLocked();
        mHandler.postDelayed(mReleaser, timeout);
    }
}

private void acquireLocked() {
    if (!mRefCounted || mCount++ == 0) {
        // Do this even if the wake lock is already thought to be held (mHeld == true)
        // because non-reference counted wake locks are not always properly released.
        // For example, the keyguard's wake lock might be forcibly released by the
        // power manager without the keyguard knowing.  A subsequent call to acquire
        // should immediately acquire the wake lock once again despite never having
        // been explicitly released by the keyguard.
        mHandler.removeCallbacks(mReleaser);
        try {
            mService.acquireWakeLock(mToken, mFlags, mTag, mWorkSource);
        } catch (RemoteException e) {
        }
        mHeld = true;
    }
}

...mService is an IPowerManager interface and the source for it isn't available so it's hard to tell what may or may not go wrong when attempting to call acquireWakeLock(...).

In any case, the only exception that can be caught is RemoteException and the catch block does nothing. Immediately after the try/catch, mHeld is set true regardless.

In short, if you call isHeld() immediately after acquire() the result will always be true.

Looking further into the source for PowerManager.WakeLock shows similar behaviour for release() which calls release(int flags) where the mHeld member is always set to false regardless of what happens.

In conclusion I'd suggest it is always a good idea to check isHeld() just as a best practice in case later versions of Android change this behaviour of the WakeLock methods.

吝吻 2024-12-08 12:32:03

在单例中管理您的wakeLock(通过所有上下文和对象访问的唯一实例)

使用自定义类的单例实例,然后您可以从调用到调用中获取唤醒锁对象引用,

这里有一个单例示例

class MyData {
   private static MyData mMydata= null; // unique reference ( singleton objet container)

   private PowerManager.Wakelock myobject = null; // inside the unique object container we have the unique working object to be use  by the application
   // can't make instance from outside... we want to have single instance
   // we want that outside use method "getInstance" to be able to use the object
   private MyData() {
   }

   // retrieve and/or create new unique instance
   public static MyData getInstance() {
     if (mMydata ==  null) mMyData = new MyData();
     return   mMyData;
   }

   // Works with your memory stored object
   // get...
   public PowerManager.WakeLock getMyWakelock() {
   return myobject;
   }
   // set ...
   public void setMyWakeLock(PowerManager.WakeLock obj) {
    myobject = obj;
   }
}

在您的应用程序中处理“唤醒锁”对象,您可以像这样访问它

// set a created wakelock
MyData.getInstance().setMyWakeLock(wl);
// get the saved wakelock object
PowerManager.WakeLock obj =  MyData.getInstance().getMyWakeLock();

Manage you wakeLock inside a singleton (unique instance accessible through all your context and object)

Use a singleton instance of a custom class, then you may get wakelock object reference from call to call ,

here an singleton example

class MyData {
   private static MyData mMydata= null; // unique reference ( singleton objet container)

   private PowerManager.Wakelock myobject = null; // inside the unique object container we have the unique working object to be use  by the application
   // can't make instance from outside... we want to have single instance
   // we want that outside use method "getInstance" to be able to use the object
   private MyData() {
   }

   // retrieve and/or create new unique instance
   public static MyData getInstance() {
     if (mMydata ==  null) mMyData = new MyData();
     return   mMyData;
   }

   // Works with your memory stored object
   // get...
   public PowerManager.WakeLock getMyWakelock() {
   return myobject;
   }
   // set ...
   public void setMyWakeLock(PowerManager.WakeLock obj) {
    myobject = obj;
   }
}

in your application to handle your "wakelock" object your may access it like

// set a created wakelock
MyData.getInstance().setMyWakeLock(wl);
// get the saved wakelock object
PowerManager.WakeLock obj =  MyData.getInstance().getMyWakeLock();
赴月观长安 2024-12-08 12:32:03

所有这些工作都可以通过名为 WakefulBroadcastReceiver

All this job can be done by a helper and native class called WakefulBroadcastReceiver

謸气贵蔟 2024-12-08 12:32:03

可能是更好的方法

我认为 android.os.Messenger对于接收者来说

public class MessengerReceiver extends BroadcastReceiver {


    private static final String TAG = "MessengerReceiver";

    private final MessengerHandler mHandler = new MessengerHandler();

    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO: This method is called when the BroadcastReceiver is receiving
        // an Intent broadcast.
        mHandler.mWakeLock = ((PowerManager)context.getSystemService(Service.POWER_SERVICE)).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "myreceiver");
        mHandler.mWakeLock.acquire();
        Log.e(TAG, "onReceive:: mHandler.mWakeLock=" + mHandler.mWakeLock + ", intent=" + intent + ", this=" + this);
        context.startService(new Intent(context, MessengerService.class).putExtra("messenger", new Messenger(mHandler)));
    }

    static class MessengerHandler extends Handler {

        WakeLock mWakeLock;
        @Override
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            if(mWakeLock != null){
                mWakeLock.release();
                Log.e(TAG, "handleMessage:mWakeLock=" + mWakeLock);
            }
            super.handleMessage(msg);
        }

    }
}

:对于服务:

public class MessengerService extends Service {
    private static final String TAG = "MessengerService";
    public MessengerService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // TODO Auto-generated method stub      
        Log.e(TAG, "onStartCommand:: intent=" + intent);
        final Messenger messenger = intent.getParcelableExtra("messenger");
        try {
            messenger.send(Message.obtain());
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return super.onStartCommand(intent, flags, startId);
    }
}

即使 service & ,此方法也可以正常工作。接收器在不同的进程中运行。

I think android.os.Messenger may be a better way

for the receiver:

public class MessengerReceiver extends BroadcastReceiver {


    private static final String TAG = "MessengerReceiver";

    private final MessengerHandler mHandler = new MessengerHandler();

    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO: This method is called when the BroadcastReceiver is receiving
        // an Intent broadcast.
        mHandler.mWakeLock = ((PowerManager)context.getSystemService(Service.POWER_SERVICE)).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "myreceiver");
        mHandler.mWakeLock.acquire();
        Log.e(TAG, "onReceive:: mHandler.mWakeLock=" + mHandler.mWakeLock + ", intent=" + intent + ", this=" + this);
        context.startService(new Intent(context, MessengerService.class).putExtra("messenger", new Messenger(mHandler)));
    }

    static class MessengerHandler extends Handler {

        WakeLock mWakeLock;
        @Override
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            if(mWakeLock != null){
                mWakeLock.release();
                Log.e(TAG, "handleMessage:mWakeLock=" + mWakeLock);
            }
            super.handleMessage(msg);
        }

    }
}

for the service:

public class MessengerService extends Service {
    private static final String TAG = "MessengerService";
    public MessengerService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // TODO Auto-generated method stub      
        Log.e(TAG, "onStartCommand:: intent=" + intent);
        final Messenger messenger = intent.getParcelableExtra("messenger");
        try {
            messenger.send(Message.obtain());
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return super.onStartCommand(intent, flags, startId);
    }
}

This method work properly even if service & receiver run in different process.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文