getActivity () mengembalikan null dalam fungsi Fragment

191

Saya memiliki fragmen (F1) dengan metode publik seperti ini

public void asd() {
    if (getActivity() == null) {
        Log.d("yes","it is null");
    }
}

dan ya ketika saya menyebutnya (dari Aktivitas), itu adalah nol ...

FragmentTransaction transaction1 = getSupportFragmentManager().beginTransaction();
F1 f1 = new F1();
transaction1.replace(R.id.upperPart, f1);
transaction1.commit();
f1.asd();

Pasti sesuatu yang saya lakukan sangat salah, tetapi saya tidak tahu apa itu

Lukap
sumber
Saya tidak yakin apakah ada kesalahan saat Anda menempelkannya di posting ini, tetapi Anda harus tanda kurung setelahnya getActivity(). Juga, bagaimana Anda membuat fragmen? Apakah Anda memilikinya di layout.xml Anda?
CaseyB
Di mana fragmen kode kedua termasuk? Ke metode oncreate () - dari Kegiatan? Dan sudahkah Anda memanggil setContentView ()?
Franziskus Karsunke
R.id.upperPar adalah elemen dalam tata letak, jadi itu seharusnya diganti dengan fragmen, tapi itu bukan masalah saya. Saya tidak mengerti mengapa saya mendapatkan nol ketika saya memanggil getActivity () dalam metode fragmen khusus, katakanlah pada metode onActivityCreated getActivity adalah aktivitas aktual yang tidak null
Lukap
masalah itu tidak dalam layout, aplikasi bekerja baik tetapi mengapa saya mendapatkan null untuk getActivity, btw semua elemen termasuk fragmen itu diberikan seperti seharusnya tidak masalah di sini?
Lukap
1
Anda harus memanggil metode ini: f1.asd (); dalam metode onActivityCreated yang akan ditimpa di kelas fragmen Anda.
Namrata Bagerwal

Jawaban:

164

commit menjadwalkan transaksi, yaitu tidak terjadi langsung tetapi dijadwalkan sebagai bekerja pada utas utama saat berikutnya utas utama siap.

Saya sarankan menambahkan

onAttach(Activity activity)

metode untuk Anda Fragmentdan menempatkan titik istirahat di atasnya dan melihat ketika itu dipanggil relatif terhadap panggilan Anda asd(). Anda akan melihat bahwa itu dipanggil setelah metode di mana Anda membuat panggilan untuk asd()keluar. The onAttachpanggilan di mana Fragmentmelekat pada aktivitas dan dari titik ini getActivity()akan kembali non-null (nb ada juga onDetach()panggilan).

PJL
sumber
5
Saya tidak mengerti bagaimana Anda bisa menyelesaikan masalah Anda. Jika getActivity () saya belum siap, bagaimana saya bisa mendapatkan referensi objek FragmentActivity?
CeccoCQ
2
@Vivek Saya tidak tahu apa yang ingin Anda capai. Jika Anda memerlukan Fragmen untuk menampilkan dialog langsung maka minta ia melakukan apa yang perlu dilakukan pada penciptaan, misalnya dalam onCreateViewatau onActivityCreatedmetode. Saya mempertanyakan mengapa ASD () perlu dipanggil ketika itu dilakukan dalam posting pertanyaan.
PJL
3
onAttach ditinggalkan
abbasalim
6
onAttach (Activity mActivity) tampaknya didepresiasi .. solusi apa pun untuk ini
ashish.n
4
API 24 diperkenalkancommitNow()
Nicolas
92

Cara terbaik untuk menghilangkannya adalah menyimpan referensi aktivitas saat onAttach dipanggil dan menggunakan referensi aktivitas di mana pun dibutuhkan, misalnya

@Override
public void onAttach(Context context) {
    super.onAttach(activity);
    mContext = context;
}

@Override
public void onDetach() {
    super.onDetach();
    mContext = null;
}
Pawan Maheshwari
sumber
34
Haruskah kita menetapkan mActivity = null onDetach ()?
Oliver Pearmain
5
@OliverPearmain jika Anda melakukannya di onDetach () maka tidak akan ada untung. Anda harus membatalkannya di onDestory (). Selanjutnya Anda harus menahannya di WeakRefernce.
Kirill Popov
Saya membatalkannya di kedua onDestroy()dan onDetach()karena onDestroy()tidak dijamin dipanggil.
Mohammed Ali
8
Apakah kita membocorkan Activityjika kita tidak membatalkannya onDestroy()?
Mohammed Ali
2
Menurut developer.android.com/intl/zh-tw/guide/components/… , onAttach () dipanggil sebelum memanggil onCreateView (). Tapi saya masih mendapatkan NullPointerException ketika saya memanggil getActivity () di onCreateView (). Bagaimana itu bisa terjadi?
Kimi Chiu
81

