getActivity () gibt in der Fragmentfunktion null zurück

191

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 nenne (aus 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

Lukap
quelle
Ich bin mir nicht sicher, ob beim Einfügen in diesen Beitrag nur ein Fehler aufgetreten ist, aber Sie benötigen danach eine Klammer getActivity(). Wie instanziieren Sie das Fragment? Hast du es in deiner layout.xml?
CaseyB
Wohin gehört das zweite Codefragment? Zur oncreate () - Methode der Aktivität? Und haben Sie schon setContentView () aufgerufen?
Franziskus Karsunke
R.id.upperPar ist ein Element im Layout, daher sollte es durch das Fragment ersetzt werden, aber das ist nicht mein Problem. Ich verstehe nicht, warum ich null bekomme, wenn ich getActivity () in benutzerdefinierten Fragmentmethoden aufrufe, sagen wir in der onActivityCreated-Methode. GetActivity ist die tatsächliche Aktivität, die nicht null ist
Lukap
das Problem nicht in den Layouts ist, funktioniert die App gut , aber warum bekomme ich null für die getActivity, btw alle Elemente einschließlich des Fragments es , wie es sollte keine Probleme hier gemacht wird?
Lukap
1
Sie sollten diese Methode aufrufen: f1.asd (); in der onActivityCreated-Methode, die in Ihrer Fragmentklasse überschrieben werden soll.
Namrata Bagerwal

Antworten:

164

commit Plant die Transaktion, dh sie geschieht nicht sofort, sondern wird beim nächsten Bereitstellen des Hauptthreads als Arbeit am Hauptthread geplant.

Ich würde vorschlagen, eine hinzuzufügen

onAttach(Activity activity)

Methode zu Ihrem Fragmentund setzen Sie einen Haltepunkt darauf und sehen Sie, wann es relativ zu Ihrem Aufruf an aufgerufen wird asd(). Sie werden sehen, dass es nach der Methode aufgerufen wird, mit der Sie asd()Exits aufrufen . Der onAttachAufruf ist der FragmentOrt, an dem der an seine Aktivität angehängt ist und ab diesem Zeitpunkt getActivity()ungleich Null zurückgibt (nb gibt es auch einen onDetach()Anruf).

PJL
quelle
5
Ich habe nicht verstanden, wie Sie Ihr Problem lösen können. Wie kann ich die Referenz des FragmentActivity-Objekts abrufen, wenn meine getActivity () nicht bereit ist?
CeccoCQ
2
@Vivek Ich weiß nicht genau, was du erreichen willst. Wenn Sie das Fragment benötigen, um ein Dialogfeld sofort anzuzeigen, lassen Sie es das tun, was es bei der Erstellung tun muss, z. B. in seinen onCreateViewoder onActivityCreatedMethoden. Ich frage mich, warum asd () aufgerufen werden muss, wenn dies in der Posting-Frage der Fall ist.
PJL
3
onAttach veraltet
abbasalim
6
onAttach (Aktivität mActivity) scheint abgeschrieben zu werden .. jede
Problemumgehung
4
API 24 eingeführtcommitNow()
Nicolas
92

Das Beste, um dies zu beseitigen, ist, die Aktivitätsreferenz beizubehalten, wenn onAttach aufgerufen wird, und die Aktivitätsreferenz zu verwenden, wo immer dies erforderlich ist, z

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

@Override
public void onDetach() {
    super.onDetach();
    mContext = null;
}
Pawan Maheshwari
quelle
34
Sollten wir mActivity = null onDetach () setzen?
Oliver Pearmain
5
@OliverPearmain Wenn Sie dies in onDetach () tun, wird es keinen Gewinn geben. Sie müssen es in onDestory () aufheben. Außerdem musst du es in WeakRefernce halten.
Kirill Popov
Ich mache es in beiden Fällen ungültig onDestroy()und onDetach()weil onDestroy()nicht garantiert wird, dass es aufgerufen wird.
Mohammed Ali
8
Lecken wir das, Activitywenn wir es nicht aufheben onDestroy()?
Mohammed Ali
2
Laut developer.android.com/intl/zh-tw/guide/components/… wird onAttach () aufgerufen, bevor onCreateView () aufgerufen wird. Ich erhalte jedoch weiterhin eine NullPointerException, während ich getActivity () in onCreateView () aufrufe. Wie konnte das passieren?
Kimi Chiu
81

Dies geschah, wenn Sie einen getActivity()anderen Thread aufrufen , der beendet wurde, nachdem das Fragment entfernt wurde. Der typische Fall ist das Aufrufen getActivity()(z. B. für a Toast), wenn eine HTTP-Anforderung beendet wurde (inonResponse z. B. z. B. in).

Um dies zu vermeiden, können Sie einen Feldnamen definieren mActivityund stattdessen verwenden getActivity(). 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;
    }
}
}

