Как обрабатывать сообщения обработчика, когда действие / fragment приостановлено

Небольшая вариация в моей другой публикации

В основном у меня есть Handler сообщений в моем Fragment который получает кучу сообщений, которые могут привести к отклонению или отображению диалогов.

Когда приложение помещается в фоновом режиме, я получаю onPause но потом все равно получаю сообщения, которые можно ожидать. Однако, поскольку я использую fragmentы, я не могу просто отклонить и показать диалоги, так как это приведет к IllegalStateException .

Я не могу просто уволить или отменить разрешение государственной потери.

Учитывая, что у меня есть Handler мне интересно, есть ли рекомендуемый подход к тому, как я должен обрабатывать сообщения, находясь в состоянии паузы.

Одно из возможных решений, которое я рассматриваю, – записать сообщения, проходящие через паузу, и воспроизвести их на onResume . Это несколько неудовлетворительно, и я думаю, что в этой области должно быть что-то, что нужно для более элегантного решения.

Хотя у операционной системы Android, похоже, нет механизма, достаточного для решения вашей проблемы. Я считаю, что этот шаблон обеспечивает относительно простой способ обхода решения.

Следующий class представляет собой оболочку для android.os.Handler которая буферизует сообщения, когда действие приостанавливается и воспроизводит их при возобновлении.

Убедитесь, что любой код, который у вас есть, который асинхронно изменяет состояние fragmentа (например, commit, off), вызывается только из сообщения в обработчике.

PauseHandler обработчик из classа PauseHandler .

Всякий раз, когда ваша активность получает вызов PauseHandler.pause() и для вызова PauseHandler.resume() .

Замените выполнение обработчика handleMessage() обработчиком processMessage() .

Предоставьте простую реализацию storeMessage() которая всегда возвращает true .

 /** * Message Handler class that supports buffering up of messages when the * activity is paused ie in the background. */ public abstract class PauseHandler 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); } } } 

Ниже приведен простой пример того, как можно использовать class PausedHandler .

По щелчку кнопки отправителю отправляется сообщение с задержкой.

Когда обработчик получает сообщение (в streamе пользовательского интерфейса), он отображает DialogFragment .

Если class 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 ie 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() в class PausedHandler если любые сообщения должны быть обработаны немедленно, даже когда действие приостановлено. Если сообщение обрабатывается, то false должно быть возвращено, и сообщение будет отброшено.

