commitAllowingStateLoss() and commit() fragment

前端 未结 5 954
陌清茗
陌清茗 2021-02-04 03:47

I want commit a fragment after network background operation. I was calling commit() after successful network operation but in case activity goes to pause or stop state it was cr

5条回答
  •  鱼传尺愫
    2021-02-04 04:13

    Well I came across the same problem and found a very simple workaround. Since android os doesn't have any solution that time ( Still don't have a good one though commitAllowingStateLoss() is one of their solution, and you know the rest).

    The solution was to write a Handler class that buffers the messages when activity is pass and plays them on onResume again.

    By using this class, Make sure you all the code which asynchronously changing fragment state ( commit etc) is called from a message in this `handler.

    ExtendFragmenntPauseHandler from handle class.

    Whenever your activity receives an onPause() call FragmenntPauseHandler.pause() and for onResume() call FragmenntPauseHandler.resume().

    Replace your implementation of the Handler handleMessage() with processMessage().

    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 FragmenntPauseHandler extends Handler {
    
        /**
         * Message Queue Buffer
         */
        final Vector messageQueueBuffer = new Vector();
    
        /**
         * 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);
            }
        }
    }
    

    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.

    When the handler receives the message (on the UI thread) it displays a DialogFragment.

    If the FragmenntPauseHandler 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 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;
                    }
                }
            }
        }
    }
    

    I've added a storeMessage() method to the FragmenntPauseHandler 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.Hope it helps. I used this one in 4 apps and never had the same issue again.

提交回复
热议问题