java.lang.IllegalStateException: Fragmen tidak dilampirkan ke Activity


148

Saya jarang mendapatkan kesalahan ini saat melakukan panggilan API.

java.lang.IllegalStateException: Fragment  not attached to Activity

Saya mencoba memasukkan kode ke dalam isAdded()metode untuk memeriksa apakah fragmen saat ini ditambahkan ke aktivitasnya tetapi saya masih jarang mendapatkan kesalahan ini. Saya gagal memahami mengapa saya masih mendapatkan kesalahan ini. Bagaimana saya bisa mencegahnya?

Ini menunjukkan kesalahan pada baris-

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

Di bawah ini adalah contoh panggilan api yang saya buat.

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));
Ashwin H

Jawaban:


203

Kesalahan ini terjadi karena efek gabungan dari dua faktor:

  • Permintaan HTTP, ketika selesai, memanggil salah satu onResponse()atau onError()(yang bekerja pada utas utama) tanpa mengetahui apakah Activitymasih di latar depan atau tidak. Jika Activityhilang (pengguna menavigasi ke tempat lain), getActivity()mengembalikan nol.
  • Voli Responsedinyatakan sebagai kelas dalam anonim, yang secara implisit memegang referensi kuat ke Activitykelas luar . Ini menghasilkan kebocoran memori klasik.

Untuk mengatasi masalah ini, Anda harus selalu melakukan:

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

    // etc ...

}

dan juga, gunakan isAdded()dalam onError()metode ini juga:

@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
Saat menggunakan permintaan Volley dan AsyncTasks dari dalam Activity, tidak ada cara mudah untuk menghindari NPE. Selalu ada kemungkinan bahwa pengguna dapat menavigasi menjauh dari saat ini Activitysementara salah satu utas sedang melakukan sesuatu di latar belakang, dan kemudian ketika utas selesai dan onPostExecute()atau onResponse()dipanggil, tidak ada Activity. Yang dapat Anda lakukan adalah melakukan pengecekan untuk referensi nol di berbagai titik dalam kode Anda, dan itu bukan antipeluru :)
YS

2
Tes android monyet (adb shell monkey) benar-benar bagus untuk membasmi kesalahan ini jika Anda belum memperhitungkannya secara generik / global.
Groovee60

5
isAdded () sudah cukup, boolean publik akhir isAdded () {return mActivity! = null && mAdded; }
lannyf

2
@ruselli: Memeriksa addedbendera boolean dan apakah Activityinstance saat ini adalah nullatau tidak.
YS

1
@gauravjain Hindari membuat permintaan asinkron (seperti panggilan HTTP) langsung dari Fragmen. Lakukan dari Kegiatan dan harus baik-baik saja. Juga, hapus referensi Fragmen dari FragmentManager, ini adalah praktik yang baik dan merupakan cara terbaik untuk menghindari kebocoran memori.
YS

56

Siklus hidup fragmen sangat kompleks dan penuh bug, coba tambahkan:

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

2
Di mana saya harus meletakkannya?
Vaclovas Rekašius Jr.

1
@ VaclovasRekašiusJr. Sepertinya cukup banyak di mana saja Anda ingin mengakses Kegiatan dari dalam fragmen. Menyenangkan!
TylerJames

2
apa yang harus saya lakukan jika aktivitas == null. agar aplikasi saya tetap hidup @Miroslav
pavel

Lihatlah isAdded () , Anda mungkin menemukan "aktivitas! = Null" tidak mubazir
BertKing

@BertKing return mHost != null && mAdded;- Itulah yang ada di dalam metode fragment.isAdded (). Saya pikir mHost adalah sebuah Kegiatan jika Anda melacaknya, tetapi sepertinya mHost ada di dalam FragmentActivity. Jadi, mungkin Anda benar. Adakah tambahan?
Johnny Five

14

Saya Menemukan Solusi Sangat Sederhana yang Diisi () metode yang merupakan salah satu metode fragmen untuk mengidentifikasi bahwa fragmen saat ini melekat pada Aktivitasnya atau tidak.

kita dapat menggunakan ini seperti di mana saja di kelas fragmen seperti:

if(isAdded())
{

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

}

12

Pengecualian: java.lang.IllegalStateException: Fragment

DeadlineListFragment {ad2ef970} tidak dilampirkan ke Activity

Kategori: Siklus Hidup

