Работа сNewIntent в фрагменте

Я пишу приложение, которое использует NFC для чтения некоторых данных, хранящихся на нем. В моем приложении Fragments и Fragment не используется метод onNewIntent (). Поскольку данные, которые я читаю, выполняются с помощью моего отдельного класса, который обрабатывает связанную с NFC операцию, единственное, что мне нужно сделать, это обновить TextView внутри Фрагмента. Однако эта реализация также может использоваться для передачи нового намерения в фрагмент.

Вот моя текущая реализация, которая использует интерфейс. Я вызываю слушателя после получения нового Intent и проверки соответствия NFC успешно. Это FragmentActivity, в котором находится фрагмент.

public class Main extends FragmentActivity implements ActionBar.OnNavigationListener { private Bundle myBalanceBundle; private NFC nfcObj; private NewBalanceListener newBlanceListener; @Override public void onNewIntent(Intent intent) { setIntent(intent); } @Override protected void onResume() { getNFCState(); super.onResume(); } private void getNFCState() { //Other NFC related codes else if (nfc_state == NFC.NFC_STATE_ENABLED){ readNFCTag(); } } private void readNFCTag() { //Other NFC related codes if (getIntent().getAction().equals(NfcAdapter.ACTION_TECH_DISCOVERED)) { nfcObj.setTag((Tag) getIntent().getParcelableExtra( NfcAdapter.EXTRA_TAG)); nfcObj.readQuickBalance(); transitQuickReadFragment(nfcObj.getCurrentBalance()); } } private void transitQuickReadFragment(String balance) { // Creates a balance bundle and calls to select MyBalance Fragment if it // is not visible. Calls listener is it is already visible. if (actionBar.getSelectedNavigationIndex() != 1) { if (myBalanceBundle == null) myBalanceBundle = new Bundle(); myBalanceBundle.putString(Keys.BALANCE.toString(), balance); actionBar.setSelectedNavigationItem(1); } else { newBlanceListener.onNewBalanceRead(balance); } } @Override public boolean onNavigationItemSelected(int position, long id) { // Other fragment related codes fragment = new MyBalance(); fragment.setArguments(myBalanceBundle); newBlanceListener = (NewBalanceListener) fragment; // Other fragment related codes } // Interface callbacks. You can pass new Intent here if your application // requires it. public interface NewBalanceListener { public void onNewBalanceRead(String newBalance); } } 

Это фрагмент MyBalance, который имеет TextView, который необходимо обновлять при чтении NFC:

 public class MyBalance extends Fragment implements NewBalanceListener { private TextView mybalance_value; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { //Other onCreateView related code Bundle bundle = this.getArguments(); if (bundle != null) mybalance_value.setText(bundle.getString(Keys.BALANCE.toString(), "0.00")); else mybalance_value.setText("0.00"); //Other onCreateView related code } @Override public void onNewBalanceRead(String newBalance) { mybalance_value.setText(newBalance); } } 

Этот код работает отлично, как и ожидалось для моего приложения, но я хочу знать, есть ли лучший способ справиться с новым намерением из фрагментов?

Solutions Collecting From Web of "Работа сNewIntent в фрагменте"

Это старый вопрос, но позвольте мне ответить на него, если кто-то столкнется с ним.

Прежде всего, у вас есть ошибка в коде:

Вы не можете регистрировать Fragments как слушатели внутри Activity так, как вы это делаете. Причина в том, что Activity и Fragments могут быть уничтожены системой и повторно созданы позже из сохраненного состояния (см. Документацию по воссозданию активности ). Когда это произойдет, будут созданы новые экземпляры как Activity и Fragment , но код, который устанавливает Fragment как прослушиватель, не будет запущен, поэтому onNewBalanceRead() никогда не будет вызван. Это очень распространенная ошибка в приложениях для Android.

Чтобы сообщать события из Activity to Fragment я вижу по крайней мере два возможных подхода:

Интерфейс:

Существует официально рекомендованный способ связи между фрагментами . Этот подход похож на то, что вы делаете сейчас, в том, что он использует интерфейсы обратного вызова, реализованные либо Fragment либо Activity , но его недостатком является жесткое соединение и много уродливого кода.

Шина событий:

Лучший подход (IMHO) – использовать шину событий – «главный компонент» ( Activity в вашем случае), сообщения «обновлять» события для шины событий, тогда как «подчиненный компонент» ( Fragment в вашем случае) регистрируется на шине событий в onStart() ( onStop() in onStop() ) для получения этих событий. Это более чистый подход, который не добавляет связи между коммуникационными компонентами.

Все мои проекты используют EventBus Green Robot , и я не могу рекомендовать его достаточно высоко.

Существует по крайней мере одна альтернатива: из документации Activity.onNewIntent :

Активность всегда будет приостановлена ​​до получения нового намерения, поэтому вы можете рассчитывать на onResume (), вызываемый после этого метода.

Обратите внимание, что getIntent () все еще возвращает исходное намерение. Вы можете использовать setIntent (Intent), чтобы обновить его до этого нового намерения.

Документация FragmentActivity.onNewIntent отличается, но я не думаю, что это противоречит вышеприведенным утверждениям. Я также делаю предположение, что Fragment.onResume будет вызван после FragmentActivity.onResume , хотя документация кажется мне немного суетливой, хотя мои тесты подтверждают это предположение. Исходя из этого, я обновил намерение в такой деятельности (примеры в Котлине)

 override fun onNewIntent(intent: Intent?) { setIntent(intent) super.onNewIntent(intent) } 

И в Fragment.onResume я мог бы справиться с новым намерением так

 override fun onResume() { super.onResume() doStuff(activity.intent) } 

Таким образом, деятельность не должна знать о том, какие фрагменты она имеет.

Нет, нет лучшего способа. Фрагменты могут жить дольше, чем Действия и не обязательно привязаны к ним, поэтому предоставление новых намерений не имеет смысла.

Кстати, у вас есть несколько ошибок в вашем коде 🙂

 if (actionBar.getSelectedNavigationIndex() != 1) { 

Магические цифры плохие! Используйте константу.

  if (myBalanceBundle == null) myBalanceBundle = new Bundle(); myBalanceBundle.putString(Keys.BALANCE.toString(), balance); actionBar.setSelectedNavigationItem(1); 

Мы уже знаем, что navigationitem установлен в 1

 } else { newBlanceListener.onNewBalanceRead(balance); 

Добавьте нулевую проверку. Пользователь никогда не мог выбрать элемент навигации.