Отсутствие вывода на экране не означает, что деструктор не вызывается: ouptut может быть захвачен с помощью output_buffering (может быть, лайм делает это, чтобы работать над ним?) , и не повторяется, когда сценарий заканчивается, например.
В целях тестирования вы можете попробовать записать файл в свой метод __destruct
вместо того, чтобы просто повторять какой-либо текст. (Просто убедитесь, что ваше приложение / PHP имеет необходимые права для записи в файл назначения)
(Я уже сталкивался с ситуациями, когда я не видел выход, сделанный в деструкторе, но он был фактически вызван)
Я нашел очень простой ответ: isAdded()
:
Возврат
true
, если фрагмент в настоящее время добавлен к его активности.
@Override
protected void onPostExecute(Void result){
if(isAdded()){
getResources().getString(R.string.app_name);
}
}
Чтобы избежать вызова onPostExecute
, когда Fragment
не присоединен к Activity
, необходимо отменить AsyncTask
при приостановке или остановке Fragment
. Тогда isAdded()
больше не будет необходимости. Тем не менее, желательно сохранить эту проверку на месте.
Проблема в том, что вы пытаетесь получить доступ к ресурсам (в данном случае к строкам) с помощью getResources (). GetString (), который попытается получить ресурсы из Activity. Посмотрите этот исходный код класса Fragment:
/**
* Return <code>getActivity().getResources()</code>.
*/
final public Resources getResources() {
if (mHost == null) {
throw new IllegalStateException("Fragment " + this + " not attached to Activity");
}
return mHost.getContext().getResources();
}
mHost
- это объект, который содержит вашу активность.
Поскольку действие не может быть присоединено, ваш вызов getResources () вызовет исключение.
Принятое решение ИМХО - это не тот путь, которым вы просто скрываете проблему. Правильный способ - просто получить ресурсы из другого места, которое всегда гарантированно существует, например, из контекста приложения:
youApplicationObject.getResources().getString(...)
Я столкнулся здесь с двумя различными сценариями:
1) Когда я все равно хочу завершить асинхронную задачу: представьте, что мой onPostExecute действительно сохраняет полученные данные, а затем вызывает слушателя для обновления представлений, чтобы эффективно, я хочу, чтобы задача все равно завершилась, поэтому у меня есть данные, готовые, когда пользователь вернется. В этом случае я обычно делаю это:
@Override
protected void onPostExecute(void result) {
// do whatever you do to save data
if (this.getView() != null) {
// update views
}
}
2) Когда я хочу, чтобы асинхронная задача завершилась только тогда, когда представления могут быть обновлены: в случае, если вы предлагаете здесь, задача только обновляет представления, нет хранилище данных необходимо, поэтому он не имеет ни малейшего представления о завершении задачи, если представления больше не отображаются. Я делаю это:
@Override
protected void onStop() {
// notice here that I keep a reference to the task being executed as a class member:
if (this.myTask != null && this.myTask.getStatus() == Status.RUNNING) this.myTask.cancel(true);
super.onStop();
}
Я не нашел никаких проблем с этим, хотя я также использую (возможно) более сложный способ, который включает запуск задач из действия вместо фрагментов.
Хотелось бы, чтобы это кому-то помогло! :)
Проблема с вашим кодом заключается в том, как вы используете AsyncTask, потому что когда вы поворачиваете экран во время спящего потока:
Thread.sleep(2000)
AsyncTask все еще работает, это потому, что вы не сделали t правильно отменить экземпляр AsyncTask в onDestroy () до перестройки фрагмента (при вращении) и при запуске этого же экземпляра AsyncTask (после вращения) с помощью onPostExecute (), он пытается найти ресурсы с помощью getResources () со старым экземпляром фрагмента ( недопустимый экземпляр):
getResources().getString(R.string.app_name)
, что эквивалентно:
MyFragment.this.getResources().getString(R.string.app_name)
Таким образом, окончательное решение заключается в управлении экземпляром AsyncTask (для отмены, если он все еще работает) до перестройки фрагмента когда вы поворачиваете экран и отменены во время перехода, перезапустите AsyncTask после реконструкции с помощью логического флага:
public class MyFragment extends SherlockFragment {
private MyAsyncTask myAsyncTask = null;
private boolean myAsyncTaskIsRunning = true;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState!=null) {
myAsyncTaskIsRunning = savedInstanceState.getBoolean("myAsyncTaskIsRunning");
}
if(myAsyncTaskIsRunning) {
myAsyncTask = new MyAsyncTask();
myAsyncTask.execute();
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("myAsyncTaskIsRunning",myAsyncTaskIsRunning);
}
@Override
public void onDestroy() {
super.onDestroy();
if(myAsyncTask!=null) myAsyncTask.cancel(true);
myAsyncTask = null;
}
public class MyAsyncTask extends AsyncTask<Void, Void, Void>() {
public MyAsyncTask(){}
@Override
protected void onPreExecute() {
super.onPreExecute();
myAsyncTaskIsRunning = true;
}
@Override
protected Void doInBackground(Void... params) {
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {}
return null;
}
@Override
protected void onPostExecute(Void result){
getResources().getString(R.string.app_name);
myAsyncTaskIsRunning = false;
myAsyncTask = null;
}
}
}
Их довольно хитрое решение для этого и утечки фрагмента из активности.
Таким образом, в случае getResource или чего-либо другого, зависящего от доступа к контексту активности из фрагмента, всегда проверяется статус активности и статус фрагментов следующим образом
Activity activity = getActivity();
if(activity != null && isAdded())
getResources().getString(R.string.no_internet_error_msg);
//Or any other depends on activity context to be live like dailog
}
}
if (getActivity() == null) return;
работает и в некоторых случаях. Просто прервите выполнение кода и убедитесь, что приложение не падает.
В моем случае методы фрагмента были вызваны после
getActivity().onBackPressed();
Я столкнулся с той же проблемой, я просто добавил экземпляр Singleton, чтобы получить ресурс, как указано Эриком
MainFragmentActivity.defaultInstance().getResources().getString(R.string.app_name);
, вы также можете использовать
getActivity().getResources().getString(R.string.app_name);
Я надеюсь, что это поможет.
Старый пост, но я был удивлен самым голосуемым ответом.
Надлежащее решение для этого должно быть, чтобы отменить asynctask в onStop (или, где это уместно в вашем фрагменте). Таким образом, вы не вносите утечку памяти (асинхронную задачу, сохраняющую ссылку на ваш уничтоженный фрагмент), и вы лучше контролируете то, что происходит в вашем фрагменте.
@Override
public void onStop() {
super.onStop();
mYourAsyncTask.cancel(true);
}
Если вы расширяете класс Application
и поддерживаете статический «глобальный» объект Context, как показано ниже, вы можете использовать его вместо действия для загрузки ресурса String.
public class MyApplication extends Application {
public static Context GLOBAL_APP_CONTEXT;
@Override
public void onCreate() {
super.onCreate();
GLOBAL_APP_CONTEXT = this;
}
}
Если вы используете это, вы можете обойтись без Toast
и загрузки ресурсов, не беспокоясь о жизненных циклах.
Я столкнулся с похожими проблемами, когда была видна активность настроек приложения с загруженными настройками. Если бы я изменил одно из предпочтений, а затем повернул отображаемое содержимое и снова изменил это предпочтение, он вылетел бы с сообщением о том, что фрагмент (мой класс предпочтений) не был прикреплен к действию.
При отладке он выглядел так, как будто метод onCreate () для PreferencesFragment вызывался дважды, когда содержимое дисплея вращалось. Это было уже достаточно странно. Затем я добавил проверку isAdded () вне блока, где он будет указывать на сбой, и это решило проблему.
Вот код слушателя, который обновляет сводку предпочтений, чтобы показать новую запись. Он находится в методе onCreate () моего класса Preferences, который расширяет класс PreferenceFragment:
public static class Preferences extends PreferenceFragment {
SharedPreferences.OnSharedPreferenceChangeListener listener;
@Override
public void onCreate(Bundle savedInstanceState) {
// ...
listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
// check if the fragment has been added to the activity yet (necessary to avoid crashes)
if (isAdded()) {
// for the preferences of type "list" set the summary to be the entry of the selected item
if (key.equals(getString(R.string.pref_fileviewer_textsize))) {
ListPreference listPref = (ListPreference) findPreference(key);
listPref.setSummary("Display file content with a text size of " + listPref.getEntry());
} else if (key.equals(getString(R.string.pref_fileviewer_segmentsize))) {
ListPreference listPref = (ListPreference) findPreference(key);
listPref.setSummary("Show " + listPref.getEntry() + " bytes of a file at once");
}
}
}
};
// ...
}
Я надеюсь, что это поможет другим!