问题描述
基本上,我的 Fragment
中有一条消息 Handler
,它接收一堆消息,这些消息可能导致对话框被关闭或显示.
Basically I have a message Handler
in my Fragment
which receives a bunch of messages that can result in dialogs being dismissed or shown.
当应用程序进入后台时,我会收到一个 onPause
,但我的消息仍然如预期的那样通过.但是,因为我使用的是片段,所以我不能只是关闭和显示对话框,因为这会导致 IllegalStateException
.
When the app is put into the background I get an onPause
but then still get my messages coming through as one would expect. However, because I'm using fragments I can't just dismiss and show dialogs as that will result in an IllegalStateException
.
我不能只是驳回或取消允许状态丢失.
I can't just dismiss or cancel allowing state loss.
鉴于我有一个 Handler
我想知道是否有推荐的方法处于暂停状态时我应该如何处理消息.
Given that I have a Handler
I'm wondering whether there is a recommended approach as tohow I should handle messages while in a paused state.
我正在考虑的一种可能的解决方案是记录暂停时通过的消息并在 onResume
上播放它们.这有点令人不满意,我认为框架中必须有一些东西可以更优雅地处理这个问题.
One possible solution I'm considering is to record the messages coming through while paused and play them back on an onResume
. This is somewhat unsatisfactory and I'm thinking that there must be something in the framework to handle this more elegantly.
推荐答案
虽然 Android 操作系统似乎没有一种机制可以充分解决您的问题,但我相信这种模式确实提供了一种相对简单的解决方法.
Although the Android operating system does not appear to have a mechanism that sufficiently addresses your problem I believe this pattern does provide a relatively simple to implement workaround.
以下类是 android.os.Handler
的包装器,它在 Activity 暂停时缓冲消息并在恢复时播放它们.
The following class is a wrapper around android.os.Handler
that buffers up messages when an activity is paused and plays them back on resume.
确保您拥有的任何异步更改片段状态(例如提交、解除)的代码仅从处理程序中的消息中调用.
Ensure any code that you have which asynchronously changes a fragment state (e.g. commit, dismiss) is only called from a message in the handler.
从 PauseHandler
类派生您的处理程序.
Derive your handler from the PauseHandler
class.
每当您的活动收到 onPause()
调用 PauseHandler.pause()
和 onResume()
调用 PauseHandler.resume()
.
Whenever your activity receives an onPause()
call PauseHandler.pause()
and for onResume()
call PauseHandler.resume()
.
用 processMessage()
替换处理程序 handleMessage()
的实现.
Replace your implementation of the Handler handleMessage()
with processMessage()
.
提供始终返回 true
的 storeMessage()
的简单实现.
Provide a simple implementation of storeMessage()
which always returns true
.
/**
* Message Handler class that supports buffering up of messages when the
* activity is paused i.e. in the background.
*/
public abstract class PauseHandler extends Handler {
/**
* Message Queue Buffer
*/
final Vector<Message> messageQueueBuffer = new Vector<Message>();
/**
* Flag indicating the pause state
*/
private boolean paused;
/**
* Resume the handler
*/
final public void resume() {
paused = false;
while (messageQueueBuffer.size() > 0) {
final Message msg = messageQueueBuffer.elementAt(0);
messageQueueBuffer.removeElementAt(0);
sendMessage(msg);
}
}
/**
* Pause the handler
*/
final public void pause() {
paused = true;
}
/**
* Notification that the message is about to be stored as the activity is
* paused. If not handled the message will be saved and replayed when the
* activity resumes.
*
* @param message
* the message which optional can be handled
* @return true if the message is to be stored
*/
protected abstract boolean storeMessage(Message message);
/**
* Notification message to be processed. This will either be directly from
* handleMessage or played back from a saved message when the activity was
* paused.
*
* @param message
* the message to be handled
*/
protected abstract void processMessage(Message message);
/** {@inheritDoc} */
@Override
final public void handleMessage(Message msg) {
if (paused) {
if (storeMessage(msg)) {
Message msgCopy = new Message();
msgCopy.copyFrom(msg);
messageQueueBuffer.add(msgCopy);
}
} else {
processMessage(msg);
}
}
}
以下是如何使用 PausedHandler
类的简单示例.
Below is a simple example of how the PausedHandler
class can be used.
单击按钮时,将向处理程序发送一条延迟消息.
On the click of a button a delayed message is sent to the handler.
当处理程序接收到消息(在 UI 线程上)时,它会显示一个 DialogFragment
.
When the handler receives the message (on the UI thread) it displays a DialogFragment
.
如果没有使用 PausedHandler
类,如果在按下测试按钮启动对话框后按下主页按钮,则会显示 IllegalStateException.
If the PausedHandler
class was not being used an IllegalStateException would be shown if the home button was pressed after pressing the test button to launch the dialog.
public class FragmentTestActivity extends Activity {
/**
* Used for "what" parameter to handler messages
*/
final static int MSG_WHAT = ('F' << 16) + ('T' << 8) + 'A';
final static int MSG_SHOW_DIALOG = 1;
int value = 1;
final static class State extends Fragment {
static final String TAG = "State";
/**
* Handler for this activity
*/
public ConcreteTestHandler handler = new ConcreteTestHandler();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
@Override
public void onResume() {
super.onResume();
handler.setActivity(getActivity());
handler.resume();
}
@Override
public void onPause() {
super.onPause();
handler.pause();
}
public void onDestroy() {
super.onDestroy();
handler.setActivity(null);
}
}
/**
* 2 second delay
*/
final static int DELAY = 2000;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
if (savedInstanceState == null) {
final Fragment state = new State();
final FragmentManager fm = getFragmentManager();
final FragmentTransaction ft = fm.beginTransaction();
ft.add(state, State.TAG);
ft.commit();
}
final Button button = (Button) findViewById(R.id.popup);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final FragmentManager fm = getFragmentManager();
State fragment = (State) fm.findFragmentByTag(State.TAG);
if (fragment != null) {
// Send a message with a delay onto the message looper
fragment.handler.sendMessageDelayed(
fragment.handler.obtainMessage(MSG_WHAT, MSG_SHOW_DIALOG, value++),
DELAY);
}
}
});
}
public void onSaveInstanceState(Bundle bundle) {
super.onSaveInstanceState(bundle);
}
/**
* Simple test dialog fragment
*/
public static class TestDialog extends DialogFragment {
int value;
/**
* Fragment Tag
*/
final static String TAG = "TestDialog";
public TestDialog() {
}
public TestDialog(int value) {
this.value = value;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View inflatedView = inflater.inflate(R.layout.dialog, container, false);
TextView text = (TextView) inflatedView.findViewById(R.id.count);
text.setText(getString(R.string.count, value));
return inflatedView;
}
}
/**
* Message Handler class that supports buffering up of messages when the
* activity is paused i.e. in the background.
*/
static class ConcreteTestHandler extends PauseHandler {
/**
* Activity instance
*/
protected Activity activity;
/**
* Set the activity associated with the handler
*
* @param activity
* the activity to set
*/
final void setActivity(Activity activity) {
this.activity = activity;
}
@Override
final protected boolean storeMessage(Message message) {
// All messages are stored by default
return true;
};
@Override
final protected void processMessage(Message msg) {
final Activity activity = this.activity;
if (activity != null) {
switch (msg.what) {
case MSG_WHAT:
switch (msg.arg1) {
case MSG_SHOW_DIALOG:
final FragmentManager fm = activity.getFragmentManager();
final TestDialog dialog = new TestDialog(msg.arg2);
// We are on the UI thread so display the dialog
// fragment
dialog.show(fm, TestDialog.TAG);
break;
}
break;
}
}
}
}
}
我已将 storeMessage()
方法添加到 PausedHandler
类,以防任何消息即使在活动暂停时也应立即处理.如果处理了一条消息,则应返回 false 并丢弃该消息.
I've added a storeMessage()
method to the PausedHandler
class in case any messages should be processed immediately even when the activity is paused. If a message is handled then false should be returned and the message will be discarded.
这篇关于活动/片段暂停时如何处理处理程序消息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!