Viel Spaß beim Codieren,

thucnguyen
quelle
19
sollen wir mActivity = null setzen; in onDetach ()?
Bharat Dodeja
thucnguyen, wie wäre es, mActivity static für Single Activity App zu deklarieren?
iamthevoid
Ich sehe überhaupt keinen Grund, Activityvon einem anderen Thread aus zuzugreifen . Sie können sowieso nichts damit anfangen, nicht einmal einen Toast zeigen. Sie sollten also entweder zuerst die Arbeit auf den Hauptthread übertragen oder Activity überhaupt nicht verwenden.
Dzmitry Lazerka
4
@BharatDodeja sollten wir mActivity = null onDetach setzen? Hast du herausgefunden?
SLearner
5
Dies wird auslaufen, ohne die Aktivität aufzuheben.
Darek Deoniziak
30

Die anderen Antworten, die darauf hindeuten, einen Verweis auf die Aktivität in onAttach beizubehalten, deuten lediglich auf ein Bandaid für das eigentliche Problem hin. Wenn getActivity null zurückgibt, bedeutet dies, dass das Fragment nicht an die Aktivität angehängt ist. Am häufigsten geschieht dies, wenn die Aktivität aufgrund einer Rotation oder eines Abschlusses der Aktivität verschwunden ist, das Fragment jedoch noch eine Art Rückruf-Listener registriert hat. Wenn der Hörer angerufen wird, wenn Sie etwas mit der Aktivität tun müssen, die Aktivität jedoch nicht mehr vorhanden ist, können Sie nicht viel tun. In Ihrem Code sollten Sie nur überprüfengetActivity() != nullund wenn es nicht da ist, dann tu nichts. Wenn Sie einen Verweis auf die Aktivität behalten, die nicht mehr vorhanden ist, verhindern Sie, dass die Aktivität durch Müll gesammelt wird. Alle UI-Dinge, die Sie möglicherweise versuchen, werden vom Benutzer nicht gesehen. Ich kann mir einige Situationen vorstellen, in denen Sie im Callback-Listener möglicherweise einen Kontext für etwas haben möchten, das nicht mit der Benutzeroberfläche zu tun hat. In diesen Fällen ist es wahrscheinlich sinnvoller, den Anwendungskontext abzurufen. Beachten Sie, dass der einzige Grund dafür, dass der onAttachTrick kein großer Speicherverlust ist, darin besteht, dass er normalerweise nach der Ausführung des Rückruf-Listeners nicht mehr benötigt wird und zusammen mit dem Fragment, all seinen Ansichten und dem Aktivitätskontext Müll gesammelt werden kann. Wenn dusetRetainInstance(true) Es besteht eine größere Wahrscheinlichkeit eines Speicherverlusts, da das Feld "Aktivität" ebenfalls beibehalten wird. Nach der Drehung kann dies jedoch die vorherige Aktivität sein, nicht die aktuelle.

Miguel
quelle
1
Das ist genau mein Problem. Ich habe ein Fragment, das einen Prozess ausführt -> dann wird eine Anzeige angezeigt -> und dann wird der Prozess fortgesetzt. Auf einigen Geräten ist getActivity () nach der Rückkehr von der Anzeige (über einen Listener zu den Anzeigenereignissen) null. Aber ich muss den anderen Teil der Arbeit fortsetzen, um den Job zu beenden. Meinst du, es gibt keine Lösung dafür?
Notbad
Genau dem stehe ich gegenüber. Ich habe eine Aktivitätsschnittstelle in einem Fragment, in der ich einige Abrechnungsaufgaben erledige. Nachdem die Zahlung abgeschlossen ist, möchte ich die Schnittstelle verwenden, um etwas zu tun, aber die Schnittstelle war null.
Freddie
Dies scheint die richtige allgemeine Antwort auf Hunderte von SO-Fragen zu diesem Thema zu sein.
Manuel
Beste Antwort. Es gibt so viele Bandaid-Lösungen für Android auf SO.
Maxbeaudoin
Wenn ich also eine Operation ausführen möchte, wie kann ich sie ausführen, nachdem getActivity () verfügbar ist (wenn überhaupt).
Sreekanth Karumanaghat
17

