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
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()
).
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;
}
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
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;
}
}
PJL ist richtig. Ich habe seinen Vorschlag benutzt und das habe ich getan:
definierte globale Variablen für Fragment:
private final Object attachingActivityLock = new Object();
private boolean syncVariable = false;
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);
}
});
Die Reihenfolge, in der die Rückrufe nach commit () aufgerufen werden:
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.
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.
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).
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();
}
}
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
}
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.
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.
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.
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.