Fragment Androida onAttach () jest przestarzały

326

Zaktualizowałem aplikację, aby korzystała z najnowszej biblioteki obsługi (wersja 23.0.0), dowiedziałem się, że przestała ona działać funkcja onAttach () klasy Fragment.

Zamiast:

onAttach (Activity activity)

Śnieży:

onAttach (Context context)

Ponieważ moja aplikacja korzysta z aktywności przekazanej przed wycofaniem, możliwym rozwiązaniem jest:

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

Czy to byłby właściwy sposób na zrobienie tego?

AKTUALIZACJA:

Jeśli uruchomię urządzenie z interfejsem API niższym niż 23, nowa funkcja onAttach () nawet nie zostanie wywołana. Mam nadzieję, że nie to zamierzali zrobić!

AKTUALIZACJA 2:

Problem został rozwiązany dzięki najnowszym aktualizacjom zestawu SDK.

Testowałem na moim urządzeniu API 22 i wywoływany jest onAttach (kontekst).

Kliknij tutaj, aby śledzić zgłoszenie błędu, które otworzyłem kilka tygodni temu i odpowiedzi od facetów z Google.

TareK Khoury
źródło
Jeśli używasz określonych metod działania z przekazanej instancji, czy próbowałeś rzutować kontekst na działanie? Pamiętaj, aktywność jest podklasą kontekstu. Może casting zadziała.
MarkSkayff,
z jakiegoś powodu onAttach () nawet nie jest wywoływany! jakieś pomysły? próbowałeś zaktualizować do najnowszej biblioteki wsparcia.?
TareK Khoury,
2
Dlaczego interfejs API został przeniesiony Context? Czy i tak nie potrzebujesz Activity, aby dołączyć i wyświetlić fragment? Jak inaczej użyjesz tego Contextparametru?
Kenny Worden,
1
Opublikowałem go jako błąd, patrz link code.google.com/p/android/issues/detail?id=183358
TareK Khoury
6
Aby onAttach(Context context)wywołać nowego , musisz albo użyć urządzenia, które ma co najmniej API 23 LUB użyć android.support.v4.app.Fragment. Zobacz tutaj
Nevzo

Odpowiedzi:

337

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

}

Aktualizacja: Niektórzy twierdzą, że nowe Contextzastąpienie nigdy nie jest wywoływane. Zrobiłem kilka testów i nie mogę znaleźć scenariusza, w którym jest to prawda i zgodnie z kodem źródłowym nigdy nie powinno być to prawdą. We wszystkich testowanych przypadkach, zarówno przed, jak i po SDK23, zarówno wersja, jak Activityi Contextwersja onAttachbyły wywoływane. Jeśli możesz znaleźć scenariusz, w którym tak nie jest, sugeruję utworzenie przykładowego projektu ilustrującego problem i zgłoszenie go zespołowi ds . Androida .

Aktualizacja 2: Zawsze używam fragmentów Biblioteki pomocy technicznej Androida, ponieważ błędy są tam naprawiane szybciej. Wydaje się, że powyższy problem polegający na tym, że przesłonięcia nie są poprawnie wywoływane, wychodzi na jaw tylko wtedy, gdy używasz fragmentów frameworka.

Kuffs
źródło
5
Nie jest to rozwiązanie, ponieważ onAttach z kontekstem nie jest nazywany tak samo jak onAttach z aktywnością. Jeśli masz kod inicjujący on onAttach z działaniem jako parametrem, zmiana ta nie zadziała.
Buddy,
2
Nie, to nie działa tak samo, przynajmniej w moim przypadku. Nie używam aktywności ani kontekstu w OnAttach, wykonuję inicjalizację. Próbowałem zastąpić onAttach przez aktywność na tę z kontekstem i nie jest wywoływana w ten sam sposób. Dlatego nie są one tak łatwe do wymiany.
Buddy,
7
Te same problemy, które ma Buddy ... onAttach(Context)nawet nie wydają się być nazywane.
Stéphane
2
Przejrzałem kod i mogę sprawdzić, czy możliwe jest, że nie można onAttach(context)go wywołać.
Chad Bingham
2
W moim przypadku funkcja onAttach () nie zostanie wywołana, jeśli ponownie uruchomimy działanie, które zostało wcześniej wyczyszczone przez system. Ale nie zawsze tak jest. Umieszczam inicjalizację WeakReference w kontekście onAttach (kontekst kontekstu), refActivity = new WeakReference <> ((AppCompatActivity)). I od czasu do czasu generuje wyjątek NullPointerException w onCreateView ().
Kimi Chiu,
45

Jest to kolejna wielka zmiana od Google ... sugerowanych modyfikacji zamienić onAttach(Activity activity)z onAttach(Context context)rozbił swoje aplikacje na starszych API ponieważ onAttach(Context context)nie zostanie wywołana na rodzime fragmentów.

Korzystam z natywnych fragmentów (android.app.Fragment), więc musiałem wykonać następujące czynności, aby znów działały na starszych interfejsach API (<23).

Oto co zrobiłem:

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

    // Code here
}

