getActivity () restituisce null nella funzione Frammento


192

Ho un frammento (F1) con un metodo pubblico come questo

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

e sì quando lo chiamo (dall'attività), è null ...

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

Deve essere qualcosa che sto facendo molto male, ma non so cosa sia


Non sono sicuro che ci sia stato solo un errore quando lo hai incollato in questo post, ma dopo hai bisogno di parentesi getActivity(). Inoltre, come stai istanziando il frammento? Ce l'hai nel tuo layout.xml?
CaseyB,

Dove appartiene il secondo frammento di codice? Al metodo oncreate () - dell'attività? E hai già chiamato setContentView ()?
Franziskus Karsunke,

R.id.upperPar è un elemento nel layout, quindi dovrebbe essere sostituito con il frammento, ma non è un mio problema. Non capisco perché ottengo null quando chiamo getActivity () nei metodi dei frammenti personalizzati, diciamo nel metodo onActivityCreated getActivity è l'attività effettiva non nulla
Lukap

il problema non è nei layout, l'app funziona bene ma perché ottengo null per getActivity?, tra tutti gli elementi incluso il frammento che viene reso come se non dovesse emettere problemi qui
Lukap

1
Dovresti chiamare questo metodo: f1.asd (); nel metodo onActivityCreated che deve essere sostituito nella classe del frammento.
Namrata Bagerwal,

Risposte:


164

commit pianifica la transazione, ovvero non avviene immediatamente ma è pianificata come lavoro sul thread principale la volta successiva che il thread principale è pronto.

Suggerirei di aggiungere un

onAttach(Activity activity)

metodo al tuo Fragmente mettere un punto di interruzione su di esso e vedere quando viene chiamato in relazione alla tua chiamata asd(). Vedrai che viene chiamato dopo il metodo in cui effettui la chiamata per asd()uscire. La onAttachchiamata è dove il Fragmentè collegato alla sua attività e da questo punto getActivity()restituirà un valore non nullo (nb c'è anche una onDetach()chiamata).


5
Non ho capito come risolvere il tuo problema. Se il mio getActivity () non è pronto per la stille, come posso ottenere il riferimento all'oggetto FragmentActivity?
CeccoCQ

2
@Vivek Non so esattamente cosa vuoi ottenere. Se hai bisogno che il frammento visualizzi immediatamente una finestra di dialogo, fai che faccia quello che deve fare al momento della creazione, ad esempio nei suoi metodi onCreateViewo onActivityCreated. Mi chiedo perché asd () debba essere chiamato quando lo fa nella pubblicazione delle domande.
PJL

3
onAttach deprecato
abbasalim il

6
onAttach (Activity mActivity) sembra essere deprezzato .. qualsiasi soluzione alternativa per questo
ashish.n

4
Introduzione dell'API 24commitNow()
Nicolas

92

Il modo migliore per sbarazzarsi di questo è mantenere il riferimento all'attività quando viene chiamato onAttach e utilizzare il riferimento all'attività dove necessario, ad es.

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

@Override
public void onDetach() {
    super.onDetach();
    mContext = null;
}

34
Dovremmo impostare mActivity = null onDetach ()?
Oliver Pearmain,

5
@OliverPearmain se lo farai in onDetach (), allora non ci sarà alcun profitto. Devi annullarlo in onDestory (). Inoltre devi tenerlo in WeakRefernce.
Kirill Popov,

Lo sto annullando in entrambi onDestroy()e onDetach()perché onDestroy()non è garantito che venga chiamato.
Mohammed Ali,

8
Perdiamo Activityse non lo annulliamo onDestroy()?
Mohammed Ali,

3
Secondo developer.android.com/intl/zh-tw/guide/components/… , onAttach () viene chiamato prima di chiamare onCreateView (). Ma ricevo ancora una NullPointerException mentre chiamo getActivity () in onCreateView (). Come è potuto succedere?
Kimi Chiu,

82

Questo è successo quando hai chiamato getActivity()un altro thread che è terminato dopo che il frammento è stato rimosso. Il caso tipico è chiamare getActivity()(es. Per a Toast) al termine di una richiesta HTTP ( onResponsead esempio).

Per evitare ciò, è possibile definire un nome campo mActivitye usarlo al posto di getActivity(). Questo campo può essere inizializzato nel metodo onAttach () del frammento come segue:

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

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

Nei miei progetti, di solito definisco una classe base per tutti i miei frammenti con questa funzione:

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

Buona programmazione,


19
dovremmo impostare mActivity = null; in onDetach ()?
Bharat Dodeja,

Cosa pensi di dichiarare mActivity statico per l'app a singola attività?
iamthevoid,

Non vedo alcun motivo per accedere Activityda un altro thread. Non puoi farci niente, nemmeno mostrare un brindisi. Quindi dovresti prima trasferire il lavoro sul thread principale o non usare affatto l'attività.
Dzmitry Lazerka,

4
@BharatDodeja dovremmo impostare mActivity = null onDetach? Lo hai scoperto?
SLearner il

5
Questo sta per perdere senza annullare l'attività.
Darek Deoniziak,

30

Le altre risposte che suggeriscono di mantenere un riferimento all'attività in onAttach stanno solo suggerendo un cerotto al problema reale. Quando getActivity restituisce null significa che il frammento non è collegato all'attività. Più comunemente questo accade quando l'attività è andata via a causa della rotazione o l'attività è finita, ma il frammento ha ancora un qualche tipo di listener di callback registrato. Quando l'ascoltatore viene chiamato se devi fare qualcosa con l'Attività ma l'Attività è sparita non c'è molto che puoi fare. Nel tuo codice dovresti solo controllaregetActivity() != nulle se non è lì, allora non fare nulla. Se si mantiene un riferimento all'attività che è andata, si sta impedendo che l'attività venga raccolta in modo inutile. Qualsiasi operazione dell'interfaccia utente che potresti provare a fare non verrà visualizzata dall'utente. Posso immaginare alcune situazioni in cui nel listener di callback potresti voler avere un contesto per qualcosa di non UI, in quei casi probabilmente ha più senso ottenere il contesto dell'applicazione. Si noti che l'unica ragione per cui il onAttachtrucco non è una grande perdita di memoria è perché normalmente dopo l'esecuzione del listener di callback non sarà più necessario e può essere spazzato via insieme al frammento, a tutte le sue viste e al contesto delle attività. Se tusetRetainInstance(true) c'è una maggiore possibilità di perdita di memoria perché anche il campo Attività verrà mantenuto, ma dopo la rotazione potrebbe essere l'attività precedente non quella corrente.


1
Questo è esattamente il mio problema. Ho un frammento che esegue un processo -> quindi viene mostrato un annuncio -> e quindi il processo continua. In alcuni dispositivi dopo il ritorno dall'annuncio (tramite un listener agli eventi dell'annuncio) getActivity () è null. Ma devo continuare a fare l'altra parte del lavoro per finire il lavoro. Vuoi dire che non c'è soluzione a questo?
Notbad,

Questo è esattamente quello che sto affrontando. Ho un'interfaccia di attività in un frammento in cui faccio alcune cose di fatturazione. Dopo aver effettuato il pagamento, desidero utilizzare l'interfaccia per fare qualcosa, ma l'interfaccia è stata nulla.
Freddie il

Questa sembra essere la risposta generale corretta a centinaia di domande SO a questo argomento.
Manuel,

Migliore risposta. Ci sono così tante soluzioni bandaid per Android su SO.
maxbeaudoin,

Quindi, se voglio fare qualche operazione, come posso eseguire dopo che getActivity () è disponibile (se non lo è affatto).
Sreekanth Karumanaghat,

17

Dal livello API 23 di Android, onAttach (Attività attività) è diventato obsoleto. È necessario utilizzare onAttach (contesto di contesto). http://developer.android.com/reference/android/app/Fragment.html#onAttach(android.app.Activity)

L'attività è un contesto, quindi se puoi semplicemente controllare il contesto è un'attività e lanciarla se necessario.

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

    Activity a;

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

}