Seit Android API Level 23 ist onAttach (Aktivitätsaktivitä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 überprüfen können, ob es sich bei dem Kontext um eine Aktivität handelt, können Sie sie bei Bedarf umwandeln.

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

    Activity a;

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

}
Sachin
quelle
Wie man das benutzt
ARR.s
10

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

  1. definierte globale Variablen für Fragment:

    private final Object attachingActivityLock = new Object();

    private boolean syncVariable = false;

  2. implementiert

@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, im Thread abgeschlossen, 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 dies (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 Folgendes tun kann:

image.post(new Runnable() {

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

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

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

Ich musste einige Arbeiten ausführen, die einige Ansichten beinhalteten, daher funktionierte onAttach () für mich nicht. es stürzte ab. Also habe ich einen Teil meines Codes verschoben, der einige Parameter in einer Methode festlegte, die direkt nach commit () (1.) aufgerufen wurde, und dann den anderen Teil des Codes, der die Ansicht in onCreateView () (3.) verarbeitet hat.

Bogdan Zurac
quelle
3

Ich benutze OkHttp und habe mich gerade diesem Problem gestellt.


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 (z. B. in onResponse).

Einige HTTP-Aufrufe wurden auch nach Abschluss der Aktivität ausgeführt (da es eine Weile dauern kann, bis eine HTTP-Anforderung abgeschlossen ist). Ich habe dann durch das HttpCallbackversucht, einige Fragmentfelder zu aktualisieren und habe nullbeim Versuch eine Ausnahme bekommen getActivity().

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

IMO besteht die Lösung darin , Rückrufe zu verhindern, wenn das Fragment nicht mehr lebt (und das nicht nur bei Okhttp).

Das Update: Prävention.

Wenn Sie sich den Fragmentlebenszyklus ansehen (weitere Informationen hier ), werden Sie feststellen, dass es onAttach(Context context)und gibtonDetach() Methoden. Diese werden aufgerufen, nachdem das Fragment zu einer Aktivität gehört und kurz bevor es aufhört.

Das bedeutet, dass wir diesen Rückruf verhindern können, indem wir ihn in der onDetachMethode 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();
    }
}
zurfyx
quelle
2

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

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

Phạm Lam
quelle
1

Gehen Sie wie folgt vor. Ich denke, es wird Ihnen hilfreich sein.

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
quelle
1

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

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

In den meisten Fällen reicht es aus, den Kontext zu speichern. 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 integrieren 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.

Shai
quelle
0

Sie können onAttach verwenden oder wenn Sie onAttach nicht überall einsetzen möchten, können Sie eine Methode einfügen, die ApplicationContext für die Haupt-App-Klasse zurückgibt:

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 bei Ihnen nicht funktioniert.

surga
quelle
0

Eine weitere gute Lösung wäre die Verwendung von Android LiveData mit MVVM-Architektur. Sie würden ein LiveData-Objekt in Ihrem ViewModel definieren und es in Ihrem Fragment beobachten. Wenn der LiveData-Wert geändert wird, wird Ihr Beobachter (in diesem Fall das Fragment) nur benachrichtigt, wenn sich Ihr Fragment im aktiven Zustand befindet, sodass Sie garantiert sind würde Ihre Benutzeroberfläche funktionieren lassen und nur dann auf die Aktivität zugreifen, wenn sich Ihr Fragment im aktiven Zustand befindet. Dies ist ein Vorteil, der damit verbunden ist LiveData

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

Serdar Samancıoğlu
quelle
0

Rufen Sie die Methode getActivity () in onActivityCreated () auf.

MuM6oJuM6o
quelle