Правильный шаблон для приобретения WakeLock в BroadcastReceiver и его выпуск в службе
Даже после многих исследований я все еще не совсем уверен, правильно ли реализуется WakeLock
для Service
, начинающийся с BroadcastReceiver
, даже если он работает нормально. Приемник вещания получает намерения, отправленные ему из тревоги, поэтому для начала из документов API AlarmManager
:
Если ваш приемник сигналов тревоги называется Context.startService(), это возможно что телефон будет спать до запуска запрошенной службы. к это позволит вашему BroadcastReceiver и Службе выполните отдельную политику блокировки слежения, чтобы убедиться, что телефон продолжает работать до тех пор, пока услуга не станет доступной.
Итак, в 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
или нет, и нужно ли вообще делать эту проверку?
Ответы
Ответ 1
То, что я не уверен, это проверка с помощью isHeld()
- действительно ли это говорит мне, что если WakeLock
приобретен или нет, и нужно ли вообще делать эту проверку?
На самом деле немного сложно ответить. Рассматривая источник PowerManager
и PowerManager.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 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
ничего не делает. Сразу после попытки/улова mHeld
устанавливается true
независимо.
Короче говоря, если вы вызываете isHeld()
сразу после acquire()
, результат всегда будет true
.
Далее в источник для PowerManager.WakeLock
показано сходное поведение для release()
, которое вызывает release(int flags)
, где член mHeld
всегда имеет значение false
независимо от того, что происходит.
В заключение я бы посоветовал, что всегда рекомендуется проверять isHeld()
как наилучшую практику, если более поздние версии Android изменят это поведение методов WakeLock
.
Ответ 2
Управление wakeLock внутри одноэлементного (уникальный экземпляр, доступный через весь ваш контекст и объект)
Используйте экземпляр singleton для настраиваемого класса, тогда вы можете получить ссылку на объект 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();
Ответ 3
Все это задание может выполняться вспомогательным и родным классом под названием WakefulBroadcastReceiver
Ответ 4
Я думаю, что 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);
}
}
Этот метод работает правильно, даже если служба и получатель работают в разных процессах.