wake-up-neo.com

getActivity () gibt in der Fragment-Funktion null zurück

Ich habe ein Fragment (F1) mit einer öffentlichen Methode wie dieser

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

und ja, wenn ich es anrufe (von der Aktivität), ist es null ...

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

Es muss etwas sein, was ich sehr falsch mache, aber ich weiß nicht, was das ist

160
Lukap

commit plant die Transaktion, d. H. Sie wird nicht sofort ausgeführt, sondern wird als Arbeit für den Haupt-Thread geplant, wenn der Haupt-Thread das nächste Mal bereit ist. 

Ich würde vorschlagen, eine 

onAttach(Activity activity)

methode zu Ihrem Fragment und setzen Sie einen Haltepunkt darauf und sehen, wann es relativ zu Ihrem Aufruf von asd() aufgerufen wird. Sie werden sehen, dass es nach der Methode aufgerufen wird, mit der Sie den Aufruf von asd() beenden. Beim Aufruf von onAttach wird Fragment an seine Aktivität angehängt, und ab diesem Punkt gibt getActivity() Nicht-NULL zurück (nb, es gibt auch einen Aufruf von onDetach()).

150
PJL

Um dies zu vermeiden, sollten Sie die Aktivitätsreferenz beibehalten, wenn onAttach aufgerufen wird, und die Aktivitätsreferenz verwenden, wo immer dies erforderlich ist.

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

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

Dies geschah, wenn Sie getActivity() in einem anderen Thread aufrufen, der beendet wurde, nachdem das Fragment entfernt wurde. Der typische Fall ist der Aufruf von getActivity() (z. B. für eine Toast), wenn eine HTTP-Anforderung abgeschlossen ist (in onResponse zum Beispiel).

Um dies zu vermeiden, können Sie einen Feldnamen mActivity definieren und ihn anstelle von getActivity() verwenden. Dieses Feld kann in der onAttach () - Methode von Fragment wie folgt initialisiert werden:

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

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

In meinen Projekten definiere ich normalerweise eine Basisklasse für alle meine Fragmente mit dieser Funktion:

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

Glückliche Kodierung

66
thucnguyen

Seit Android-API-Level 23 ist onAttach (Activity-Aktivität) veraltet. Sie müssen onAttach (Kontextkontext) verwenden. http://developer.Android.com/reference/Android/app/Fragment.html#onAttach(Android.app.Activity)

Aktivität ist ein Kontext. Wenn Sie also einfach prüfen können, ob der Kontext eine Aktivität ist, können Sie diese gegebenenfalls umsetzen.

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

    Activity a;

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

}
16
Sachin

PJL ist richtig. Ich habe seinen Vorschlag benutzt und das habe ich getan:

  1. definierte globale Variablen für Fragment:

    private final Object attachingActivityLock = new Object();

    private boolean syncVariable = false;

  2. umgesetzt

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

