Android Fragment onAttach() deprecated
Asked Answered
R

10

344

I have updated my app to use the latest support library (version 23.0.0), I've found out that they deprecated the onAttach() function of the Fragment class.

Instead of:

onAttach (Activity activity)

It's now:

onAttach (Context context)

As my app uses the activity passed before deprecation, a possible solution i think is:

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

Would that be the correct way to do it?

UPDATE:

If i run a device with API lower than 23, the new onAttach() is not even being called. I hope that this is not what they intended to do!

UPDATE 2:

Issue has been resolved with the latest updates to the SDK.

I have tested on my API 22 device and onAttach(Context) is being called.

Click here to follow the bug report I've opened a couple of weeks ago and the answers from the guys at Google.

Ressler answered 18/8, 2015 at 21:40 Comment(5)
If you are using specific Activity methods from your passed instance then, have you tried casting the context to your Activity's? Remember Activity is a subclass of Context. Maybe a casting would work.Solanaceous
for some reason, the onAttach() is not even being called! any ideas? did you try updating to the latest support lib.?Ressler
Why has the API moved to Context? Don't you need an Activity in order to attach and display a fragment anyways? How else will you use the Context parameter?Vitiate
I have posted it as a bug, see link code.google.com/p/android/issues/detail?id=183358Ressler
For the new onAttach(Context context) to be called, you need to either use a device that has at least API 23 OR use android.support.v4.app.Fragment. See hereGovern
T
363

Activity is a context so if you can simply check the context is an Activity and cast it if necessary.

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

    Activity a;

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

}

Update: Some are claiming that the new Context override is never called. I have done some tests and cannot find a scenario where this is true and according to the source code, it should never be true. In all cases I tested, both pre and post SDK23, both the Activity and the Context versions of onAttach were called. If you can find a scenario where this is not the case, I would suggest you create a sample project illustrating the issue and report it to the Android team.

Update 2: I only ever use the Android Support Library fragments as bugs get fixed faster there. It seems the above issue where the overrides do not get called correctly only comes to light if you use the framework fragments.

Tybi answered 19/8, 2015 at 6:55 Comment(13)
Not really a solution since onAttach with context is not called the same way as onAttach with activity. If you have some initialization code in onAttach with an activity as parameter this change won't work.Stench
No, it doesn't work the same, at least in my case. I don't use activity nor context in onAttach, I do initialization. I've tried replacing onAttach with activity to the one with context and it doesn't get called in the same way. So they aren't that easily interchangeable.Stench
@Tybi there is no casting. the hostActivity is being called using the mHost callbacks. Context is not being used. have a look at the source code. final Activity hostActivity = mHost == null ? null : mHost.getActivity();Zobe
Picky but OK. Bad choice of words. FragmentManager calls ` Fragment.onAttach(mHost.getContext());` It still calls directly through to the old method and the Context it uses is still the exact same Activity. I still maintain that there is no difference under final use when fragments are hosted in activities.Tybi
Same issues that Buddy has... onAttach(Context) doesn't even seem to be called.Trigger
Maybe there is a specific use case or bug that some people are hitting. For me though. it works totally as it should. I can override both methods and get the exact same activity passed into each one as the parameter. It actually makes no sense that the Context version be called but the Activity version doesn't because FragmentManager now ONLY calls the Context version which then internally passes the operation to the Activity version. (btw, Buddy didn't say it wasn't called. He\she said it was not called the same way although I do not know what he\she meant by that)Tybi
I have stepped through my code and I can verify that it is possible for onAttach(context) to not be called.Sempiternal
It doesn't make sense so have the new onAttach(context) only be called form API >=23. And for older API's, we must use a deprecated function onAttach(activity)Ressler
In my case, the onAttach() won't be called if the we restart the activity which is cleared by the system before. But it's not always like this. I put a initialization of a WeakReference in the onAttach(Context context), refActivity = new WeakReference<>((AppCompatActivity) context). And it throws NullPointerException in the onCreateView() occasionally.Syringa
this doesn't compile.Fishtail
It does if you do it right. Open your own question if you don't know howTybi
On some Xiaomi phones removing the onAttach(Activity) causes NullPointerExceptions (because the fragment is not being attached, and later your code wants to use it) See my answer below how to be on the safe side.Homemade
Work or not? I need the onAttach method to my app run at least android and to 21 APIMarela
P
47

This is another great change from Google ... The suggested modification: replace onAttach(Activity activity) with onAttach(Context context) crashed my apps on older APIs since onAttach(Context context) will not be called on native fragments.

I am using the native fragments (android.app.Fragment) so I had to do the following to make it work again on older APIs (< 23).

Here is what I did:

@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
    }
}
Pemba answered 12/1, 2016 at 15:3 Comment(4)
Mr.Yoann Hercouet, Can u explain how to do it above API 23, kindly help me, thanks in advance, E/AndroidRuntime: FATAL EXCEPTION: main, java.lang.ClassCastException: com.ual.SMS_Activity@afd5683, at android.support.v4.app.FragmentManagerImpl.moveToState, at android.support.v4.app.FragmentManagerImpl.execSingleAction. Here i posted necessary lines which will u to know my bug.Monia
@MohanRajS It is the same on API 24 (the one I used), according to your piece of code, your problem does not seem to have to do anything with this.Pemba
@YoannHercouet ,Thank u for your reply, let me check and update.Monia
You're not happy with Google about this because of difficulty targeting older API's. Well, consider the blessing that you CAN target older API's, whereas Apple, for instance, would just cripple you tremendously and force you to support only the newest ones.Spavined
H
40

If you use the the framework fragments and the SDK version of the device is lower than 23, OnAttach(Context context) wouldn't be called.

I use support fragments instead, so deprecation is fixed and onAttach(Context context) always gets called.

Hebbe answered 17/9, 2015 at 12:0 Comment(0)
Z
13

Currently from the onAttach Fragment code, it is not clear if the Context is the current activity: Source Code

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

If you will take a look at getActivity you will see the same call

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

So If you want to be sure that you are getting the Activity then use getActivity() (in onAttach in your Fragment) but don't forget to check for null because if mHost is null your activity will be null

Zobe answered 23/8, 2015 at 10:49 Comment(0)
H
7
@Override
public void onAttach(Context context) {
    super.onAttach(context);

    Activity activity = context instanceof Activity ? (Activity) context : null;
}
Hills answered 2/10, 2015 at 23:4 Comment(0)
H
7

Although it seems that in most cases it's enough to have onAttach(Context), there are some phones (i.e: Xiaomi Redme Note 2) where it's not being called, thus it causes NullPointerExceptions. So to be on the safe side I suggest to leave the deprecated method as well:

// 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
}
Homemade answered 17/11, 2017 at 11:9 Comment(0)
K
3

Download newest Support library with the sdk manager and include

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

in gradle.app and set compile version to api 23

Kirbee answered 25/11, 2015 at 19:5 Comment(0)
N
1

The answer below is related to this deprecation warning occurring in the Fragments tutorial on the Android developer website and may not be related to the posts above.

I used this code on the tutorial lesson and it did worked.

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

    Activity activity = getActivity();

I was worried that activity maybe null as what the documentation states.

getActivity

FragmentActivity getActivity () Return the FragmentActivity this fragment is currently associated with. May return null if the fragment is associated with a Context instead.

But the onCreate on the main_activity clearly shows that the fragment was loaded and so after this method, calling get activity from the fragment will return the main_activity class.

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

I hope I am correct with this. I am an absolute newbie.

Normand answered 24/10, 2016 at 8:2 Comment(0)
X
1

you are probably using android.support.v4.app.Fragment. For this instead of onAttach method, just use getActivity() to get the FragmentActivity with which the fragment is associated with. Else you could use onAttach(Context context) method.

Xuanxunit answered 10/11, 2016 at 7:57 Comment(0)
P
0

This worked for me when i have userdefined Interface 'TopSectionListener', its object 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());}

}
Paratroops answered 12/11, 2019 at 7:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.