1. ホーム
  2. android

[解決済み] アクティビティ/フラグメントが一時停止しているときのハンドラメッセージの扱い方

2022-11-28 03:55:03

質問

私の 他の投稿

基本的に、私はメッセージ Handler の中に Fragment で、これはダイアログを閉じたり表示したりすることになる、たくさんのメッセージを受け取ります。

アプリがバックグラウンドになったとき、私は onPause が表示されますが、期待どおりにメッセージは表示されます。しかし、フラグメントを使用しているため、ダイアログを閉じたり表示したりすることはできません。 IllegalStateException .

状態異常を許可しているのを解除したり、キャンセルしたりするのは

ということを考えると、私は Handler があることを考えると、一時停止中にメッセージをどのように扱うべきかについて、推奨されるアプローチがあるかどうか疑問に思います。 どのように一時停止状態のメッセージを処理すべきかについて、推奨されるアプローチがあるかどうか。

私が考えている 1 つの可能な解決策は、一時停止中に送られてくるメッセージを記録し、それを onResume . これはやや不満足で、これをよりエレガントに処理するフレームワークの何かがあるに違いないと思っています。

どのように解決するのですか?

Android オペレーティング システムには、この問題に十分に対応するメカニズムがないように見えますが、このパターンは比較的簡単に実装できる回避策を提供するものだと考えています。

次のクラスは android.os.Handler のラッパーで、アクティビティが一時停止したときにメッセージをバッファリングし、再開時にそれらを再生します。

フラグメントの状態を非同期に変更するコード (例: commit, dismiss) が、ハンドラ内のメッセージからのみ呼び出されることを確認します。

ハンドラを PauseHandler クラスから派生させます。

あなたのアクティビティが onPause() を呼び出します。 PauseHandler.pause() そして onResume() 呼ぶ PauseHandler.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 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 クラスが使用できる簡単な例です。

ボタンがクリックされると、遅延メッセージがハンドラに送信されます。

ハンドラがメッセージを受信すると (UI スレッドで) DialogFragment .

もし PausedHandler クラスが使用されていない場合、ダイアログを起動するためにテストボタンを押した後にホームボタンを押すと、IllegalStateExceptionが表示されるでしょう。

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が返され、メッセージは破棄されます。