Правильный шаблон для получения WakeLock в BroadcastReceiver и выпуска его в Сервисе - PullRequest
19 голосов
/ 25 августа 2011

Даже после долгих исследований я все еще не до конца уверен, что способ, которым я реализую 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 является пакетом private, static и volatile.

В чем я не уверен, так это в чекеиспользуя isHeld() - действительно ли он говорит мне, если WakeLock получен или нет, и мне вообще нужно делать эту проверку?

Ответы [ 4 ]

4 голосов
/ 25 августа 2011

Управление 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();
4 голосов
/ 25 августа 2011

В чем я не уверен, так это в чеке с использованием 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'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 ничего не делает. Сразу после попытки / улова mHeld устанавливается true независимо.

Короче говоря, если вы позвоните isHeld() сразу после acquire(), результат всегда будет true.

Дальнейшее изучение источника для PowerManager.WakeLock показывает аналогичное поведение для release(), который вызывает release(int flags), где элемент mHeld всегда установлен на false независимо от того, что происходит.

В заключение я хотел бы предложить, чтобы проверка isHeld() всегда была хорошей идеей, как и в случае, если более поздние версии Android изменят это поведение методов WakeLock.

3 голосов
/ 21 января 2016

Вся эта работа может быть выполнена вспомогательным и нативным классом с именем WakefulBroadcastReceiver

0 голосов
/ 07 августа 2016

Я думаю, что 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);
    }
}

Этот метод работаетправильно, даже если служба и приемник работают в другом процессе.

...