Why is onAttach (Activity activity) deprecated? - android

After updating the SDK to API level 23, I found that onAttach (Activity activity) is deprecated and the new method is onAttach (Context context). can any one enlighten me on why this change was made?

I think it has basically been to expand the scope of the method, but the official changelog doesn't say anything about it.
As you can see in the changelog they have removed the void onAttach(Activity) but they added a new one with the same name, and it says that is deprecated in the Android Official Documentation.
As richq commented, the support version of Fragment also deprecates onAttach(Activity) and has an onAttach(Context) that can be used instead on all Android versions right back to prehistoric ones.
To adapt to this new changes you can follow this steps:
Change the argument type of onAttach callback from Activity to Context. For unknown reason, this modification results in the fact that the method onAttach(Context) is not called anymore during the fragment lifecycle.
Move the code that was in onAttach method to onCreate one since it gets still executed.
With this modification, the app turns to run as before. No additional import statements are required.

Until this change happened, a fragment could only be attached to an activity. After this change Google can work towards attaching fragments to Services too. Something like how Facebook chat heads work, they could have fragment floating outside an activity too.

Related

onAttach() not called in Fragment

My Fragment doesn't call onAttach(context) method when it launched from AppCompatActivity.
Fragment creating in XML:
<fragment
android:id="#+id/toolbar"
class="package.MainToolbarFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:layout="#layout/fragment_main_toolbar" />
But if I extends it from support.v4.Fragment, onAttach(context) call !
What could be the problem?
Of course, I can extend all fragments from v4.Fragment, but I don't want it. Is it bad practice?
Also project min sdk 14.
It's not called because this method has been added in API 23. If you run your application on a device with API 23 (marshmallow) then onAttach(Context) will be called. On all previous Android Versions onAttach(Activity) will be called.
http://developer.android.com/reference/android/app/Fragment.html#onAttach(android.app.Activity)
Support libraries fragment is platform independent. Hence it works on all API versions.
While Google wants us to stop using deprecated API's
#Override
public void onAttach(Context context) {
super.onAttach(context);
...
Is so new that it isn't widely called. You need to also implement
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
...
For me they are identical but I like to KISS and introducing another support library tends to double my apk to about 1000kb. I only updated my SDK yesterday.
The reason the types are not interchangeable here, as they are in many instances, is that the method taking an Activity will still be called when an Activity is provided as they are both publicly visible and Activity is more specialised than (as a sub class of) Context so will take precedence.
In addition to the aforementioned comments, I believe it is important to note that if you are attempting to use the onAttach() to update data contained inside the fragment from the parent Activity, it is possible to run into issues when the collection variable inside the Activity is null or empty when the fragment is inflated. At some point inside your Activity's life cycle, your data model may change and need to be updated inside the fragment. You might attempt to get a reference to a fragment already inflated, but find as you step through your code that onAttach() never fires, even when using the override containing a Context or Activity object.
If you are attempting to create a listener for the fragment and initialize the listener from the onAttach() callback method, onAttach() will not fire unless you provide the tag parameter as shown below when adding the fragment to the Activity:
// in the Activity
getFragmentManager().beginTransaction()
.add(
R.id.fragmentContainer,
CustomFragment.newInstance(customDataSource),
CustomFragment.TAG // Must be passed in for the code below to work
).commit();
// Getting a reference to the fragment later on (say to update your data model inside the fragment (in onActivityResult())
CustomFragment fragmentDelegate = (CustomFragment) getFragmentManager().findFragmentByTag(CustomFragment.TAG);
fragmentListener.updateDataSource(customDataSource);

Should we be using setTargetFragment()? I thought Fragments should not be communicating with each other

The android developer tutorials recommend me using the host activities of fragments to pass around data and whatnot, so why is there a set/get target fragment method?
My application thus far contains a host activity, and a fragment, which has a button that launches a DialogFragment, in which there is a button that launches ANOTHER DialogFragment. Using setTargetFragment/getTargetFragment has made this whole ordeal somewhat confusing though, so I am thinking of reimplementing to let my main activity handle DialogFragment.show methods through my main fragment's custom interface.
Is this the right way of thinking? Or is there no harm in using setTargetFragment? Can anyone provide good and bad examples of using it?
Also, you may end up with exception of no target fragment found in fragment manager. This happens if after rotation (or other config change) your target fragment will not be readded to the fragment manager by the time when caller fragment will be adding.
Imagine you have some sort of Confirmation fragment which you add from MainFragment as so:
ConfirmationFragment frag = new ConfirmationFragment();
frag.setTargetFragment(this, 0);
getFragmentManager().beginFragmentTransaction().add(R.id.container, frag).commit();
Now on some confirmation button click you invoke a method from MainFragment by calling:
((MainFragment)this.getTargetFragment()).onUserSelectedGoodButton();
This is pretty and simple, but if you will rotate the screen and for some reason ConfirmationFragment will be added to FragmentManager before MainFragment, exception will be thrown, stating that target fragment is not found in the fragment manager
I don't think there is implicit harm in using setTargetFragment, however, I would only use it in very specific circumstances. For example, if the target fragment is only going to ever be used by the fragment (taking into account object reuse and designing your classes to be reusable when possible) and even then, sparingly.
By using them too much, you will end up with what you're seeing - confusing code that is hard to follow and maintain. On the outset, by marshaling everything through your activity you maintain a "flat" hierarchy that is simple to follow and maintain.
I think the decision to use setTargetFragment or not is a coding-style/philosophical one that, with wisdom and experience, it "feels" right or wrong. Maybe on your case, by evidence that you are questioning your older code, you are gaining that wisdom :)

To use getSherlockActivity() or getActivity() or other?