@SuppressWarnings("deprecation")
@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
        // Code here
    }
}
Yoann Hercouet
źródło
Mr. Yoann Hercouet, Czy możesz wyjaśnić, jak to zrobić powyżej API 23, uprzejmie pomóż mi, z góry dziękuję, E / AndroidRuntime: FATAL EXCEPTION: main, java.lang.ClassCastException: com.ual.SMS_Activity@afd5683, w Androidzie. support.v4.app.FragmentManagerImpl.moveToState, na android.support.v4.app.FragmentManagerImpl.execSingleAction. Tutaj zamieściłem niezbędne linie, które pozwolą poznać mój błąd.
MohanRaj S
@MohanRajS To samo dotyczy API 24 (tego, którego użyłem), zgodnie z twoim fragmentem kodu, twój problem nie wydaje się mieć z tym nic wspólnego.
Yoann Hercouet
@YoannHercouet, dziękuję za odpowiedź, pozwól mi sprawdzić i zaktualizować.
MohanRaj S
1
Nie jesteś z tego zadowolony z Google z powodu trudności z celowaniem w starsze interfejsy API. Cóż, rozważ błogosławieństwo, że MOŻESZ atakować starsze interfejsy API, podczas gdy Apple, na przykład, po prostu ogromnie cię okaleczy i zmusi do wspierania tylko najnowszych.
Stan
39

Jeśli użyjesz fragmentów frameworka, a wersja SDK urządzenia jest mniejsza niż 23, OnAttach(Context context)nie zostanie wywołana.

Zamiast tego używam fragmentów wsparcia, więc przestanie być naprawione i onAttach(Context context)zawsze wywoływane.

kikermo
źródło
11

Obecnie z onAttachkodu fragmentu nie jest jasne, czy Contextjest to bieżąca aktywność: kod źródłowy

public void onAttach(Context context) {
    mCalled = true;
    final Activity hostActivity = mHost == null ? null : mHost.getActivity();
    if (hostActivity != null) {
        mCalled = false;
        onAttach(hostActivity);
    }
}

Jeśli rzucisz okiem getActivity, zobaczysz to samo połączenie

/**
 * Return the Activity this fragment is currently associated with.
 */
final public Activity getActivity() {
    return mHost == null ? null : mHost.getActivity();
}

Więc jeśli chcesz mieć pewność, że otrzymujesz Aktywność, użyj getActivity() (w onAttachw swoje Fragment), ale nie zapomnij sprawdzić, nullbo jeśli mHostjest nullTwoja aktywność będzienull

royB
źródło
6
@Override
public void onAttach(Context context) {
    super.onAttach(context);

    Activity activity = context instanceof Activity ? (Activity) context : null;
}
pracował
źródło
5

Chociaż wydaje się, że w większości przypadków wystarczy mieć onAttach(Context), istnieją pewne telefony (np .: Xiaomi Redme Note 2), w których nie jest wywoływany, dlatego powoduje wyjątki NullPointerExceptions. Aby być bezpiecznym, sugeruję pozostawienie przestarzałej metody:

// onAttach(Activity) is necessary in some Xiaomi phones
@SuppressWarnings("deprecation")
@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    _onAttach(activity);
}

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

private void _onAttach(Context context) {
    // do your real stuff here
}
Gavriel
źródło
3

Pobierz najnowszą bibliotekę pomocy technicznej za pomocą menedżera SDK i dołącz

compile 'com.android.support:appcompat-v7:23.1.1'

w gradle.app i ustaw wersję kompilacji na api 23

Essah
źródło
1

Poniższa odpowiedź jest związana z ostrzeżeniem o wycofaniu występującym w samouczku Fragmenty w witrynie dla programistów Androida i może nie być związana z powyższymi postami.

Użyłem tego kodu na lekcji samouczka i zadziałało.

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

    Activity activity = getActivity();

Martwiłem się, że ta aktywność może być zerowa, jak podaje dokumentacja.

getActivity

FragmentActivity getActivity () Zwraca FragmentActivity, z którą ten fragment jest obecnie powiązany. Może zwrócić wartość null, jeśli fragment jest skojarzony z kontekstem.

Ale onCreate na main_activity wyraźnie pokazuje, że fragment został załadowany, więc po tej metodzie wywołanie działania get z fragmentu zwróci klasę main_activity.

getSupportFragmentManager (). beginTransaction () .add (R.id.fragment_container, firstFragment) .commit ();

Mam nadzieję, że się z tym zgadzam. Jestem absolutnym nowicjuszem.

Roy Selim
źródło
1

prawdopodobnie używasz android.support.v4.app.Fragment. W tym przypadku zamiast onAttachmetody wystarczy użyć, getActivity()aby uzyskać FragmentActivityz którym fragment jest powiązany. W przeciwnym razie możesz użyć onAttach(Context context)metody.

LeoCoder
źródło
0

Działa to dla mnie, gdy zdefiniowałem interfejs użytkownika „TopSectionListener”, jego obiekt activitycommander:

  //This method gets called whenever we attach fragment to the activity
@Override
public void onAttach(Context context) {
    super.onAttach(context);
    Activity a=getActivity();
    try {
        if(context instanceof Activity)
           this.activitycommander=(TopSectionListener)a;
    }catch (ClassCastException e){
        throw new ClassCastException(a.toString());}

}
Muskan Agarwal
źródło