05-20 10:31:27.598 2445-2445/com.fred.testactivity E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.fred.testactivity, PID: 2445
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1328)
at android.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1346)
at android.app.BackStackRecord.commitInternal(BackStackRecord.java:728)
at android.app.BackStackRecord.commit(BackStackRecord.java:704)
Process: com.fred.testactivity, PID: 2482
java.lang.IllegalStateException: Activity has been destroyed
at android.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1350)
at android.app.BackStackRecord.commitInternal(BackStackRecord.java:728)
at android.app.BackStackRecord.commit(BackStackRecord.java:704)
at android.app.DialogFragment.show(DialogFragment.java:230)
at com.fred.testactivity.TestPauseHandler$1$1.run(TestPauseHandler.java:49)
2.我们将刷UI操作的run方法中代码换成
Dialog dialog = new Dialog(TestPauseHandler.this);
dialog.show();
Process: com.fred.testactivity, PID: 2331
android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@15b4466a is not valid; is your activity running?
at android.view.ViewRootImpl.setView(ViewRootImpl.java:562)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:272)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69)
at android.app.Dialog.show(Dialog.java:298)
/**
* 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);
}
}
}
在开发过程中经常会碰到这种情况,某一个
Activity
已经处于不可交互状态或者不可见状态时,这个Activity
中定义的Handler
却还没有处理完相关的任务,或者是启动的一个线程,线程尚未执行完毕。在通常这种情况下,由于有尚未处理的任务存在,该Handler
不会被销毁,任务会执行。但如果这个Handler
中涉及到刷新UI
的相关操作,那么可能就有点麻烦了,特别是如果此时Activity
已经是不可见状态,或已经被回收了,刷新UI变会抛出异常,导致程序崩溃。需要注意的是,当activity处于不可见状态,或者已销毁,对其刷UI的操作时并一定会出现异常,得看具体是做什么样的刷UI操作,Android真是神奇下面给出一个例子,以下代码中Activity的布局非常简单,里面就只有一个按钮,当点击这个按钮后会有一个刷UI的操作,用来显示一个DialogFragment,只是这个操作会在6秒钟之后执行。代码如下:
如果我们这样玩它: 1.点击这个按钮后,接着点
HOME
按钮,或者切到系统的Setting
屏,我们会发现该activity的onSaveInstanceState
方法执行了,然后过了一会,程序就Crash了。程序报的异常是如果在点了按钮后, 按
Back
键,结束这个activity, 回到桌面, 会发现onDestroy
方法调用了,过了一会,程序抛出异常。2.我们将刷UI操作的run方法中代码换成
点击按钮后,接着点
HOME
按钮,或者切到系统的Setting
屏,我们会发现该activity的onSaveInstanceState
方法执行了,点Recent apps
键,重新回到这个app, 发现dialog弹出来了,程序没有crash。但是,如果在点了按钮后, 按
Back
键,结束这个activity, 回到桌面, 会发现onDestroy
方法调用了,过了一会,程序抛出异常3.将run方法中代码换成
只是修改了Button上面的文字,接着做实验。 点击按钮后,接着点
HOME
按钮,或者切到系统的Setting
屏,我们会发现该activity的onSaveInstanceState
方法执行了,点Recent apps
键,重新回到这个app, 发现dialog弹出来了,程序没有crash。点击按钮后, 按
Back
键,结束这个activity, 回到桌面, 会发现onDestroy
方法调用了,程序依旧没有Crash。Android真是神奇,当activity处于不可见状态,或者已销毁,对其刷UI的操作时并一定会出现异常,得看具体是做什么样的刷UI操作。 想知道具体原因就得去看源码了。
在Android中刷UI的操作其实也是通过发送消息进行的。于是我们希望的结果是,在当前
Activity
处于不可交互状态时,若有消息没有处理完,先将消息缓存着,等到以后当此Activity
恢复可交互状态时,再处理消息。 在Stackoverflow
上面有这么一个问答,有高手给出了这么一个解决方案,非常优雅。在我们的项目中也用得到了采用。核心代码如下:其思路是:
paused
来标识当前Activity
的状态。如果paused
的值是true,代表当前的Activity
处于不可交互状态。paused
的值只会在两个位置发生改变,一个是onResume
方法执行时将paused
设为false
,另一个是在onPause
方法执行时,将paused
设为true
.Handler
继承PauseHandler
,覆盖其processMessage
方法。Handler
的外部类(Activity或Fragment)的onResume
或onPause
方法中也调用handler相应的onResume
和onPause
方法。一个采用
PauseHandler
的实现如下:采用这种方式,问题便可以完美的解决