I have an Android app with TabManager. Due to change of Android API, I need to upgrade my app for Activity to contain the Fragment. To provide backward compatibility, I use ActionBarSherlock.
My App is working fine. However, looking at the Google Play Developer Console, there is always few crash reports on "java.lang.NullPointerException" on the line with getSherlockActivity(), I think only less than 0.1% out of total users are affected.
For example,
// Example 1
File file = new File(getSherlockActivity().getCacheDir(), "filename");
// Example 2
getSherlockActivity().setSupportProgressBarIndeterminateVisibility(false);
My question:
1. Should I change all the getSherlockActivity() to getActivity()? Or under certain rule, it is mandatory to use one of them?
2. What is the difference between them?
Thanks a lot.
The only difference is that with getSherlockActivity () you get the result of getActivity() but casted as a SherlockActivity. This allows to get access to the ABS specific apis.
If you just need something that is general enough to be in the Activity class, just use getActivity(). It will highlight this, otherwise highlight the fact that you use something that is ABS specific using getSherlockActivity ().
The NPE can come from :
using getActivity() (or siblings...) before onAttach has been executed
using getActivity() (or siblings...) after onDetach has been executed
So the solution is to check if your fragment is attached before using its activity :
if( isAttached() ) {
getActivity()....
}
Same topic
The NullPointerException is normally
Caution: If you need a Context object within your Fragment, you can call getActivity(). However, be careful to call getActivity() only when the fragment is attached to an activity. When the fragment is not yet attached, or was detached during the end of its lifecycle, getActivity() will return null.
This exception probably means that the code is being exectuted when the fragment is not attached to an activity.
The reference returned by getSherlockActivity() is set when the Fragment is attached, and then cleared (set to null) when the Fragment is detached. If your code tries to reference getSherlockActivity() before or after this, you would get a null-pointer.
getSherlockActivity is just a shortcut to:
(SherlockActivity) getActivity()
So in your case there is no problem to use getActivity() instead in Example 1 but in Example 2 it will not work since it is a method of SherlockActivity.

Which Android Fragment lifecycle methods require super

Currently (Android API 17), the only mention of super in the Android Reference on Fragment is casually via some code examples (unlike the Android Reference on Activity, which carefully notes where super is required).
SO suggests searching the web as needed, or waiting for a crash, to identify where a call to super is required. I'm asking SO users to share their knowledge on which of the Fragment lifecycle methods require a call to super.
Fragment lifecycle methods - require call to super
onAttach()
onCreate() - presumably yes, as Activity version requires it
onCreateView() - seems ok with or without
onActivityCreated()
onViewStateRestored()
onStart() - presumably yes, as Activity version requires it
onResume() - presumably yes, as Activity version requires it
onPause() - presumably yes, as Activity version requires it
onStop() - presumably yes, as Activity version requires it
onDestroyView()
onDestroy() - presumably yes, as Activity version requires it
onDetach()
onSaveInstanceState() - presumably yes, as Activity version requires it
All of the corresponding Activity lifecycle methods except onSaveInstanceState require calls to super. In addition:
onAttach() - yes
onActivityCreated() - yes
onViewStateRestored() - is not a Fragment method
onDestroyView() - yes
onDetach() - yes
onSaveInstanceState() - from Fragment#onSaveInstanceState it
looks like a no
All of the methods that require calls to super share the first line of their method in android.app.Fragment:
mCalled = true;
That way the FragmentManager can check if mCalled is true and throw a SuperNotCalledException when it is not called. See FragmentManager#moveToState to see this implementation.
When generating a fragment with Eclipse, the onCreateView method template code does not have a call to super.onCreateView. Also, the generally quite good book published by WROX: Android 4 Application Development misses it out in its sample lifetime code (it does not miss out any other calls to super).
Of course both these two sources could be incorrect, but using the Eclipse template and not adding super.onCreateView has not caused me an issue.
I am typing with capital letter 'O' instead of small letter 'o'
means : OnCreate instead of onCreate methods.
Its a silly mistake but need to remember :)
Thanks

setRetainInstance not working for ListFragment when using compatibiltiy library

I'm attempting to save a ListFragment subclass across an orientation change so I added setRetainInstance(true) to the end of my onCreate function. I added an onSaveInstanceState method to add all of it's data to a bundle, then added code into onActivityCreated to load that data back. Unfortunately, it wouldn't work.
When I added some debugging messages with the help of Log.d I discovered that not only was onSaveInstanceState not being called, but onCreate was (which the documentation seems to say shouldn't happen when retainInstance is true). Neither onCreate nor onActivityCreated have bundles with my data (unsuprisingly).
I'm guessing this may be a problem with compatibility library, though I don't have an android 3.0+ device to test this.
Any help is appreciated and I can post some code snippets if necessary, though I'm not doing anything complicated.
Update: onDestroy is not being called when I change orientation (which is how it should be), so it seems that some of setRetainInstance is working
I finally figured out what my problem was. It all came down to a single line I'd forgot to add. In my FragmentActivity subclass I'd overrode onSaveInstanceState, but I never called super.onSaveInstanceState. Apparently, unlike other methods whose parents I'd forgotten to call, onSaveInstanceState won't throw a runtime error when I forget to call the parent classes version of it, instead setRetainInstance just stops working. I hope this saves someone the headache I went through trying to solve this.
It seems, when you set setRetainInstance = true while both onSaveInstanceState() and onActivityCreated() are called, then Bundle will not be returned.
However, as the as the ListFragment is being retained, you can simply store its state into a field, and handle it inside onActivityCreated().
Bear in mind, the Activity will still be destroyed and recreated.

Categories

Resources