Come usarlo
ARR.s

10

PJL ha ragione. Ho usato il suo suggerimento e questo è quello che ho fatto:

  1. variabili globali definite per frammento:

    private final Object attachingActivityLock = new Object();

    private boolean syncVariable = false;

  2. implementato

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

3 Ho concluso la mia funzione, dove ho bisogno di chiamare getActivity (), nel thread, perché se fosse eseguito sul thread principale, bloccherei il thread con il passaggio 4. e onAttach () non verrebbe mai chiamato.

    Thread processImage = new Thread(new Runnable() {

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

4 nella mia funzione in cui devo chiamare getActivity (), lo uso (prima della chiamata getActivity ())

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

Se si dispone di alcuni aggiornamenti dell'interfaccia utente, ricordarsi di eseguirli sul thread dell'interfaccia utente. Devo aggiornare ImgeView, quindi ho fatto:

image.post(new Runnable() {

    @Override
    public void run() {
        image.setImageBitmap(imageToShow);
    }
});

7

L'ordine in cui vengono richiamati i callback dopo commit ():

  1. Qualunque metodo tu chiami manualmente subito dopo commit ()
  2. onAttach ()
  3. onCreateView ()
  4. onActivityCreated ()

Avevo bisogno di fare un lavoro che coinvolgesse alcune viste, quindi onAttach () non ha funzionato per me; si è rotto. Quindi ho spostato parte del mio codice che stava impostando alcuni parametri all'interno di un metodo chiamato subito dopo commit () (1.), quindi l'altra parte del codice che gestiva la vista all'interno di onCreateView () (3.).


3

Sto usando OkHttp e ho appena affrontato questo problema.


Per la prima parte, @thucnguyen era sulla buona strada .

Questo si è verificato quando si chiama getActivity () in un altro thread terminato dopo la rimozione del frammento. Il caso tipico è chiamare getActivity () (es. Per un Toast) al termine di una richiesta HTTP (ad esempio in onResponse).

Alcune chiamate HTTP venivano eseguite anche dopo che l'attività era stata chiusa (perché il completamento di una richiesta HTTP può richiedere del tempo). Poi, attraverso il HttpCallbacktentativo di aggiornare alcuni campi Frammento e ho ottenuto nullun'eccezione quando ho provato a farlo getActivity().

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

La soluzione IMO è impedire che si verifichino callback quando il frammento non è più in vita (e non solo con Okhttp).

La correzione: prevenzione.

Se dai un'occhiata al ciclo di vita dei frammenti (maggiori informazioni qui ), noterai che ci sono onAttach(Context context)e onDetach()metodi. Questi vengono chiamati dopo che il frammento appartiene a un'attività e poco prima di smettere di esserlo, rispettivamente.

Ciò significa che possiamo impedire che il callback avvenga controllandolo nel onDetachmetodo.

@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();
    }
}