Deskripsi : Saat melakukan operasi yang memakan waktu di utas latar (mis., AsyncTask), sementara itu Fragmen baru telah dibuat, dan dilepaskan ke Aktivitas sebelum utas latar belakang selesai. Kode di utas UI (misalnya, onPostExecute) memanggil Fragment yang terpisah, melempar pengecualian tersebut.

Perbaiki solusi:

  1. Batalkan utas latar saat menjeda atau menghentikan Fragmen

  2. Gunakan isAdded () untuk memeriksa apakah fragmen dilampirkan dan kemudian untuk getResources () dari aktivitas.


11

saya mungkin terlambat tetapi dapat membantu seseorang ..... Solusi terbaik untuk ini adalah membuat instance kelas aplikasi global dan menyebutnya dalam fragmen khusus di mana aktivitas Anda tidak dilampirkan

seperti di bawah ini

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

Ini adalah kelas aplikasi

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
Ya, metode ini juga banyak digunakan.
CoolMind

1
Ini bukan pilihan bijak. FragmentContext dan ApplicationContext memiliki gaya yang berbeda. Fragmen Konteks mungkin memiliki tema gelap, gaya khusus, Lokal dll. Yang akan menarik warna, sumber daya string dari file yang berbeda. Sementara ApplicationContext mungkin tidak menarik sumber daya yang benar. Jika Anda tidak memiliki Konteks, maka Anda tidak boleh mencoba membuat sumber daya itu.
Jemshit Iskenderov

2

Kesalahan ini dapat terjadi jika Anda membuat sebuah fragmen yang entah bagaimana tidak dapat dipakai:

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
  }
}

Dalam kasus saya, saya telah bertemu ini ketika saya mencoba menggunakan:

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

2

Sedang digunakan Fragment isAdded() Ini akan mengembalikan true jika fragmen saat ini dilampirkan ke Aktivitas.

Jika Anda ingin memeriksa bagian dalam Activity

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

Semoga ini bisa membantu seseorang


1

Saya mengadopsi pendekatan berikut untuk menangani masalah ini. Menciptakan kelas baru yang bertindak sebagai pembungkus untuk metode aktivitas seperti ini

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

}

Sekarang di mana pun saya perlu mengakses sumber daya dari fragmen atau kegiatan, alih-alih langsung memanggil metode, saya menggunakan kelas ini. Jika aktivitasnya contexttidak nullmengembalikan nilai aset dan jika contextbernilai nol, akan melewati nilai default (yang juga ditentukan oleh penelepon fungsi).

Penting Ini bukan solusi, ini adalah cara yang efektif di mana Anda dapat menangani kecelakaan ini dengan anggun. Anda ingin menambahkan beberapa log jika Anda mendapatkan instance aktivitas sebagai nol dan coba memperbaikinya, jika memungkinkan.


0

ini terjadi ketika fragmen tidak memiliki konteks, sehingga metode getActivity () mengembalikan nol. periksa apakah Anda menggunakan konteksnya sebelum Anda mendapatkannya, atau jika Activity tidak ada lagi. gunakan konteks di fragment.onCreate dan setelah respon api biasanya kasus masalah ini


0

Terkadang pengecualian ini disebabkan oleh bug dalam implementasi perpustakaan dukungan. Baru-baru ini saya harus menurunkan versi dari 26.1.0 ke 25.4.0 untuk menyingkirkannya.


Tidak, saya tidak, tapi mungkin saya harus membuatnya.
Bord81

0

Masalah ini terjadi setiap kali Anda memanggil konteks yang tidak tersedia atau nol saat Anda menyebutnya. Ini bisa menjadi situasi ketika Anda memanggil konteks utas aktivitas utama pada utas latar belakang atau konteks utas latar belakang pada utas aktivitas utama.

Misalnya, saya memperbarui string preferensi bersama saya seperti berikut.

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

Dan disebut finish () tepat setelah itu. Sekarang apa yang dilakukannya adalah bahwa commit berjalan di utas utama dan menghentikan komitmen Async lainnya jika datang sampai selesai. Jadi konteksnya hidup sampai penulisan selesai. Karenanya konteks sebelumnya adalah langsung, menyebabkan kesalahan terjadi.

Jadi pastikan kode Anda diperiksa ulang jika ada beberapa kode yang memiliki masalah konteks ini.


bagaimana Anda bisa memperbaiki masalah ini? Saya menyebutnya dalam utas async dan saya mengalami masalah ini sekarang.
Simon

Pastikan saja bahwa operasi tulis selesai, maka hanya konteksnya yang dimatikan tidak sebelum penyelesaian operasi penulisan.
Prashant Paliwal
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.