getActivity () zwraca null w funkcji Fragment


191

Mam fragment (F1) z taką metodą publiczną

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

i tak, kiedy nazywam to (z działania), jest ono zerowe ...

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

To musi być coś, co robię bardzo źle, ale nie wiem co to jest


Nie jestem pewien, czy po wklejeniu go do tego postu wystąpił błąd, ale po nim potrzebujesz nawiasu getActivity(). Ponadto, jak tworzysz fragment tego fragmentu? Czy masz go w pliku layout.xml?
CaseyB

Gdzie należy drugi fragment kodu? Do oncreate () - metoda działania? A czy już wywołałeś setContentView ()?
Franziskus Karsunke

R.id.upperPar jest elementem układu, więc powinien zostać zastąpiony fragmentem, ale to nie jest mój problem. Nie rozumiem, dlaczego otrzymuję wartość NULL, gdy wywołuję metodę getActivity () w niestandardowych metodach fragmentów, powiedzmy, że w metodzie onActivityCreated metoda getActivity jest faktyczną aktywnością
inną

problem nie występuje w układach, aplikacja działa dobrze, ale dlaczego dostaję zero dla getActivity ?, przy okazji wszystkich elementów, w tym fragmentu, który jest renderowany, jakby nie powinien się tutaj
pojawiać

1
Powinieneś wywołać tę metodę: f1.asd (); w metodzie onActivityCreated, która ma zostać zastąpiona w klasie fragmentu.
Namrata Bagerwal

Odpowiedzi:


164

commit planuje transakcję, tzn. nie dzieje się to od razu, ale jest zaplanowane jako praca nad głównym wątkiem, gdy następny wątek będzie gotowy.

Sugeruję dodanie

onAttach(Activity activity)

metodę Fragmenti umieszczenie na niej punktu przerwania i sprawdzenie, kiedy jest wywoływane względem twojego wezwania do asd(). Zobaczysz, że jest wywoływany po metodzie, w której wykonujesz wezwanie do asd()wyjścia. onAttachPołączenia jest, gdy Fragmentjest zamocowana do jego aktywności i z tego punktu getActivity()powróci non null (NB jest również onDetach()połączenie).


5
Nie rozumiem, jak możesz rozwiązać swój problem. Jeśli moja metoda getActivity () nie jest jeszcze gotowa, jak mogę uzyskać odwołanie do obiektu FragmentActivity?
CeccoCQ,

2
@Vivek Nie wiem dokładnie, co chcesz osiągnąć. Jeśli potrzebujesz fragmentu, aby wyświetlić okno dialogowe razu wtedy zrobić to, co trzeba zrobić w sprawie utworzenia, na przykład w jego onCreateViewlub onActivityCreatedmetod. Zastanawiam się, dlaczego należy wywoływać funkcję asd (), gdy ma to miejsce podczas zamieszczania pytań.
PJL

3
onAttach deprecated
abbasalim 18.08.16

6
onAttach (Activity mActivity) wydaje się być amortyzowany .. wszelkie obejścia tego
ashish.n

4
Wprowadzono API 24commitNow()
Nicolas

92

Najlepszym sposobem na pozbycie się tego jest zachowanie odwołania do aktywności po wywołaniu funkcji onAttach i używanie odwołania do aktywności tam, gdzie jest to potrzebne, na przykład

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

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

34
Czy powinniśmy ustawić mActivity = null onDetach ()?
Oliver Pearmain

5
@OliverPearmain, jeśli zrobisz to w onDetach (), wtedy nie będzie zysku. Musisz go unieważnić w onDestory (). Ponadto musisz trzymać go w WeakRefernce.
Kirill Popov

Ja znoszący go w obu onDestroy()i onDetach()dlatego onDestroy()nie jest gwarantowana na miano.
Mohammed Ali,

8
Czy wyciekujemy, Activityjeśli nie unieważnimy go onDestroy()?
Mohammed Ali,

3
Według developer.android.com/intl/zh-tw/guide/components/… , onAttach () jest wywoływany przed wywołaniem onCreateView (). Ale nadal otrzymuję wyjątek NullPointerException podczas wywoływania metody getActivity () w funkcji onCreateView (). Jak to się mogło stać?
Kimi Chiu,

81

Stało się tak, gdy wywołujesz getActivity()inny wątek, który zakończył się po usunięciu fragmentu. Typowym przypadkiem jest wywołanie getActivity()(np. Dla a Toast), gdy żądanie HTTP zakończy się ( onResponsena przykład).

Aby tego uniknąć, możesz zdefiniować nazwę pola mActivityi używać jej zamiast getActivity(). To pole można zainicjować w metodzie onAttach () Fragment w następujący sposób:

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

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

