Android 在广播接收器中获取唤醒锁并在服务中释放它的正确模式

Android 在广播接收器中获取唤醒锁并在服务中释放它的正确模式,android,service,broadcastreceiver,wakelock,Android,Service,Broadcastreceiver,Wakelock,即使经过大量研究,我仍然不能完全确定我如何为广播接收器启动的服务实现唤醒锁的方法是否正确-尽管它似乎工作正常。首先,广播接收器从AlarmManager的API文档中获取警报发送给它的意图: 如果报警接收器调用Context.startService(),则可能 手机将在请求的服务启动前休眠。到 为了防止这种情况,您的广播接收器和服务需要 实施单独的唤醒锁定策略,以确保手机 继续运行,直到服务可用 因此,在onReceive()中,我会: Intent serviceIntent = n

即使经过大量研究,我仍然不能完全确定我如何为
广播接收器启动的
服务
实现
唤醒锁
的方法是否正确-尽管它似乎工作正常。首先,广播接收器从
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
,我是否需要进行此检查

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

其实回答起来有点棘手。查看
PowerManager
PowerManager.WakeLock
的源代码
WakeLock.acquire()
WakeLock.acquirelock()
方法如下所示

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()
之后立即调用
ishelld()
,结果将始终为
true

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


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

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

使用一个自定义类的单例实例,然后您可以从一个调用到另一个调用获取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;
   }
}

在处理“wakelock”对象的应用程序中,您可以像访问

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

所有这些工作都可以由一个名为

的助手和本地类完成,我认为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&receiver在不同的进程中运行,这种方法也能正常工作。当文档不够时,我也应该看看Android的源代码(我认为这是非常罕见的)-谢谢!您建议在调用
release()
之前调用
ishelld()
,以防将来版本的Android改变行为。你不认为如果未来的版本改变了它的行为,它(出于向后兼容性的原因,并为了防止许多市场应用程序崩溃)需要正确处理对
release()
的调用,而这些调用实际上没有锁?不仅如此,
PowerManager
源代码中的示例代码以及文档中的示例代码都明确无条件地调用了
acquire()
release()
。另外,由于
WakeLock
s是引用计数的,因此要求……调用
release()
的次数与调用
acquire()
的次数相同。当然,在调用
release()
之前一定要先调用
isHeld()
,但我不认为这是“最佳实践”。没错,静态单例在Android中似乎很常见。与使用静态成员相比,我不确定其优缺点,同时也考虑了单元测试。这主要是因为它是大多数OO语言中普遍使用的一种被广泛接受的设计模式。。。但是,如果经典静态适合您,您可以自由使用它:)我真的不同意-我认为静态单例有点反模式,特别是在JEE中(我主要使用它),只有在没有其他方法的情况下,我才会使用它。但这对任何静态的东西都适用,所以我尽量避免它。顺便说一句,单例的首选模式是枚举模式(参见Joshua Blochs的“有效Java”)@Torsten:你是对的,这就是为什么该模式的大多数实现使用的不是简单的静态模式,而是“线程”单例或“上下文类加载器”singleton等…这个问题值得关注。自API级别26.0以来,已弃用。0@EmmanuelDevaux:它适用于问题有意义的所有API级别。