3 . Ich habe meine Funktion, in der ich getActivity () aufrufen muss, in Thread eingebunden, da ich den Thread mit Schritt 4 blockieren würde, wenn er auf dem Hauptthread ausgeführt würde, und onAttach () niemals aufgerufen würde.

    Thread processImage = new Thread(new Runnable() {

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

4. In meiner Funktion, in der ich getActivity () aufrufen muss, verwende ich diese Funktion (vor dem Aufruf von getActivity ()).

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

Wenn Sie einige UI-Updates haben, denken Sie daran, diese auf dem UI-Thread auszuführen. Ich muss ImgeView aktualisieren, damit ich das getan habe:

image.post(new Runnable() {

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

Die Reihenfolge, in der die Rückrufe nach commit () aufgerufen werden:

  1. Welche Methode Sie auch direkt nach commit () manuell aufrufen
  2. onAttach ()
  3. onCreateView ()
  4. onActivityCreated ()

Ich musste einige Arbeit machen, die einige Ansichten beinhaltete, so dass onAttach () für mich nicht funktionierte. es stürzte ab. Also habe ich einen Teil meines Codes verschoben, der einige Parameter innerhalb einer Methode festlegte, die direkt nach commit () (1.) aufgerufen wurde, dann den anderen Teil des Codes, der view in onCreateView () (3.) behandelte.

7
Bogdan Zurac

Die anderen Antworten, die darauf schließen lassen, dass ein Hinweis auf die Aktivität in onAttach beibehalten wird, schlagen lediglich eine unverbindliche Antwort auf das eigentliche Problem vor. Wenn getActivity null zurückgibt, bedeutet dies, dass das Fragment nicht an die Aktivität angehängt ist. Dies geschieht meistens, wenn die Aktivität aufgrund einer Rotation oder der Beendigung der Aktivität verschwunden ist, das Fragment jedoch über eine Art Callback-Listener verfügt. Wenn der Listener angerufen wird, wenn Sie etwas mit der Aktivität tun müssen, aber die Aktivität nicht mehr vorhanden ist, können Sie nicht viel tun. In Ihrem Code sollten Sie einfach getActivity() != null überprüfen und wenn nicht, dann tun Sie nichts. Wenn Sie einen Verweis auf die gelöschte Aktivität behalten, verhindern Sie, dass die Aktivität gesammelt wird. Alle UI-Dinge, die Sie versuchen könnten, werden vom Benutzer nicht gesehen. Ich kann mir einige Situationen vorstellen, in denen Sie im Callback-Listener möglicherweise einen Kontext für etwas außerhalb der Benutzeroberfläche verwenden möchten. In diesen Fällen ist es wahrscheinlich sinnvoller, den Anwendungskontext abzurufen. Beachten Sie, dass der Trick onAttach kein einziger großer Speicherverlust ist, da normalerweise der Callback-Listener nicht mehr benötigt wird und der Müll zusammen mit dem Fragment, all seinen Ansichten und dem Aktivitätskontext gesammelt werden kann. Bei setRetainInstance(true) besteht eine größere Wahrscheinlichkeit eines Speicherverlusts, da das Aktivitätsfeld ebenfalls beibehalten wird. Nach einer Rotation kann dies jedoch die vorherige Aktivität und nicht die aktuelle sein.

6
miguel

Ich benutze OkHttp und bin gerade mit diesem Problem konfrontiert.


Für den ersten Teil war @thucnguyen auf dem richtigen Weg .

Dies geschah, wenn Sie getActivity () in einem anderen Thread aufrufen, der beendet wurde, nachdem das Fragment entfernt wurde. Der typische Fall ist der Aufruf von getActivity () (z. B. für einen Toast), wenn eine HTTP-Anforderung abgeschlossen ist (beispielsweise in onResponse).

Einige HTTP-Aufrufe wurden ausgeführt, auch nachdem die Aktivität geschlossen wurde (da es eine Weile dauern kann, bis eine HTTP-Anforderung abgeschlossen ist). Ich habe dann über HttpCallback versucht, einige Fragmentfelder zu aktualisieren, und bekam eine null-Ausnahme, wenn ich getActivity() versuchte.

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

IMO Die Lösung ist, zu verhindern, dass Callbacks auftreten, wenn das Fragment nicht mehr lebt (und das nicht nur bei Okhttp).

Der Fix: Vorbeugung.

Wenn Sie sich den Lebenszyklus von fragment (mehr Infos hier ) anschauen, werden Sie feststellen, dass es onAttach(Context context) und onDetach() Methoden gibt. Diese werden aufgerufen, nachdem das Fragment zu einer Aktivität gehört und kurz bevor es aufhört, so zu sein.

Das bedeutet, dass wir diesen Callback verhindern können, indem wir ihn in der onDetach-Methode steuern.

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

Gehen Sie wie folgt vor. Ich denke, es wird dir helfen.

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
Vinil Chandran

Wo nennen Sie diese Funktion? Wenn Sie es im Konstruktor von Fragment aufrufen, wird null zurückgegeben.

Rufen Sie einfach getActivity() auf, wenn die Methode onCreateView() ausgeführt wird.

1
Phạm Lam

Diejenigen, die immer noch das Problem mit onAttach (Aktivitätsaktivität) haben, haben sich gerade in Kontext geändert - 

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

In den meisten Fällen reicht das Speichern des Kontexts für Sie aus. Wenn Sie beispielsweise getResources () ausführen möchten, können Sie dies direkt aus dem Kontext heraus tun. Wenn Sie den Kontext noch in Ihre Aktivität aufnehmen müssen, tun Sie dies - 

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

Wie von user1868713 vorgeschlagen.

0
Shai

Sie können onAttach verwenden oder wenn Sie nicht überall aufAttach setzen möchten, können Sie eine Methode, die ApplicationContext zurückgibt, in die App-Hauptklasse einfügen:

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

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

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

Danach können Sie es überall in Ihrem Projekt wie folgt wiederverwenden:

App.getContext().getString(id)

Bitte lassen Sie mich wissen, wenn dies für Sie nicht funktioniert.

0
surga

Eine andere gute Lösung wäre die Verwendung von LiveData von Android mit der MVVM-Architektur. Sie definieren ein LiveData-Objekt in Ihrem ViewModel und beobachten es in Ihrem Fragment. Wenn der LiveData-Wert geändert wird, wird nur der Beobachter (Fragment) in diesem Fall benachrichtigt Wenn sich Ihr Fragment im aktiven Status befindet, können Sie also sicherstellen, dass Sie Ihre Benutzeroberfläche aktivieren und nur dann auf die Aktivität zugreifen können, wenn Ihr Fragment im aktiven Status ist. Dies ist ein Vorteil von LiveData

Als diese Frage zum ersten Mal gestellt wurde, gab es natürlich keine LiveData. Ich lasse diese Antwort hier, denn wie ich sehe, gibt es immer noch dieses Problem und es könnte für jemanden hilfreich sein.