102 votes

Comment gérer Gestionnaire de messages lors de l'activité/fragment est en pause

De légères variations sur mon autre affichage

Fondamentalement, j'ai un message d' Handler mon Fragment qui reçoit un tas de messages qui peuvent en résulter dans les boîtes de dialogue d'être rejeté ou montré.

Lorsque l'application est mise en arrière-plan, je reçois un onPause mais reçois toujours mes messages à venir à travers comme on pourrait le penser. Cependant, parce que je suis en utilisant des fragments je ne peux pas juste de rejeter et d'afficher les boîtes de dialogue comme en IllegalStateException.

Je ne peux pas juste de rejeter ou d'annuler permettant à l'état de perte.

Étant donné que j'ai un Handler je me demande si il n'y est une approche recommandée pour comment je dois gérer les messages alors que dans un état de pause.

Une solution que j'envisage est d'enregistrer les messages à venir à travers en pause et de les lire sur un onResume. C'est un peu insatisfaisant et je pense qu'il doit y avoir quelque chose dans le cadre de gérer cela de manière plus élégante.

Merci à l'avance. Pierre.

169voto

quickdraw mcgraw Points 1106

Bien que le système d'exploitation Android ne semble pas disposer d'un mécanisme de manière satisfaisante votre problème, je crois que ce modèle n'est relativement simple à mettre en œuvre une solution de contournement.

La classe suivante est un wrapper autour d'android.os.Gestionnaire de tampons de messages lorsqu'une activité est en pause et joue en arrière sur le curriculum vitae.

Assurer le code que vous avez qui de manière asynchrone les changements d'un fragment de l'état (par exemple s'engager, de les rejeter) n'est appelée à partir d'un message dans le gestionnaire.

Tirer votre gestionnaire de l'PauseHandler classe.

Chaque fois que votre activité reçoit un onPause() appel PauseHandler.pause() et pour onResume() appel PauseHandler.resume().

Remplacer votre mise en œuvre de la Gestionnaire handleMessage() avec processMessage().

Fournir un moyen simple de mise en œuvre de storeMessage() qui retourne toujours 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);
        }
    }
}

Ci-dessous est un exemple simple de la façon dont le PausedHandler classe peut être utilisée.

Sur le clic d'un bouton retardée, un message est envoyé au gestionnaire.

Lorsque le gestionnaire reçoit le message (sur le thread d'INTERFACE utilisateur), il affiche une DialogFragment.

Si le PausedHandler classe n'a pas été utilisé une IllegalStateException serait montré si le bouton a été pressé après avoir appuyé sur le bouton test pour lancer la boîte de dialogue.

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;
                }
            }
        }
    }
}

J'ai ajouté un storeMessage() la méthode la PausedHandler classe dans le cas où les messages doivent être traitées immédiatement, même lorsque l'activité est suspendue. Si un message est traité puis false doit être retourné et le message sera supprimé.

10voto

William Points 5270

Un peu plus simple version de quickdraw excellent PauseHandler est

/**
 * 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
     */
    private final List<Message> messageQueueBuffer = Collections.synchronizedList(new ArrayList<Message>());

    /**
     * Flag indicating the pause state
     */
    private Activity activity;

    /**
     * Resume the handler.
     */
    public final synchronized void resume(Activity activity) {
        this.activity = activity;

        while (messageQueueBuffer.size() > 0) {
            final Message msg = messageQueueBuffer.get(0);
            messageQueueBuffer.remove(0);
            sendMessage(msg);
        }
    }

    /**
     * Pause the handler.
     */
    public final synchronized void pause() {
        activity = null;
    }

    /**
     * Store the message if we have been paused, otherwise handle it now.
     *
     * @param msg   Message to handle.
     */
    @Override
    public final synchronized void handleMessage(Message msg) {
        if (activity == null) {
            final Message msgCopy = new Message();
            msgCopy.copyFrom(msg);
            messageQueueBuffer.add(msgCopy);
        } else {
            processMessage(activity, msg);
        }
    }

    /**
     * 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 activity  Activity owning this Handler that isn't currently paused.
     * @param message   Message to be handled
     */
    protected abstract void processMessage(Activity activity, Message message);

}

Il suppose que vous voulez toujours de stocker les messages hors ligne pour la relecture. Et fournit de l'Activité que de l'entrée à l' #processMessages de sorte que vous n'avez pas besoin de le gérer dans la sous-classe.

0voto

dangel Points 376

Dans mes projets, j'utilise le design pattern observateur de résoudre ce problème. Dans Android, récepteurs de radiodiffusion et les intentions sont une mise en oeuvre de ce modèle.

Ce que j'ai à faire est de créer un BroadcastReceiver qui m'inscrire dans le fragment de la/de l'activité onResume et annuler l'inscription dans le fragment de la/de l'activité onPause. Dans BroadcastReceiver'méthode onReceive j'ai mis tout le code qui doit s'exécuter en tant que résultat de la BroadcastReceiver - réception d'une Intention(message) qui a été envoyé à votre application en général. Pour augmenter la sélectivité sur ce type d'intentions de votre fragment peut vous recevoir, vous pouvez utiliser un filtre d'intention comme dans l'exemple ci-dessous.

Un avantage de cette approche est que l' Intention(message) peuvent être envoyés à partir de partout dans votre application(une boîte de dialogue qui s'ouvrait au-dessus de votre fragment, une tâche asynchrone, un autre fragment, etc.). Les paramètres peuvent même passé comme une intention extras.

Un autre avantage est que cette approche est compatible avec n'importe quel Android version de l'API, depuis BroadcastReceivers et les Intentions ont été introduits sur l'API de niveau 1.

Vous n'êtes pas requis pour l'installation de toutes les autorisations spéciales sur votre fichier manifeste de l'application, sauf si vous prévoyez d'utiliser sendStickyBroadcast(où vous devez ajouter BROADCAST_STICKY).

public class MyFragment extends Fragment { 

    public static final String INTENT_FILTER = "gr.tasos.myfragment.refresh";

    private BroadcastReceiver mReceiver = new BroadcastReceiver() {

        // this always runs in UI Thread 
        @Override
        public void onReceive(Context context, Intent intent) {
            // your UI related code here

            // you can receiver data login with the intent as below
            boolean parameter = intent.getExtras().getBoolean("parameter");
        }
    };

    public void onResume() {
        super.onResume();
        getActivity().registerReceiver(mReceiver, new IntentFilter(INTENT_FILTER));

    };

    @Override
    public void onPause() {
        getActivity().unregisterReceiver(mReceiver);
        super.onPause();
    }

    // send a broadcast that will be "caught" once the receiver is up
    protected void notifyFragment() {
        Intent intent = new Intent(SelectCategoryFragment.INTENT_FILTER);
        // you can send data to receiver as intent extras
        intent.putExtra("parameter", true);
        getActivity().sendBroadcast(intent);
    }

}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X