W moich projektach zwykle definiuję klasę bazową dla wszystkich moich fragmentów za pomocą tej funkcji:

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

Szczęśliwego kodowania,


19
ustawimy mActivity = null; w onDetach ()?
Bharat Dodeja

thucnguyen, co powiesz na zadeklarowanie statyczności mActivity dla pojedynczej aplikacji?
iamthe Unik

W ogóle nie widzę powodu, aby uzyskiwać dostęp Activityz innego wątku. I tak nie możesz nic z tym zrobić, nawet nie pokazywać Toastu. Dlatego albo najpierw przenieś pracę do głównego wątku, albo w ogóle nie używaj Aktywności.
Dzmitry Lazerka,

4
@BharatDodeja czy powinniśmy ustawić mActivity = null onDetach? Dowiedziałeś się?
SLearner

5
To wycieknie bez zerowania aktywności.
Darek Deoniziak

30

Inne odpowiedzi, które sugerują zachowanie odniesienia do aktywności w onAttach, sugerują bandażowanie prawdziwego problemu. Kiedy getActivity zwraca null, oznacza to, że Fragment nie jest dołączony do Działania. Najczęściej dzieje się tak, gdy działanie zniknęło z powodu rotacji lub działania zostało zakończone, ale we fragmencie nadal jest zarejestrowany jakiś odbiornik wywołania zwrotnego. Kiedy słuchacz zostaje wezwany, jeśli musisz coś zrobić z Działaniem, ale Działanie zniknęło, niewiele możesz zrobić. W swoim kodzie powinieneś po prostu sprawdzićgetActivity() != nulla jeśli go nie ma, nie rób nic. Jeśli zachowasz odniesienie do działania, które zniknęło, uniemożliwisz zbieranie śmieci. Użytkownik nie będzie widział żadnych elementów interfejsu użytkownika, które możesz spróbować. Mogę sobie wyobrazić niektóre sytuacje, w których w detektorze wywołania zwrotnego może być potrzebny kontekst dla czegoś niezwiązanego z interfejsem użytkownika, w takich przypadkach prawdopodobnie bardziej sensowne jest uzyskanie kontekstu aplikacji. Zauważ, że jedynym powodem, dla którego onAttachsztuczka nie jest dużym przeciekiem pamięci, jest to, że normalnie po wykonaniu nasłuchiwania oddzwaniania nie będzie już potrzebna i można ją wyrzucać do śmieci wraz z fragmentem, wszystkimi jego widokami i kontekstem działania. Jeśli tysetRetainInstance(true) istnieje większa szansa na wyciek pamięci, ponieważ pole Działanie również zostanie zachowane, ale po rotacji może to być poprzednie Działanie, a nie bieżące.


1
To jest dokładnie mój problem. Mam fragment, który wykonuje proces -> następnie wyświetla się reklama -> a następnie proces trwa. W niektórych urządzeniach po powrocie z reklamy (przez detektor do zdarzeń reklamowych) getActivity () ma wartość zerową. Ale muszę kontynuować wykonywanie drugiej części pracy, aby zakończyć pracę. Czy masz na myśli, że nie ma na to rozwiązania?
Notbad

Właśnie z tym mam do czynienia. Mam interfejs działania w fragmencie, w którym wykonuję pewne czynności związane z rozliczeniami. Po dokonaniu płatności chcę użyć interfejsu, aby coś zrobić, ale interfejs jest pusty.
Freddie

To wydaje się być poprawną ogólną odpowiedzią na setki SO pytań na ten temat.
Manuel

Najlepsza odpowiedź. Jest tak wiele rozwiązań bandaid dla Androida na SO.
maxbeaudoin

Więc jeśli chcę wykonać jakąś operację, jak mogę wykonać, gdy getActivity () jest dostępne (jeśli w ogóle jest).
Sreekanth Karumanaghat

17

Od wersji Androida API 23, funkcja onAttach (działalność związana z aktywnością) jest przestarzała. Musisz użyć onAttach (kontekst kontekstowy). http://developer.android.com/reference/android/app/Fragment.html#onAttach(android.app.Activity)

Aktywność jest kontekstem, więc jeśli możesz po prostu sprawdzić, czy kontekst jest Aktywnością i przesłać go w razie potrzeby.

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

    Activity a;

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

}

Jak tego użyć
ARR.s

10

PJL ma rację. Skorzystałem z jego sugestii i właśnie to zrobiłem:

  1. zdefiniowane zmienne globalne dla fragmentu:

    private final Object attachingActivityLock = new Object();

    private boolean syncVariable = false;

  2. zaimplementowano

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

