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();
                        }
                    }
                });
Pengembang Android
sumber
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();
        }
    }
}
YS
sumber
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) {
...
}
Miroslav Michalec
sumber
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.

}
Dharmesh Baldha
sumber
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.

Rahil Ali
sumber
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;
    }
}
md gouse
sumber
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);
sagit
sumber
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

Prateek218
sumber
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.

Ezio
sumber
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

penggemar dexian
sumber
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.

Bord81
sumber
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.

Prashant Paliwal
sumber
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