Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save quwac/a1d7108921f224bf75ddbbc4fe1f34cf to your computer and use it in GitHub Desktop.
Save quwac/a1d7108921f224bf75ddbbc4fe1f34cf to your computer and use it in GitHub Desktop.
PauseHandler for Handler#post method

PauseHandler for Handler#post method

PauseHandler is a good solution for cancelling or saving requested message on an activities lifecycle.

However requested actions by post method cannot be cancelled or saved because original PauseHandle class is overridden handleMessage.

This modified PauseHandler is overridden dispatchMessage so the actions by post method can be cancelled or saved.

  • PauseHandler.java: PauseHandler for Handler#post Java version
  • PauseHandler.kt: PauseHandler for Handler#post Kotlin version
  • BaseFragment.java: Example of using PauseHandler.java
// ------------------------------------------------------------------------------------------
// This work is licensed under Creative Commons Attribution-ShareAlike license version 2.5.
//
// * Author: que
// * License
// * Legal code: https://creativecommons.org/licenses/by-sa/2.5/legalcode
// * Summary: https://creativecommons.org/licenses/by-sa/2.5/
// * Original work
// * Author: StackOverflow
// * URL: https://stackoverflow.com/questions/8040280/how-to-handle-handler-messages-when-activity-fragment-is-paused#
// * Changes: This work has been changed from the original work.
// ------------------------------------------------------------------------------------------
package que
import android.os.Handler
import android.os.Message
import android.support.annotation.GuardedBy
import android.support.annotation.UiThread
import java.util.LinkedList
import java.util.Queue
/**
* PauseHandler for [Handler.post].
*/
class PauseHandler : Handler {
private val mIsPauseLocker = Any()
private val mStoresMessagesLocker = Any()
@GuardedBy("mIsPauseLocker")
private var mIsPause: Boolean = false
@GuardedBy("mStoresMessagesLocker")
private val mStoresMessages: Boolean
private val mQueue = LinkedList<Message>()
private var isPause: Boolean
get() {
synchronized(mIsPauseLocker) {
return mIsPause
}
}
set(isPause) {
synchronized(mIsPauseLocker) {
mIsPause = isPause
}
}
/**
* Create an instance.
*
* @param storesMessages When `true` is set messages are saved,
* otherwise the messages are destroyed.
*/
@JvmOverloads constructor(storesMessages: Boolean = true) : super() {
mStoresMessages = storesMessages
}
/**
* Create an instance.
*
* @param callback The callback. The instance does not callback if `null` is set.
* @param storesMessages When `true` is set messages are saved,
* otherwise the messages are destroyed.
*/
@JvmOverloads constructor(callback: Handler.Callback?,
storesMessages: Boolean = true) : super(callback) {
mStoresMessages = storesMessages
}
/**
* When this method is called saved messages are re-sent.
*/
@UiThread
fun onResume() {
isPause = false
while (!mQueue.isEmpty()) {
val msg = mQueue.poll()
if (msg != null) {
sendMessage(msg)
}
}
}
/**
* After this method is called requested messages will be saved or destroyed.
*/
@UiThread
fun onPause() {
isPause = true
}
/**
* When this method is called saved messages are destroyed and recycled.
*/
@UiThread
fun onDestroy() {
while (!mQueue.isEmpty()) {
val msg = mQueue.poll()
if (msg != null) {
msg!!.recycle()
}
}
}
/**
* {@inheritDoc}
*
* @param msg
*/
@UiThread
override fun dispatchMessage(msg: Message?) {
if (isPause) {
if (msg != null && storesMessages()) {
val copied = Message.obtain(msg)
mQueue.offer(copied)
}
} else {
super.dispatchMessage(msg)
}
}
private fun storesMessages(): Boolean {
synchronized(mStoresMessagesLocker) {
return mStoresMessages
}
}
/**
* [android.app.Activity.runOnUiThread] like utility method.
*
*
* This method behave like [android.app.Activity.runOnUiThread],
* so an action is executed on the UI thread.
* When [.onPause] is called the action is destroyed or
* saved until [.onResume] is called.
*
* @param action the action to run on the UI thread
*/
fun runOnUiThreadSafely(action: Runnable) {
val isUiThread = Thread.currentThread() === getLooper().getThread()
val isPause = isPause
if (isUiThread && !isPause) {
action.run()
} else {
post(action)
}
}
}
// ------------------------------------------------------------------------------------------
// This work is licensed under Creative Commons Attribution-ShareAlike license version 2.5.
//
// * Author: que
// * License
// * Legal code: https://creativecommons.org/licenses/by-sa/2.5/legalcode
// * Summary: https://creativecommons.org/licenses/by-sa/2.5/
// * Original work
// * Author: StackOverflow
// * URL: https://stackoverflow.com/questions/8040280/how-to-handle-handler-messages-when-activity-fragment-is-paused#
// * Changes: This work has been changed from the original work.
// ------------------------------------------------------------------------------------------
package que;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.GuardedBy;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import java.util.LinkedList;
import java.util.Queue;
/**
* PauseHandler for {@link Handler#post(Runnable)}.
*/
public class PauseHandler extends Handler {
private final Object mIsPauseLocker = new Object();
private final Object mStoresMessagesLocker = new Object();
@GuardedBy("mIsPauseLocker")
private boolean mIsPause;
@GuardedBy("mStoresMessagesLocker")
private final boolean mStoresMessages;
@NonNull
private final Queue<Message> mQueue = new LinkedList<>();
/**
* Create an instance. Queued messages are saved while {@link #onPause()} is called to
* {@link #onResume()} is called.
*/
public PauseHandler() {
this(true);
}
/**
* Create an instance and set a callback.
* Queued messages are saved while {@link #onPause()} is called to
* {@link #onResume()} is called.
*
* @param callback The callback. The instance does not callback if {@code null} is set.
*/
public PauseHandler(@Nullable final Callback callback) {
this(callback, true);
}
/**
* Create an instance.
*
* @param storesMessages When {@code true} is set messages are saved,
* otherwise the messages are destroyed.
*/
public PauseHandler(final boolean storesMessages) {
super();
mStoresMessages = storesMessages;
}
/**
* Create an instance.
*
* @param callback The callback. The instance does not callback if {@code null} is set.
* @param storesMessages When {@code true} is set messages are saved,
* otherwise the messages are destroyed.
*/
public PauseHandler(@Nullable final Callback callback,
final boolean storesMessages) {
super(callback);
mStoresMessages = storesMessages;
}
/**
* When this method is called saved messages are re-sent.
*/
@UiThread
public void onResume() {
setPause(false);
while (!mQueue.isEmpty()) {
final Message msg = mQueue.poll();
if (msg != null) {
sendMessage(msg);
}
}
}
/**
* After this method is called requested messages will be saved or destroyed.
*/
@UiThread
public void onPause() {
setPause(true);
}
/**
* When this method is called saved messages are destroyed and recycled.
*/
@UiThread
public void onDestroy() {
while (!mQueue.isEmpty()) {
final Message msg = mQueue.poll();
if (msg != null) {
msg.recycle();
}
}
}
/**
* {@inheritDoc}
*
* @param msg
*/
@UiThread
@Override
public void dispatchMessage(final Message msg) {
if (isPause()) {
if (msg != null && storesMessages()) {
final Message copied = Message.obtain(msg);
mQueue.offer(copied);
}
} else {
super.dispatchMessage(msg);
}
}
private boolean storesMessages() {
synchronized (mStoresMessagesLocker) {
return mStoresMessages;
}
}
private void setPause(final boolean isPause) {
synchronized (mIsPauseLocker) {
mIsPause = isPause;
}
}
private boolean isPause() {
synchronized (mIsPauseLocker) {
return mIsPause;
}
}
/**
* {@link android.app.Activity#runOnUiThread(Runnable)} like utility method.
* <p>
* This method behave like {@link android.app.Activity#runOnUiThread(Runnable)},
* so an action is executed on the UI thread.
* When {@link #onPause()} is called the action is destroyed or
* saved until {@link #onResume()} is called.
*
* @param action the action to run on the UI thread
*/
public void runOnUiThreadSafely(@NonNull final Runnable action) {
final boolean isUiThread = Thread.currentThread() == getLooper().getThread();
final boolean isPause = isPause();
if (isUiThread && !isPause) {
action.run();
} else {
post(action);
}
}
}
// ------------------------------------------------------------------------------------------
// This work is licensed under Creative Commons Attribution-ShareAlike license version 2.5.
//
// * Author: que
// * License
// * Legal code: https://creativecommons.org/licenses/by-sa/2.5/legalcode
// * Summary: https://creativecommons.org/licenses/by-sa/2.5/
// * Original work
// * Author: StackOverflow
// * URL: https://stackoverflow.com/questions/8040280/how-to-handle-handler-messages-when-activity-fragment-is-paused#
// * Changes: This work has been changed from the original work.
// ------------------------------------------------------------------------------------------
package que
import android.os.Handler
import android.os.Message
import android.support.annotation.GuardedBy
import android.support.annotation.UiThread
import java.util.LinkedList
import java.util.Queue
/**
* PauseHandler for [Handler.post].
*/
class PauseHandler : Handler {
private val mIsPauseLocker = Any()
private val mStoresMessagesLocker = Any()
@GuardedBy("mIsPauseLocker")
private var mIsPause: Boolean = false
@GuardedBy("mStoresMessagesLocker")
private val mStoresMessages: Boolean
private val mQueue = LinkedList<Message>()
private var isPause: Boolean
get() {
synchronized(mIsPauseLocker) {
return mIsPause
}
}
set(isPause) {
synchronized(mIsPauseLocker) {
mIsPause = isPause
}
}
/**
* Create an instance.
*
* @param storesMessages When `true` is set messages are saved,
* otherwise the messages are destroyed.
*/
@JvmOverloads constructor(storesMessages: Boolean = true) : super() {
mStoresMessages = storesMessages
}
/**
* Create an instance.
*
* @param callback The callback. The instance does not callback if `null` is set.
* @param storesMessages When `true` is set messages are saved,
* otherwise the messages are destroyed.
*/
@JvmOverloads constructor(callback: Handler.Callback?,
storesMessages: Boolean = true) : super(callback) {
mStoresMessages = storesMessages
}
/**
* When this method is called saved messages are re-sent.
*/
@UiThread
fun onResume() {
isPause = false
while (!mQueue.isEmpty()) {
val msg = mQueue.poll()
if (msg != null) {
sendMessage(msg)
}
}
}
/**
* After this method is called requested messages will be saved or destroyed.
*/
@UiThread
fun onPause() {
isPause = true
}
/**
* When this method is called saved messages are destroyed and recycled.
*/
@UiThread
fun onDestroy() {
while (!mQueue.isEmpty()) {
val msg = mQueue.poll()
if (msg != null) {
msg!!.recycle()
}
}
}
/**
* {@inheritDoc}
*
* @param msg
*/
@UiThread
override fun dispatchMessage(msg: Message?) {
if (isPause) {
if (msg != null && storesMessages()) {
val copied = Message.obtain(msg)
mQueue.offer(copied)
}
} else {
super.dispatchMessage(msg)
}
}
private fun storesMessages(): Boolean {
synchronized(mStoresMessagesLocker) {
return mStoresMessages
}
}
/**
* [android.app.Activity.runOnUiThread] like utility method.
*
*
* This method behave like [android.app.Activity.runOnUiThread],
* so an action is executed on the UI thread.
* When [.onPause] is called the action is destroyed or
* saved until [.onResume] is called.
*
* @param action the action to run on the UI thread
*/
fun runOnUiThreadSafely(action: Runnable) {
val isUiThread = Thread.currentThread() === getLooper().getThread()
val isPause = isPause
if (isUiThread && !isPause) {
action.run()
} else {
post(action)
}
}
}
// ------------------------------------------------------------------------------------------
// This work is licensed under CC0 license version 1.0.
//
// * Author: que
// * License
// * Legal code: https://creativecommons.org/publicdomain/zero/1.0/legalcode
// * Summary: https://creativecommons.org/publicdomain/zero/1.0/deed
// ------------------------------------------------------------------------------------------
package que;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
public class SampleFragment extends Fragment {
private PauseHandler mHandler;
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new PauseHandler();
}
@Override
public void onResume() {
super.onResume();
mHandler.onResume();
}
@Override
public void onPause() {
super.onPause();
mHandler.onPause();
}
@Override
public void onDestroy() {
super.onDestroy();
mHandler.onDestroy();
}
/**
* Fragment version of {@link android.app.Activity#runOnUiThread(Runnable)}.
* <p>
* This method behave like {@link android.app.Activity#runOnUiThread(Runnable)},
* so an action is executed on the UI thread.
* When {@link #onPause()} is called the action is destroyed or
* saved until {@link #onResume()} is called.
*
* @param action the action to run on the UI thread
*/
protected void runOnUiThreadSafely(@NonNull final Runnable action) {
mHandler.runOnUiThreadSafely(action);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment