Фрагменты, заменяемые при выполнении AsyncTask, - NullPointerException в getActivity()
Недавно я преобразовал свои действия в фрагменты.
Используя что-то похожее на Tab-Navigation, фрагменты заменяются, когда пользователь выбирает другую вкладку.
После заполнения фрагмента я запускаю хотя бы одну AsyncTask, чтобы получить некоторую информацию из Интернета. Однако - если пользователь переключится на другую вкладку так же, как выполняется doBackground-метод из моей AsyncTask - фрагмент заменен, и поэтому я получаю NullPointerException
в отмеченных строках:
@Override
protected Object doInBackground(Object... params) {
...
String tempjson = helper.SendPost(getResources().getText(R.string.apiid)); //ERROR: Fragment not attached
...
}
protected onPostExecute(Object result) {
...
getActivity().getContentResolver() //NULLPOINTEREXCEPTION
getView().findViewById(R.id.button) //NULL
...
}
getActivity()
и getResources()
вызывает ошибку, потому что мой фрагмент заменен.
Что я пробовал:
- Вызов метода отмены на моей AsyncTask (не будет исправлять первую ошибку или вторую ошибку, если фрагмент заменяется при выполнении
onPostExecute()
)
- проверка if
getActivity()
null
или вызов this.isDetached()
(не реальное исправление, и мне нужно будет его проверять всякий раз, когда я вызываю getActivity()
и т.д.)
Итак, мой вопрос: что было бы лучше, чтобы избавиться от этих проблем AsyncTask? У меня не было этих проблем с помощью "Активных", поскольку они не были "убиты" /отключены при изменении вкладок (что привело к увеличению использования памяти - причина, по которой мне нравится переключаться на фрагменты)
Ответы
Ответ 1
Так как AsyncTask
работает в фоновом режиме, ваш фрагмент может быть отделен от его родительской активности к моменту окончания. Как вы узнали, вы можете использовать isDetached()
для проверки. В этом нет ничего плохого, и вам не нужно каждый раз проверять, просто рассмотрите фрагменты и жизненные циклы активности.
Две другие альтернативы:
- Используйте Loaders, они предназначены для более приятного воспроизведения фрагментов.
- Переместите загрузку AsyncTask в родительскую активность и используйте интерфейсы для развязки с фрагментами. Активность будет знать, есть ли фрагмент или нет, и действовать соответствующим образом (возможно, отбрасывая результат, если фрагмент исчез).
Ответ 2
Сегодня я столкнулся с той же проблемой: когда я изменил отображаемый fragment
, если AsyncTask
еще не закончен, и он пытается получить доступ к view
, чтобы заполнить его еще несколькими элементами, он будет верните a NullPointerException
.
Я решил проблему, переопределяющую один метод жизненного цикла фрагментов: onDetach()
. Этот метод вызывается в момент, когда fragment
отсоединяется от activity
.
Что вам нужно сделать, так это вызвать метод cancel()
на вашем AsyncTask
. Это остановит выполнение задачи, чтобы избежать NullPointerExecption
.
Здесь образец onDetach()
:
@Override
public void onDetach() {
super.onDetach();
task.cancel(true);
}
Проверьте эту страницу, чтобы получить дополнительную информацию о жизненном цикле фрагментов:
http://developer.android.com/reference/android/app/Fragment.html#Lifecycle
И это, чтобы больше узнать об отмене задачи:
http://developer.android.com/reference/android/os/AsyncTask.html
Ответ 3
Попробуйте вызвать setRetainInstance(true);
в функции onCreate()
вашего класса фрагмента?