3) Zakończyłem moją funkcję, w której muszę wywołać getActivity (), w wątku, ponieważ gdyby działał na wątku głównym, zablokowałbym wątek krokiem 4. i onAttach () nigdy nie zostałby wywołany.

    Thread processImage = new Thread(new Runnable() {

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

4 w mojej funkcji, w której muszę wywołać getActivity (), używam tego (przed wywołaniem getActivity ())

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

Jeśli masz jakieś aktualizacje interfejsu użytkownika, pamiętaj, aby uruchomić je w wątku interfejsu użytkownika. Muszę zaktualizować ImgeView, więc zrobiłem:

image.post(new Runnable() {

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

7

Kolejność wywoływania wywołań zwrotnych po zatwierdzeniu ():

  1. Jakąkolwiek metodę wywołujesz ręcznie zaraz po zatwierdzeniu ()
  2. onAttach ()
  3. onCreateView ()
  4. onActivityCreated ()

Musiałem wykonać pracę, która obejmowała niektóre widoki, więc onAttach () nie działało dla mnie; rozbiło się. Więc przesunąłem część mojego kodu, który ustawiał niektóre parametry w metodzie o nazwie zaraz po commit () (1.), a następnie drugą część kodu, która obsługiwała widok wewnątrz onCreateView () (3.).


3

Używam OkHttp i właśnie napotkałem ten problem.


W pierwszej części @thucnguyen był na dobrej drodze .

Stało się tak, gdy wywołasz getActivity () w innym wątku, który zakończył się po usunięciu fragmentu. Typowym przypadkiem jest wywołanie getActivity () (np. Dla Toast) po zakończeniu żądania HTTP (na przykład w onResponse).

Niektóre połączenia HTTP były wykonywane nawet po zamknięciu działania (ponieważ wykonanie żądania HTTP może chwilę potrwać). Następnie HttpCallbackspróbowałem zaktualizować niektóre pola Fragment i dostałem nullwyjątek przy próbie getActivity().

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

IMO rozwiązaniem jest zapobieganie wywołaniom zwrotnym, gdy fragment nie jest już żywy (i to nie tylko z Okhttp).

Poprawka: Zapobieganie.

Jeśli spojrzysz na cykl życia fragmentu (więcej informacji tutaj ), zauważysz, że istnieją onAttach(Context context)i onDetach()metody. Są one wywoływane, gdy Fragment należy do działania i tuż przed tym, zanim przestaną być.

Oznacza to, że możemy zapobiec temu wywołaniu zwrotnemu, kontrolując je w onDetachmetodzie.

@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

Jak wywołujesz tę funkcję? Jeśli wywołasz to w konstruktorze Fragment, zwróci null.

Wystarczy wywołać, getActivity()gdy metoda onCreateView()zostanie wykonana.


1

Wykonaj następujące czynności. Myślę, że będzie ci to pomocne.

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

Ci, którzy nadal mają problem z onAttach (Aktywność), Właśnie zmieniono na Kontekst -

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

W większości przypadków wystarczy zapisać kontekst - na przykład, jeśli chcesz wykonać getResources (), możesz to zrobić bezpośrednio z kontekstu. Jeśli nadal musisz włączyć kontekst do działania, zrób to -

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

Zgodnie z sugestią użytkownika 1868713.


0

Możesz użyć onAttach lub jeśli nie chcesz wszędzie włączaćAttach, możesz umieścić metodę, która zwraca ApplicationContext na głównej klasie aplikacji:

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

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

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

Następnie możesz ponownie użyć go w dowolnym miejscu w całym projekcie, w ten sposób:

App.getContext().getString(id)

Daj mi znać, jeśli to nie zadziała.


0

Innym dobrym rozwiązaniem byłoby użycie LiveData Androida z architekturą MVVM. Zdefiniowałbyś obiekt LiveData w swoim ViewModel i obserwowałbyś go we fragmencie, a gdy wartość LiveData zostanie zmieniona, powiadomiłby swojego obserwatora (w tym przypadku fragment) tylko wtedy, gdy twój fragment jest w stanie aktywnym, więc zagwarantowanie, że sprawi, że interfejs użytkownika będzie działał, a dostęp do działania będzie możliwy tylko wtedy, gdy fragment będzie w stanie aktywnym. Jest to jedna zaleta związana z LiveData

Oczywiście, kiedy zadawano to pytanie po raz pierwszy, nie było LiveData. Pozostawiam tę odpowiedź tutaj, ponieważ, jak widzę, nadal występuje ten problem i może być komuś pomocny.


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.