Ini terjadi ketika Anda memanggil getActivity()utas lain yang selesai setelah fragmen telah dihapus. Kasus khas adalah panggilan getActivity()(mis. Untuk a Toast) saat permintaan HTTP selesai ( onResponsemisalnya).

Untuk menghindari ini, Anda bisa menentukan nama bidang mActivitydan menggunakannya getActivity(). Bidang ini dapat diinisialisasi dalam metode Fragmen onAttach () sebagai berikut:

@Override
public void onAttach(Context context) {
    super.onAttach(context);

    if (context instanceof Activity){
        mActivity =(Activity) context;
    }
}

Dalam proyek saya, saya biasanya menentukan kelas dasar untuk semua Fragmen saya dengan fitur ini:

public abstract class BaseFragment extends Fragment {

    protected FragmentActivity mActivity;

    @Override
public void onAttach(Context context) {
    super.onAttach(context);

    if (context instanceof Activity){
        mActivity =(Activity) context;
    }
}
}

Selamat coding,

thucnguyen
sumber
19
haruskah kita menetapkan mActivity = null; di onDetach ()?
Bharat Dodeja
thucnguyen, bagaimana kalau menyatakan mActivity statis untuk aplikasi aktivitas tunggal?
iamthevoid
Saya tidak melihat alasan untuk mengakses Activitydari utas lainnya sama sekali. Anda tidak dapat melakukan apa pun dengan itu, bahkan tidak menunjukkan Toast. Jadi Anda harus mentransfer pekerjaan ke utas utama terlebih dahulu, atau tidak menggunakan Activity sama sekali.
Dzmitry Lazerka
4
@ BharatDodeja haruskah kita atur mActivity = null onDetach? Apakah kamu mengetahuinya?
SLearner
5
Ini akan bocor tanpa membatalkan aktivitas.
Darek Deoniziak
30

Jawaban lain yang menyarankan menyimpan referensi ke aktivitas di onAttach hanya menyarankan bandaid untuk masalah sebenarnya. Ketika getActivity mengembalikan nol, itu berarti Fragmen tidak dilampirkan ke Aktivitas. Paling umum hal ini terjadi ketika Aktivitas telah hilang karena rotasi atau Aktivitas selesai tetapi Fragmen masih memiliki semacam pendengar panggilan balik yang terdaftar. Ketika pendengar dipanggil jika Anda perlu melakukan sesuatu dengan Kegiatan tetapi Kegiatan itu hilang tidak banyak yang bisa Anda lakukan. Dalam kode Anda, Anda hanya harus memeriksagetActivity() != nulldan jika tidak ada maka jangan lakukan apa-apa. Jika Anda menyimpan referensi ke Aktivitas yang hilang, Anda mencegah agar Aktivitas tidak menjadi sampah yang dikumpulkan. Segala hal UI yang mungkin Anda coba lakukan tidak akan terlihat oleh pengguna. Saya bisa membayangkan beberapa situasi di mana dalam pemanggil panggilan balik Anda mungkin ingin memiliki Konteks untuk sesuatu yang tidak terkait UI, dalam kasus-kasus itu mungkin lebih masuk akal untuk mendapatkan konteks Aplikasi. Perhatikan bahwa satu-satunya alasan onAttachtrik ini bukan kebocoran memori yang besar adalah karena biasanya setelah pemanggil panggil balik dijalankan itu tidak akan diperlukan lagi dan dapat menjadi sampah yang dikumpulkan bersama dengan Fragment, semua View-nya dan konteks Activity. Jika kamusetRetainInstance(true) ada kemungkinan lebih besar kebocoran memori karena bidang Aktivitas juga akan dipertahankan tetapi setelah rotasi yang bisa menjadi Aktivitas sebelumnya bukan yang sekarang.