Немного более простая версия превосходного PauseHandler от quickdraw

 /** * Message Handler class that supports buffering up of messages when the activity is paused ie in the background. */ public abstract class PauseHandler extends Handler { /** * Message Queue Buffer */ private final List messageQueueBuffer = Collections.synchronizedList(new ArrayList()); /** * 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); } 

Предполагается, что вы всегда хотите хранить автономные сообщения для воспроизведения. И предоставляет Activity в качестве входных данных для #processMessages поэтому вам не нужно управлять им в подclassе.

Вот несколько другой способ подойти к проблеме выполнения Fragment в функции обратного вызова и избежать проблемы с IllegalStateException.

Сначала создайте настраиваемый управляемый интерфейс.

 public interface MyRunnable { void run(AppCompatActivity context); } 

Затем создайте fragment для обработки объектов MyRunnable. Если объект MyRunnable был создан после приостановки действия, например, если экран повернут или пользователь нажимает кнопку «домой», он помещается в очередь для последующей обработки с новым контекстом. Очередь сохраняется при любых изменениях конфигурации, поскольку для экземпляра setRetain установлено значение true. Метод runProtected работает в streamе пользовательского интерфейса, чтобы избежать состояния гонки с помощью флажка isPaused.

 public class PauseHandlerFragment extends Fragment { private AppCompatActivity context; private boolean isPaused = true; private Vector buffer = new Vector<>(); @Override public void onAttach(Context context) { super.onAttach(context); this.context = (AppCompatActivity)context; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); } @Override public void onPause() { isPaused = true; super.onPause(); } @Override public void onResume() { isPaused = false; playback(); super.onResume(); } private void playback() { while (buffer.size() > 0) { final MyRunnable runnable = buffer.elementAt(0); buffer.removeElementAt(0); new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { //execute run block, providing new context, incase //Android re-creates the parent activity runnable.run(context); } }); } } public final void runProtected(final MyRunnable runnable) { context.runOnUiThread(new Runnable() { @Override public void run() { if(isPaused) { buffer.add(runnable); } else { runnable.run(context); } } }); } } 

Наконец, fragment может быть использован в основном приложении следующим образом:

 public class SomeActivity extends AppCompatActivity implements SomeListener { PauseHandlerFragment mPauseHandlerFragment; static class Storyboard { public static String PAUSE_HANDLER_FRAGMENT_TAG = "phft"; } protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... //register pause handler FragmentManager fm = getSupportFragmentManager(); mPauseHandlerFragment = (PauseHandlerFragment) fm. findFragmentByTag(Storyboard.PAUSE_HANDLER_FRAGMENT_TAG); if(mPauseHandlerFragment == null) { mPauseHandlerFragment = new PauseHandlerFragment(); fm.beginTransaction() .add(mPauseHandlerFragment, Storyboard.PAUSE_HANDLER_FRAGMENT_TAG) .commit(); } } // part of SomeListener interface public void OnCallback(final String data) { mPauseHandlerFragment.runProtected(new MyRunnable() { @Override public void run(AppCompatActivity context) { //this block of code should be protected from IllegalStateException FragmentManager fm = context.getSupportFragmentManager(); ... } }); } } 

В моих проектах я использую шаблон проектирования наблюдателя, чтобы решить эту проблему. В Android широковещательные приемники и намерения являются воплощением этого шаблона.

То, что я делаю, это создать BroadcastReceiver, который я регистрирую в on / и fragmentе / активности fragmentа / активности в onpause fragmentа / активности. В методе BroadcastReceiver onReceive я помещаю весь код, который должен запускаться в результате – BroadcastReceiver – получая Intent (сообщение), который был отправлен в ваше приложение в целом. Чтобы повысить избирательность, какой тип намерений может получить ваш fragment, вы можете использовать фильтр намерений, как в приведенном ниже примере.

Преимущество этого подхода заключается в том, что Intent (message) может быть отправлено повсюду в вашем приложении (диалог, который открывается поверх вашего fragmentа, задача async, другой fragment и т. Д.). Параметры могут даже передаваться как дополнительные намерения.

Еще одно преимущество заключается в том, что этот подход совместим с любой версией Android API, поскольку BroadcastReceivers и Intents были введены на уровне API 1.

Вам не требуется устанавливать какие-либо специальные разрешения в файле манифеста приложения, за исключением случаев, когда вы планируете использовать sendStickyBroadcast (где вам нужно добавить 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); } } 
  • Android: что выбрать для значений кода запроса?
  • андроидный захват видео кадра
  • Android Не удалось создать экземпляр активности: не нашел class на пути
  • Как сохранить объект в базе данных sqlite?
  • Окно оверлей системы Android
  • Что отличает MainActivity.this от getApplicationContext ()
  • Как создать круговой (бесконечный) RecyclerView?
  • Android Emulator vs Real Device
  • Запуск приложения Google Maps на Android Emulator
  • Проблема с подключением к Интернету в Интернете
  • Переход 3D-куба в Android
  • Interesting Posts

    iPhone – UIWebview – Получите URL-адрес ссылки, нажатой

    Android Как настроить макет в полноэкранном режиме, когда видна клавиатура

    $ (window) .width () не совпадает с запросом медиа

    Ошибка получения родительского элемента: не найден ресурс, который соответствует указанному имени ‘@android: style / TextAppearance.Holo.Widget.ActionBar.Title’

    Как настроить инвариантную культуру в ASP.NET-глобализации?

    Пример сокета клиента Python

    Как мне перейти с Windows 7 RC на Windows 7 RTM?

    Json.Net добавляет $ id к объектам EF, несмотря на то, что PreserveReferencesHandling указывает на “None”

    JSON Web Token (JWT) с основанной на Spring SockJS / STOMP Web Socket

    Как получить счетчик ссылок NSObject?

    Как ограничить количество обновляемых документов в mongodb

    Перемещение хромированного окна на другой монитор превращает его в черный

    Как преобразовать изображение RGB в оттенки серого, но сохранить один цвет?

    Сопоставления файлов для Windows 8

    Как измерить производительность диска?

    Давайте будем гением компьютера.