java.lang.IllegalStateException: фрагмент не приєднаний до активності


148

Я рідко отримую цю помилку під час здійснення дзвінка API.

java.lang.IllegalStateException: Fragment  not attached to Activity

Я спробував ввести код всередині isAdded()методу, щоб перевірити, чи доданий фрагмент до його активності, але все-таки я рідко отримує цю помилку. Я не розумію, чому я все ще отримую цю помилку. Як я можу запобігти це?

Помилка відображення на рядку-

cameraInfo.setId(getResources().getString(R.string.camera_id));

Нижче наведено зразок виклику api, який я роблю.

SAPI.getInfo(getActivity(),
                new APIResponseListener() {
                    @Override
                    public void onResponse(Object response) {


                        cameraInfo = new SInfo();
                        if(isAdded()) {
                            cameraInfo.setId(getResources().getString(R.string.camera_id));
                            cameraInfo.setName(getResources().getString(R.string.camera_name));
                            cameraInfo.setColor(getResources().getString(R.string.camera_color));
                            cameraInfo.setEnabled(true);
                        }


                    }

                    @Override
                    public void onError(VolleyError error) {
                        mProgressDialog.setVisibility(View.GONE);
                        if (error instanceof NoConnectionError) {
                            String errormsg = getResources().getString(R.string.no_internet_error_msg);
                            Toast.makeText(getActivity(), errormsg, Toast.LENGTH_LONG).show();
                        }
                    }
                });

cameraInfo.setId (getActivity (). getResources (). getString (R.string.camera_id));
Ешвін Н

Відповіді:


203

Ця помилка трапляється через комбіновану дію двох факторів:

  • Після завершення запит HTTP викликає onResponse()або onError()( або які працюють в основному потоці), не знаючи, чи Activityвсе ще на передньому плані чи ні. Якщо значення Activityпішло (користувач перемістився в іншому місці), getActivity()повертає null.
  • Воллей Responseвиражається як анонімний внутрішній клас, який неявно має чітке посилання на зовнішній Activityклас. Це призводить до класичного витоку пам'яті.

Щоб вирішити цю проблему, слід завжди:

Activity activity = getActivity();
if(activity != null){

    // etc ...

}

а також використовувати isAdded()в onError()методі також:

@Override
public void onError(VolleyError error) {

    Activity activity = getActivity(); 
    if(activity != null && isAdded())
        mProgressDialog.setVisibility(View.GONE);
        if (error instanceof NoConnectionError) {
           String errormsg = getResources().getString(R.string.no_internet_error_msg);
           Toast.makeText(activity, errormsg, Toast.LENGTH_LONG).show();
        }
    }
}

2
При використанні запитів і волейбольних запитів AsyncTaskзсередини Activity, не існує надійного способу уникнути NPE. Завжди є ймовірність, що користувач може відійти від поточного, Activityпоки одна з ниток робить щось на задньому плані, і тоді, коли нитка завершується і onPostExecute()або onResponse()викликається, її немає Activity. Все, що ви можете зробити - це перевірити наявність нульових посилань у різних точках вашого коду, і це не є бронезахисним :)
YS

2
Тест на андроїд мавпи (мавпа adb shell) справді добре виправляє цю помилку, якщо ви її ще не врахували загально / глобально.
Groovee60

5
isAdded () достатньо, остаточний загальнодоступний логічний isAdded () {повернути mActivity! = null && mAdded; }
lannyf

2
@ruselli: Перевіряє addedбулевий прапор і чи є поточний Activityекземпляр nullчи ні.
YS

1
@gauravjain Уникайте асинхронних запитів (наприклад, HTTP-дзвінків) безпосередньо з фрагментів. Зробіть це з Діяльності, і це повинно бути добре. Крім того, очистіть посилання на фрагменти від FragmentManager, це хороша практика і найкращий спосіб уникнути витоку пам'яті.
YS

56

Життєвий цикл фрагмента дуже складний і повний помилок, спробуйте додати:

Activity activity = getActivity(); 
if (isAdded() && activity != null) {
...
}

2
Куди мені його поставити?
Вацлова Рекашіус-молодший

1
@ VaclovasRekašiusJr. Здається, майже все, де ви хочете отримати доступ до дії зсередини фрагмента. Весело!
TylerJames

2
що мені робити, якщо активність == null. щоб залишитися в живих моя заява @Miroslav
pavel

Подивіться на isAdded () , Ви можете виявити, що "активність! = Null" не є зайвою
BertKing,

@BertKing return mHost != null && mAdded;- це метод всередині fragment.isAdded (). Я подумав, що mHost - це діяльність, якщо простежити її, але, схоже, mHost знаходиться всередині FragmentActivity. Отже, напевно, ви маєте рацію. Будь-які доповнення?
Джонні П'ять

14

Я знайшов дуже просте рішення isAdded () метод, який є одним із методів фрагмента, щоб визначити, що цей поточний фрагмент приєднаний до своєї активності чи ні.

ми можемо використовувати це як і скрізь у класі фрагментів, наприклад:

if(isAdded())
{

// using this method, we can do whatever we want which will prevent   **java.lang.IllegalStateException: Fragment not attached to Activity** exception.

}

12

Виняток: java.lang.IllegalStateException: фрагмент

DeadlineListFragment {ad2ef970} не додано до активності

Категорія: Життєвий цикл

Опис : Під час виконання трудомістких операцій у фоновому потоці (наприклад, AsyncTask) тим часом був створений новий Фрагмент і був відключений до Діяльності до закінчення фонового потоку. Код у потоці користувальницького інтерфейсу (наприклад, onPostExecute) викликає окремий фрагмент, викидаючи такий виняток.

Виправити рішення:

  1. Скасуйте фонову нитку, призупинивши або зупинивши фрагмент

  2. Використовуйте isAdded (), щоб перевірити, чи доданий фрагмент, а потім дістатиResources () від активності.


11

я можу запізнитися, але я можу допомогти кому-небудь ...

як, як показано нижче

icon = MyApplication.getInstance().getString(R.string.weather_thunder);

Ось клас додатків

public class MyApplication extends Application {

    private static MyApplication mInstance;
    private RequestQueue mRequestQueue;

    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;
    }

    public static synchronized MyApplication getInstance() {
        return mInstance;
    }
}

1
Так, цей метод також широко застосовується.
CoolMind

1
Це не мудрий варіант. FragmentContext і ApplicationContext мають різні стилі. Контекст фрагмента може мати темну тему, користувацький стиль, місце розташування тощо. Хоча ApplicationContext може не залучати правильний ресурс. Якщо у вас немає контексту, ви не повинні намагатися рендерувати цей ресурс.
Джемшит Іскендеров

2

Ця помилка може статися, якщо ви створюєте фрагмент, який якимось чином неможливо ініціювати:

Fragment myFragment = MyFragment.NewInstance();


public classs MyFragment extends Fragment {
  public void onCreate() {
   // Some error here, or anywhere inside the class is preventing it from being instantiated
  }
}

У моєму випадку я зіткнувся з цим, коли намагався використовувати:

private String loading = getString(R.string.loading);

2

У використанні фрагмента isAdded() повернеться істинним, якщо фрагмент зараз приєднаний до активності.

Якщо ви хочете перевірити активність

 Fragment fragment = new MyFragment();
   if(fragment.getActivity()!=null)
      { // your code here}
      else{
       //do something
       }

Сподіваюся, це комусь допоможе


1

Я прийняв такий підхід до вирішення цього питання. Створено новий клас, який виступає в якості обгортки для таких методів діяльності

public class ContextWrapper {
    public static String getString(Activity activity, int resourceId, String defaultValue) {
        if (activity != null) {
            return activity.getString(resourceId);
        } else {
            return defaultValue;
        }
    }

    //similar methods like getDrawable(), getResources() etc

}

Тепер, де мені потрібно отримати доступ до ресурсів з фрагментів або діяльності, замість того, щоб безпосередньо викликати метод, я використовую цей клас. Якщо діяльність contextне є, nullвона повертає вартість активу, а у випадку -context є нульовим, воно передає значення за замовчуванням (яке також визначається абонентом функції).

Важливо Це не рішення, це ефективний спосіб, коли ви можете виправити цю аварію витончено. Ви хочете додати кілька журналів у випадках, коли ви отримуєте екземпляр діяльності як нульовий, і спробуйте це виправити, якщо це можливо.


0

це трапляється, коли фрагмент не має контексту, тому метод getActivity () повертає нуль. перевірте, чи використовуєте ви контекст перед тим, як отримати його , чи активність більше не існує. використовувати контекст у fragment.onCreate і після відповіді api зазвичай трапляється ця проблема


0

Іноді цей виняток викликаний помилкою в реалізації бібліотеки підтримки. Нещодавно мені довелося знизити з 26.1.0 до 25.4.0, щоб позбутися цього.


Ні, я ні, але, можливо, я мушу створити його.
Bord81

0

Ця проблема виникає щоразу, коли ви викликаєте контекст, який недоступний або недійсний при його виклику. Це може бути ситуація, коли ви викликаєте контекст потоку основної активності на фоновому потоці або контексті фонового потоку на потоці основної активності.

Наприклад, я оновив спільний рядок уподобань, як:

editor.putString("penname",penNameEditeText.getText().toString());
editor.commit();
finish();

І називається закінчення () відразу після нього. Тепер це робиться так, що, коли команда виконує основний потік і зупиняє будь-які інші зобов’язання Async, якщо йде, поки вона не завершиться. Таким чином, її контекст залишається живим до тих пір, поки запис не завершиться. Отже, попередній контекст є "живим", через що виникає помилка.

Тому переконайтесь, що ваш код повторно переглянуто, якщо є якийсь код, що має цю проблему з контекстом.


як вам вдалося виправити це питання? Я називаю це в потоці асинхронізації, і я відчуваю цю проблему зараз.
Саймон

Просто переконайтеся, що операція запису закінчена, тоді лише контекст вбивається не до завершення операції запису.
Prashant Paliwal
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.