miguel
sumber
1
Inilah masalah saya. Saya memiliki fragmen yang melakukan proses -> kemudian iklan ditampilkan -> dan kemudian proses berlanjut. Di beberapa perangkat setelah kembali dari iklan (melalui pendengar ke acara iklan) getActivity () adalah nol. Tetapi saya harus terus melakukan bagian lain dari pekerjaan untuk menyelesaikan pekerjaan. Apakah maksud Anda tidak ada solusi untuk ini?
Notbad
Ini persis apa yang saya hadapi. Saya memiliki antarmuka Aktivitas dalam sebuah fragmen di mana saya melakukan beberapa hal penagihan. Setelah pembayaran selesai, saya ingin menggunakan antarmuka untuk melakukan sesuatu, tetapi antarmuka telah nol.
Freddie
Ini tampaknya menjadi jawaban umum yang benar untuk 100-an pertanyaan SO untuk topik ini.
Manuel
Jawaban Terbaik. Ada begitu banyak solusi bandaid untuk Android di SO.
maxbeaudoin
Jadi Jika saya ingin melakukan beberapa operasi, bagaimana saya bisa menjalankan setelah getActivity () tersedia (jika memang demikian).
Sreekanth Karumanaghat
17

Sejak Android API level 23, onAttach (Aktivitas aktivitas) telah ditinggalkan. Anda perlu menggunakan onAttach (Konteks konteks). http://developer.android.com/reference/android/app/Fragment.html#onAttach(android.app.Activity)

Aktivitas adalah konteks, jadi jika Anda bisa mengecek konteksnya adalah sebuah Aktivitas dan melemparkannya jika perlu.

@Override
public void onAttach(Context context) {
    super.onAttach(context);

    Activity a;

    if (context instanceof Activity){
        a=(Activity) context;
    }

}
Sachin
sumber
Bagaimana cara menggunakannya
ARR.s
10

PJL benar. Saya telah menggunakan sarannya dan inilah yang telah saya lakukan:

  1. variabel global yang didefinisikan untuk fragmen:

    private final Object attachingActivityLock = new Object();

    private boolean syncVariable = false;

  2. diimplementasikan

@Override
public void onAttach(Activity activity) {
  super.onAttach(activity);
  synchronized (attachingActivityLock) {
      syncVariable = true;
      attachingActivityLock.notifyAll();
  }
}

3. Saya membungkus fungsi saya, di mana saya perlu memanggil getActivity (), di utas, karena jika itu akan berjalan pada utas utama, saya akan memblokir utas dengan langkah 4. dan onAttach () tidak akan pernah dipanggil.

    Thread processImage = new Thread(new Runnable() {

        @Override
        public void run() {
            processImage();
        }
    });
    processImage.start();

