2014-10-09 3 views
4

У меня есть CustomDialog класс, который расширяет DialogFragment. Я переопределяю метод onCreateDialog, чтобы получить настраиваемый диалог, который мне нужен.Сериализация прослушивателя Android

@Override 
public Dialog onCreateDialog(Bundle savedInstanceState) { 
    dialog = new Dialog(activity, styleId); 
    view = activity.getLayoutInflater().inflate(layoutId, null); 
    dialog.setContentView(view); 
    if (listener != null) { 
     listener.onViewInit(view, this); 
    } 
    return dialog; 

} 

Это код создания пользовательского диалога. После того, как просмотр завышен, я вызываю метод прослушивания listener.onViewInit(view, this) типа OnViewInitListener, который является интерфейсом и расширяет Serializable, чтобы связать пользовательский код для просмотра (просмотр текстов, прослушивателей и т. Д.), Так что при повороте я хочу потерять логику нажатия кнопки.

@Override 
public void onSaveInstanceState(Bundle bundle) { 
    bundle.putInt("layoutId", layoutId); 
    bundle.putInt("styleId", styleId); 
    bundle.putSerializable("listener", listener); 
    super.onSaveInstanceState(bundle); 

} 

public RsCustomDialog setOnListenerAssignment(OnViewInitListener listener) { 
    this.listener = listener; 
    return this; 
} 

Когда я реализую OnViewInitListener от деятельности, на изменения ориентации работают так, как ожидалось: onCreateDialog вызывается каждый фрагмент раз заново, и Ther ошибок нет посылок, но когда я нажимаю приложениям кнопку истории (на крайний справа) android buttons

Я получаю эту ошибку:

10-09 11:09:38.256: E/AndroidRuntime(24153): FATAL EXCEPTION: main 
10-09 11:09:38.256: E/AndroidRuntime(24153): java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = base.RsCustomDialog$OnClickListener) 
10-09 11:09:38.256: E/AndroidRuntime(24153): at android.os.Parcel.writeSerializable(Parcel.java:1279) 
10-09 11:09:38.256: E/AndroidRuntime(24153): at android.os.Parcel.writeValue(Parcel.java:1233) 
10-09 11:09:38.256: E/AndroidRuntime(24153): at android.os.Parcel.writeMapInternal(Parcel.java:591) 
10-09 11:09:38.256: E/AndroidRuntime(24153): at android.os.Bundle.writeToParcel(Bundle.java:1627) 
10-09 11:09:38.256: E/AndroidRuntime(24153): at android.os.Parcel.writeBundle(Parcel.java:605) 
10-09 11:09:38.256: E/AndroidRuntime(24153): at android.support.v4.app.FragmentState.writeToParcel(Fragment.java:133) 

Я думаю, это потому, что, когда я реализую OnViewInitListener из моей активности, java неявно помещает переменную активности в реализованный объект, а Parcel не может обрабатывать транзакцию Activity.

Может кто-нибудь предложить, как справиться с этой проблемой или посоветовать лучшее решение.

ответ

0

Ну, я решил! То, что я сделал это я реализовал Parcable следующим образом:

public abstract class OnClickListener implements DialogInterface.OnClickListener, Parcelable { 


@Override 
public abstract void onClick(DialogInterface dialog, int which); 

@Override 
public void writeToParcel(Parcel dest, int flags) { 

} 

@Override 
public int describeContents() { 
    return 0; 
} 

} 

Так что мой ConfirmDialog код остается прежним:

@Override 
public Dialog onCreateDialog(Bundle savedInstanceState) { 
    return new AlertDialog.Builder(getActivity()).setTitle(title).setMessage(text).setPositiveButton(R.string.yes, onYes).setNegativeButton(R.string.no, onNo).create(); 

} 



@Override 
public void onSaveInstanceState(Bundle bundle) { 
    super.onSaveInstanceState(bundle); 
    bundle.putParcelable("onYes", onYes); 
    bundle.putParcelable("onNo", onNo); 
} 

Единственное ограничение не использовать автоматические переменные, которые не Parcelable в onClick метод. Вот мой пример показывает этот диалог:

 showConfirmDialog(getString(R.string.sure_want_to_exit), new base.dialog.OnClickListener() { 

     @Override 
     public void onClick(DialogInterface dialog, int which) { 
      ((NewProtocol) getCurrentActivity()).exit = true;    
      getCurrentActivity().finish(); 

     } 


    }, null); 

getCurrentActivity() статический метод, который возвращает текущую активную деятельность.

+4

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

+0

Почему это даже работает? Фактически, он также работает без явного сохранения onSaveInstanceState. 'getArguments(). getParcelable (" listener ")' всегда возвращает тот же объект, переданный в Bundle, независимо от того, сколько раз изменяется ориентация телефона. –

1

Ваш OnViewInitListener должен быть статическим и сериализуемым и иметь все сериализуемые поля внутри. Если вы ссылаетесь на Activity из этого, вы делаете это неправильно. Чтобы преодолеть эту проблему, вы можете:

  1. Ссылка на экземпляр активности, хранящийся в статической переменной WeakReference, которая заполняется при создании активности.
  2. Использование широковещательных приемников
  3. Перерегистрировать прослушиватель, когда фрагмент восстанавливается с новым и соответствующим контекстом.
+0

Привет, спасибо за Ответить! Я могу сказать, что я проверил 1 и 3 из вашего списка: 1) Я ссылаюсь на переменные из ссылки статической активности, то есть в BaseActivity. 3) Я не хочу прослушивать перерегистратор, моя цель - зарегистрировать его один раз, а затем забыть об этом. При изменении конфигурации он должен автоматически восстанавливать прослушиватель. Но я всегда использую правильный контекст в классе CustomDialog. 2- Я не знаю, что вы подразумеваете под этим, как это может помочь? –

3

Вы не можете сериализовать и восстановить прослушиватель.

Сериализация (в том числе с использованием Parcelable) сохраняет состояние экземпляра объекта и десериализации помещает это состояние в новый экземпляр объекта.

Слушатели не имеют состояния - вот почему ваша Parcelable реализация не спасает и не восстанавливает ничего. Переменная слушателя является ссылкой на экземпляр объекта (тот, который, как известно, реализует интерфейс слушателя). Если создается новый экземпляр этого объекта (например: из-за поворота или процесса, который был убит из-за низкой памяти), это не помогает диалогу попытаться восстановить указатель на предыдущий экземпляр. Предыдущий экземпляр больше не существует, и вновь созданный (правильный) экземпляр не существовал в то время, когда был вызван onSaveInstanceState.

два возможных варианта:

  • Если слушатель предназначен для деятельности, результатом которых диалог прикреплен к, то, что может быть восстановлен в методе onAttach в DialogFragment в
  • Если нет, то активность может установите прослушиватель фрагментов, когда он создан (или воссоздан). Для того, чтобы получить ссылку на автоматически восстановлен фрагмент, например, можно использовать getFragmentManager().findFragmentById(id) или getFragmentManager().findFragmentByTag(tag)
+0

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

+0

@lxx Я согласен, статическая переменная не является хорошей идеей - не может думать о случае, когда пример приложения имеет смысл, но это не значит, что он не существует. Моя мысль о втором варианте заключалась в том, чтобы либо создать новый экземпляр слушателя, либо установить экземпляр фрагмента, извлеченный из FragmentManager в качестве слушателя. –

-1

вы можете написать что-то в onResume и OnStop, что вы слушатель незарегистрированного в OnStop методы, зарегистрироваться в методе onResume

+0

Это скорее комментарий, чем ответ. – LethalProgrammer