我希望在网络后台操作完成后提交一个fragment。 我在成功的网络操作后调用commit(),但如果活动进入暂停或停止状态,它将导致应用程序崩溃并显示IllegalStateException。
因此,我尝试使用commitAllowingStateLoss()现在可以正常工作。
我阅读了一些博客和文章,发现commitAllowingStateLoss()不适合使用。
如何处理在网络操作处理活动暂停和停止状态后提交碎片的方式?
我希望在网络后台操作完成后提交一个fragment。 我在成功的网络操作后调用commit(),但如果活动进入暂停或停止状态,它将导致应用程序崩溃并显示IllegalStateException。
因此,我尝试使用commitAllowingStateLoss()现在可以正常工作。
我阅读了一些博客和文章,发现commitAllowingStateLoss()不适合使用。
如何处理在网络操作处理活动暂停和停止状态后提交碎片的方式?
public void onResume/onPostResume() {
super.onResume/onPostResume();
if(someTreatmentIsPending) {
/*do what you need to do with your variable here : fragment
transactions, dialog showing...*/
}
}
附加信息: 此主题,特别是@jed的回答以及@pjv、@Sufian对此的评论。 这篇博客可以帮助您了解为什么会出现此错误以及为什么提出/接受的答案能够解决问题。
最后: 如果您想知道“为什么使用服务比asyncTask更好”,我所理解的并不是真正的更好。主要区别在于,适当地使用服务允许您在activity暂停/恢复时注册/注销处理程序。因此,在您的活动处于活动状态时,您始终可以获得您的答案,从而防止出现错误。
请注意,这并不意味着没有错误发生,如果您直接更改视图,则不涉及片段事务,因此不能保证更改将在应用程序重新创建、恢复、重新启动或其他任何情况下得到保留和重建。
简单的方法是等待Activity
恢复,这样你就可以commit
你的操作,一个简单的解决方法可能看起来像这样:
@Override
public void onNetworkResponse(){
//Move to next fragmnt if Activity is Started or Resumed
shouldMove = true;
if (isResumed()){
moveToNext = false;
//Move to Next Page
getActivity().getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, new NextFragment())
.addToBackStack(null)
.commit();
}
}
因此,如果Fragment
已恢复(因此Activity
也已恢复),则您可以提交
您的操作,但如果没有恢复,则需要等待Activity
启动后才能提交
您的操作:
@Override
public void onResume() {
super.onResume();
if(moveToNext){
moveToNext = false;
//Move to Next Page
getActivity().getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, new NextFragment())
.addToBackStack(null)
.commit();
}
}
注意:moveToNext = false
;它的存在是为了确保在使用后退按钮返回时,commit
不会重复执行。
commitAllowingStateLoss()
是他们的其中一个解决方案,你知道其余的)。解决方法是编写一个处理程序类,在活动传递时缓冲消息并在onResume
上播放它们。通过使用这个类,请确保所有异步更改片段状态(提交等)的代码都是从此处理程序中的消息调用的。从处理程序类继承FragmenntPauseHandler
。每当您的活动接收到onPause()
调用时,请调用FragmenntPauseHandler.pause()
,对于onResume()
调用FragmenntPauseHandler.resume()
。将处理程序handleMessage()
的实现替换为processMessage()
。提供storeMessage()
的简单实现,始终返回true。/**
* Message Handler class that supports buffering up of messages when the
* activity is paused i.e. in the background.
*/
public abstract class FragmenntPauseHandler 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);
}
}
}
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 FragmenntPauseHandler {
/**
* 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;
}
}
}
}
}
FragmentPauseHandler
类中添加了一个storeMessage()
方法,以便在活动暂停时立即处理任何消息。如果处理了消息,则应返回false并且该消息将被丢弃。希望能对您有所帮助。我在4个应用程序中使用了这个方法,再也没有遇到同样的问题了。提交 安排提交此事务。提交不会立即发生;它将被安排为主线程上的工作,在下一次该线程准备好进行时执行。
只能在包含活动保存其状态之前使用此方法提交事务。如果在此之后尝试提交,则会抛出异常。这是因为提交后的状态可以在需要从其状态还原活动时丢失。
commitAllowingStateLoss
类似于commit(),但允许在保存活动状态后执行提交。这很危险,因为如果稍后需要从其状态还原活动,则可能会丢失提交,因此这仅应用于允许用户UI状态意外更改的情况。
根据您的问题,您正在使用AsyncTask进行网络后台操作。因此,在其回调方法中提交片段可能会出现问题。要避免这种异常,只需不要在asynctask回调方法中提交。这不是解决方案,而是预防措施。
只需检查活动是否正在结束,然后commit()
事务。
if (!isFinishing()) {
// commit transaction
}
你得到的异常是在一个场景中,当活动正在结束,并且你尝试提交一个新的事务,显然,由于FragmentManager
已经执行了onSavedInstanceState()
,所以它不会被保存。这就是为什么框架强制要求你"正确"实现它。
commit()
方法来说不行... 这个方法是异步的,所以它会稍后执行,在那个时候Activity
可能正在结束(在onSaveInstanceState
之后的状态)- 我从生产应用程序中的崩溃日志可以证实这一点。检查isFinishing()
就足够了,并且将与commitNow()
一起使用,这个方法是同步的。 - snachmsmcommit()
时,会在 MessageQueue
上发布一条 Message
,该消息将在 Looper
的一个即将到来的循环中处理。现在,当活动即将完成时,也会有一条 Message
,它将被发布到 MessageQueue
上,因此可以保证在稍早发布的 commit()
消息之后处理。这是我对此案例的理解。 - azizbekian