4. dalam fungsi saya di mana saya perlu memanggil getActivity (), saya menggunakan ini (sebelum panggilan getActivity ())

    synchronized (attachingActivityLock) {
        while(!syncVariable){
            try {
                attachingActivityLock.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

Jika Anda memiliki beberapa pembaruan UI, ingatlah untuk menjalankannya di utas UI. Saya perlu memperbarui ImgeView jadi saya lakukan:

image.post(new Runnable() {

    @Override
    public void run() {
        image.setImageBitmap(imageToShow);
    }
});
zajac.m2
sumber
7

Urutan panggilan dipanggil setelah commit ():

  1. Metode apa pun yang Anda panggil secara manual segera setelah komit ()
  2. onAttach ()
  3. onCreateView ()
  4. onActivityCreated ()

Saya perlu melakukan beberapa pekerjaan yang melibatkan beberapa Tampilan, jadi onAttach () tidak bekerja untuk saya; itu jatuh. Jadi saya memindahkan bagian dari kode saya yang mengatur beberapa params di dalam metode yang disebut setelah komit () (1.), kemudian bagian lain dari kode yang menangani tampilan di dalam onCreateView () (3.).

Bogdan Zurac
sumber
3

Saya menggunakan OkHttp dan saya baru saja menghadapi masalah ini.


Untuk bagian pertama @thucnguyen berada di jalur yang benar .

Ini terjadi ketika Anda memanggil getActivity () di utas lain yang selesai setelah fragmen dihapus. Kasus khasnya adalah memanggil getActivity () (mis. Untuk Toast) ketika permintaan HTTP selesai (misalnya di onResponse).

Beberapa panggilan HTTP sedang dieksekusi bahkan setelah aktivitas telah ditutup (karena itu bisa memakan waktu beberapa saat untuk permintaan HTTP diselesaikan). Saya kemudian, melalui HttpCallbackmencoba memperbarui beberapa bidang Fragmen dan mendapat nullpengecualian ketika mencoba getActivity().

http.newCall(request).enqueue(new Callback(...
  onResponse(Call call, Response response) {
    ...
    getActivity().runOnUiThread(...) // <-- getActivity() was null when it had been destroyed already

IMO solusinya adalah untuk mencegah panggilan balik terjadi ketika fragmen tidak lagi hidup lagi (dan itu tidak hanya dengan Okhttp).

Cara mengatasinya: Pencegahan.

Jika Anda melihat siklus fragmen (info lebih lanjut di sini ), Anda akan melihat ada onAttach(Context context)dan onDetach()metode. Ini dipanggil setelah Fragmen milik aktivitas dan sesaat sebelum berhenti menjadi masing-masing.

Itu berarti bahwa kita dapat mencegah panggilan balik itu terjadi dengan mengendalikannya dalam onDetachmetode.

@Override
public void onAttach(Context context) {
    super.onAttach(context);

    // Initialize HTTP we're going to use later.
    http = new OkHttpClient.Builder().build();
}

@Override
public void onDetach() {
    super.onDetach();

    // We don't want to receive any more information about the current HTTP calls after this point.
    // With Okhttp we can simply cancel the on-going ones (credits to https://github.com/square/okhttp/issues/2205#issuecomment-169363942).
    for (Call call : http.dispatcher().queuedCalls()) {
        call.cancel();
    }
    for (Call call : http.dispatcher().runningCalls()) {
        call.cancel();
    }
}
zurfyx
sumber
2

Di mana Anda memanggil fungsi ini? Jika Anda memanggilnya di konstruktor Fragment, ia akan kembali null.

Panggil saja getActivity()ketika metode onCreateView()ini dijalankan.

Phạm Lam
sumber
1

Lakukan sebagai berikut. Saya pikir ini akan sangat membantu Anda.

private boolean isVisibleToUser = false;
private boolean isExecutedOnce = false;


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View root = inflater.inflate(R.layout.fragment_my, container, false);
    if (isVisibleToUser && !isExecutedOnce) {
        executeWithActivity(getActivity());
    }
    return root;
}

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    this.isVisibleToUser = isVisibleToUser;
    if (isVisibleToUser && getActivity()!=null) {
        isExecutedOnce =true;
        executeWithActivity(getActivity());
    }
}


private void executeWithActivity(Activity activity){
    //Do what you have to do when page is loaded with activity

}
Vinil Chandran
sumber
1

Mereka yang masih memiliki masalah dengan onAttach (Aktivitas aktivitas), Itu baru saja berubah menjadi Konteks -

    @Override
public void onAttach(Context context) {
    super.onAttach(context);
    this.context = context;
}

Dalam kebanyakan kasus, menyimpan konteks akan cukup untuk Anda - misalnya jika Anda ingin mendapatkan getResources () Anda dapat melakukannya langsung dari konteksnya. Jika Anda masih perlu membuat konteks ke dalam Aktivitas Anda, lakukanlah -

 @Override
public void onAttach(Context context) {
    super.onAttach(context);
    mActivity a; //Your activity class - will probably be a global var.
    if (context instanceof mActivity){
        a=(mActivity) context;
    }
}

Seperti yang disarankan oleh user1868713.

Shai
sumber
0

Anda bisa menggunakan onAttach atau jika Anda tidak ingin memakai onAttach di mana-mana maka Anda bisa meletakkan metode yang mengembalikan ApplicationContext pada kelas App utama:

public class App {
    ...  
    private static Context context;

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

    public static Context getContext() {
        return context;
    }
    ...
}

Setelah itu Anda dapat menggunakannya kembali di mana-mana di seluruh proyek Anda, seperti ini:

App.getContext().getString(id)

Tolong beri tahu saya jika ini tidak berhasil untuk Anda.

surga
sumber
0

Solusi bagus lainnya adalah menggunakan LiveData Android dengan arsitektur MVVM. Anda akan mendefinisikan objek LiveData di dalam ViewModel Anda dan mengamatinya di fragmen Anda, dan ketika nilai LiveData diubah, itu akan memberi tahu pengamat Anda (fragmen dalam kasus ini) hanya jika fragmen Anda dalam keadaan aktif, sehingga dijamin bahwa Anda akan akan membuat UI Anda berfungsi dan mengakses aktivitas hanya ketika fragmen Anda dalam keadaan aktif. Ini adalah salah satu keuntungan yang hadir dengan LiveData

Tentu saja ketika pertanyaan ini pertama kali ditanyakan, tidak ada LiveData. Saya meninggalkan jawaban ini di sini karena seperti yang saya lihat, masih ada masalah ini dan itu bisa membantu seseorang.

Serdar Samancıoğlu
sumber
0

Panggil metode getActivity () di dalam onActivityCreated ()

MuM6oJuM6o
sumber