2

Come si chiama questa funzione? Se lo chiami nel costruttore di Fragment, tornerà null.

Basta chiamare getActivity()quando onCreateView()viene eseguito il metodo .


1

Fai come segue. Penso che ti sarà utile.

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

}

1

Coloro che hanno ancora il problema con onAttach (Attività attività), è appena cambiato in Contesto -

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

Nella maggior parte dei casi, salvare il contesto sarà sufficiente per te, ad esempio se vuoi ottenere getResources () puoi farlo direttamente dal contesto. Se hai ancora bisogno di inserire il contesto nella tua attività, fallo -

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

Come suggerito dall'utente1868713.


0

Puoi usare onAttach o se non vuoi mettere onAttach ovunque quindi puoi mettere un metodo che restituisce ApplicationContext sulla classe principale dell'app:

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

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

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

Dopodiché puoi riutilizzarlo ovunque in tutto il tuo progetto, in questo modo:

App.getContext().getString(id)

Per favore fatemi sapere se questo non funziona per voi.


0

Un'altra buona soluzione sarebbe utilizzare LiveData di Android con architettura MVVM. Definiresti un oggetto LiveData all'interno di ViewModel e lo osserveresti nel tuo frammento, e quando il valore di LiveData viene modificato, notificherebbe il tuo osservatore (frammento in questo caso) solo se il tuo frammento è in stato attivo, quindi sarebbe garantito che tu farebbe funzionare la tua UI e accederà all'attività solo quando il tuo frammento è in stato attivo. Questo è un vantaggio offerto da LiveData

Naturalmente quando questa domanda è stata posta per la prima volta, non esisteva LiveData. Lascio qui questa risposta perché, come vedo, c'è ancora questo problema e potrebbe essere utile a qualcuno.


0

Chiama il metodo getActivity () all'interno di onActivityCreated ()

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.