ViewPager and Fragment lifecycle with Activity - android

I'm using an Activity which has a ViewPager holding 2 fragments, the pager handler is some implementation of FragmentPagerAdapter.
As I understand, pager adapter handles the lifecycle of the fragments inside it.
I found out that my Activity onResume() method already gets called but the fragment onStart() method didn't even started.
how in the world can I fix that? it destroy the whole point of lifecycle interactions between activity an fragments...
Since pager adapter handles the lifecycle of the Fragment, does this means I can no longer depend on interaction with the Activity? I mean, if I want the Activity to do something in the onResume() but after the Fragment onStart() is called, I just can't do it...
Edited:
To make things clear:
Google says lifecycle of activity and fragment are going together, once one gets called, the other also gets called, e.g
Activiy -> onCreate() , and then, Fragment -> onCreate()
Activiy -> onResume() , and then, Fragment -> onResume()
BUT! in my case I get:
Activity -> onCreate() -> onStart() -> onResume() -> onPostResume()
And then:
Fragment -> onAttach() -> onCreateView() -> ... ->onResume().
and to be clear, I am using a pager adapter (not "state" pager) and I have an abstract base activiy in my app which all activities should extend.
public abstract class AbsLoginAppCompatActivity extends AppCompatActivity {
.............
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "*******************onCreate");
//do some general stuff like check for updates on server
}
And in my extend activity:
public class A extends AbsLoginAppCompatActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e(TAG, "*******************onCreate");
setContentView(R.layout.activity_a);
//also set pager + adapter + give it getSupportFragmentManager()
}
I am using:
android.support.v4.view.ViewPager
android.support.v4.app.FragmentPagerAdapter
android.support.v4.app.Fragment
android.support.v7.app.AppCompatActivity (for abs activity)

The Fragment[State]PagerAdapter uses the activities FragmentManager - or in case of a nested ViewPager in a parent fragment - that fragment's child FragmentManager to manage the fragments, just like normal fragments would do. Really, the only thing that these adapter implementations do is that they hide the nasty FragmentTransaction stuff for you.
I had never problems that particular lifecycle callbacks weren't called for me in my fragments, so I cannot say anything about that. One thing however that is important to understand and that many people get wrong is that the adapter's getItem() method is called only when a fragment is freshly created; if it is restored from a saved state this method is not called again and people tend to do all fancy things there to initialize their just "created" fragment, while they should really look into instantiateItem(), which either returns the instance you give the adapter via getItem() or returns the reference of the fragment that was automatically re-created for you.
Another thing that is good to know about fragments in pager is the method setUserVisibleHint(boolean). Since fragments are usually recreated and resumed all at once (non-state adapter) or on demand (state adapter), its usually important to know when one instance is actually visible to the user. This can be achieved by overriding the aforementioned method in a custom fragment.

Related

Difference between creating a new fragment and getting an existing one from the manager on rotation

It seems whether I set retainInstance to true or not, when I rotate the device, I get an existing fragment. The difference is that if I set it to true, I get "test = yes!", otherwise I get "test = no!" after rotating the device after clicking the test button to change test. That is, the member variable is kept, if I retain the instance.
But as I have said, even if I do not retain it, I get an existing fragment from the manager, anyway (always get "Reusing existing" on rotation). In that case, if all member variables are lost and the views of the fragment are recreated, what are kept? What is the point of getting an existing instance of the fragment?
In the activity's onCreate,
var frag = supportFragmentManager.findFragmentById(R.id.frame)
if(frag == null)
{
frag = Fragment1.newInstance("", "");
}
else
{
Log.d("sss", "Reusing exsiting");
}
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.frame, frag)
transaction.commit()
In the fragment,
var test = "no!";
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
{
super.onViewCreated(view, savedInstanceState)
Log.d("sss", "test = " + test);
testButton.setOnClickListener {
test = "yes!";
}
}
I have spent some hours trying to recreate your situation with different scenarios. First of all, I should point out that the life cycle of Fragments are in fact so complicated that during Google I/O 2018, one of the lead developers asked the audiences if onCreate() method of the Activity is invoked first or that of Fragment's. And the answer was that, it depends on the SDK version. But they are focusing more and more on Compatibility Libraries and advice developers to use these to have a universal experience across different devices, as well as enjoying the new APIs that will do the job of dealing with Fragments, so much easier for us.
While looking at the documentation of AppCompatActivity class, I realized that this behavior is their way of dealing with fragments.
Protected methods
void onCreate(Bundle savedInstanceState)
Perform initialization of all fragments.
void onDestroy()
Destroy all fragments.
void onPostCreate(Bundle savedInstanceState)
void onPostResume()
Dispatch onResume() to fragments.
void onSaveInstanceState(Bundle outState)
Save all appropriate fragment state.
void onStart()
Dispatch onStart() to all fragments.
void onStop()
Dispatch onStop() to all fragments.
As you can see they "save all appropriate fragment state" in onSaveInstanceState(); meaning that the states will be restored later on, after the Activity gets destroyed and recreated. So in onDestroy() all fragments get destroyed and when the Activity is created again, they get recreated as well. To make sure, you could override these methods inside both Fragment and Activity and check the result. If you do not check FragmentManager for already attached fragments, onCreate() method of the Fragment will be called twice, once directly by you -adding as a new Fragment- and once by the AppCompatActivity itself.
About the retainInstance = true, the documentation says that it keeps the member variables during configuration changes and will cause a slight difference in the life cycle of the Fragment.
setRetainInstance(true)
Control whether a fragment instance is retained across Activity re-creation (such as from a configuration change). This can only be used with fragments not in the back stack. If set, the fragment lifecycle will be slightly different when an activity is recreated:
onDestroy() will not be called (but onDetach() still will be, because the fragment is being detached from its current activity).
onCreate(Bundle) will not be called since the fragment is not being re-created.
onAttach(Activity) and onActivityCreated(Bundle) will still be called.

Can onViewCreated be called before Activity onCreate finishes?

I am trying to redesign some fragments to remove dependencies from the onAttach and onActivityCreated overrides and instead look up the Activity later on in the onViewCreated override.
Are there any cases in the Android application lifecycle where onViewCreated for the fragment is called before Activity onCreate finishes. For example I know that:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
will usually not result in Fragments onAttach being called (assuming the fragment is added to the fragment manager programmatically later on), however in rare cases with configuration updates the fragment manager can recreate the fragments in the super.onCreate which causes the onAttach to be called before onCreate for the activity has finished.
onCreate()
The system calls this when creating the fragment. Within your implementation, you should initialize essential components of the fragment that you want to retain when the fragment is paused or stopped, then resumed.
onCreateView()
Called to create the view hierarchy associated with the fragment.
The system calls this when it's time for the fragment to draw its user interface for the first time. To draw a UI for your fragment, you must return a View from this method that is the root of your fragment's layout. You can return null if the fragment does not provide a UI.
Refer Fragments Life Cycle

Why is onAttach called before onCreate?

In a Fragment's Lifecycle, the onAttach() method is called before the onCreate() method. I can't wrap my head around this. Why would you attach a Fragment first?
TL;DR:
In order to not break the design consistency amongst different UI components in android,the onCreate() method will have similar functionality across all of them.
When linking Containers to Contents like Window to Activity and Activity to Fragment a preliminary check needs to be done to determine the state of container.
And that explains the use and position of onAttach() in the fragment lifecycle.
Too short;Need longer:
The answer is in the archetype code itself,
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnFragmentInteractionListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnFragmentInteractionListener");
}
}
Another example would be in Jake Wharton's ActionBarSherlock library.
Why would you want to use a method like onCreate() which is has the same purpose in an activity ,service.
The onCreate() is meant to handle issues with respect to that particular context creation.It does not make sense if onCreate() is used to check the state of its container.
The second reason that I can come determine is that a fragment is designed to be activity independent.The onAttach() provides an interface to determine the state/type/(other detail that matters to the fragment) of the containing activity with reference to the fragment before you initialize a fragment.
EDIT:
An activity exists independently and therefore has a self sustaining lifecycle.
for a fragment :
The independent lifecycle components(same as any other components):
onCreate()
onStart()
onResume()
onPause()
onStop()
onDestroy()
The interaction based components:
onAttach()
onCreateView()
onActivityCreated()
onDestroyView()
onDetach()
from the documentation:
The flow of a fragment's lifecycle, as it is affected by its host
activity, (...) each successive state of the activity determines which
callback methods a fragment may receive. For example, when the
activity has received its onCreate() callback, a fragment in the
activity receives no more than the onActivityCreated() callback.
Once the activity reaches the resumed state, you can freely add and
remove fragments to the activity. Thus, only while the activity is in
the resumed state can the lifecycle of a fragment change
independently.
However, when the activity leaves the resumed state, the fragment
again is pushed through its lifecycle by the activity.
answering another question which came up in the comments:
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.
The design philosophy states that a Fragment is designed for reuse. A fragment (by design) could(and should) be used across multiple activities.
The onCreate by definition is responsible to create a fragment.
Consider the case of orientation,your fragment could be:
- using different layouts in different orientations.
- applicable only in portrait orientation and not landscape
- To be used only on tables and mobile phones.
All these situations would require a check before the fragment is initialized from the android perspective(onCreate()) and the view inflated(onCreateView()).
Also consider the situation of a headless fragment.The onAttach() provides you the interface required for preliminary checks.
Because onAttach() assigns hosting activity to the Fragment. If it had been called after onCreate() then there would be no context for your fragment (getActivity() would return null) and you would not be able to do anything in onCreate() method without that context anyway.
Another fitting reason is that Fragment's lifecycle is similar to Activity's lifecycle. In Activity.onAttach() activity gets attached to its parent (a window). Similarly in Fragment.onAttach() fragment gets attached to its parent (an activity), before any other initialization is done.
This is related to retained fragments. Following Fragment setRetainInstance(boolean retain) documentation:
If set, the fragment lifecycle will be slightly different when an activity is recreated:
onDestroy() will not be called (but onDetach() still will be, because the fragment is being detached from its current activity).
onCreate(Bundle) will not be called since the fragment is not being re-created.
onAttach(Activity) and onActivityCreated(Bundle) will still be called.
Take a look at the source code (android.support.v4.app.FragmentManager, v21):
void moveToState(Fragment f,
int newState,
int transit,
int transitionStyle,
boolean keepActive) {
...
f.onAttach(mActivity);
if (!f.mCalled) {
throw new SuperNotCalledException("Fragment " + f
+ " did not call through to super.onAttach()");
}
if (f.mParentFragment == null) {
mActivity.onAttachFragment(f);
}
if (!f.mRetaining) {
f.performCreate(f.mSavedFragmentState); // <- Here onCreate() will be called
}
...
}
Example
Case 1: not retained fragment or setRetainInstanceState(false)
Application is started. Fragment is added dynamically using FragmentManager or inflated from XML via setContentView().
onAttach() called after Activity super.onCreate() call - Activity is already initialised.
MainActivity﹕ call super.onCreate() before
MainActivity﹕ super.onCreate() returned
MainFragment﹕ onAttach() getActivity=com.example.MainActivity#1be4f2dd
MainFragment﹕ onCreate() getActivity=com.example.MainActivity#1be4f2dd
Configuration changed. Activity recreates fragments from saved state, fragments are added/attached from inside Activity super.onCreate() call:
MainActivity﹕ call super.onCreate() before
MainFragment﹕ onAttach() getActivity=com.example.MainActivity#2443d905
MainFragment﹕ onCreate() getActivity=com.example.MainActivity#2443d905
MainActivity﹕ super.onCreate() returned
Case 2: setRetainsInstanceState(true)
Application is started. Fragment is added dynamically using FragmentManager or inflated from XML via setContentView(). Same as above:
onAttach() called after Activity super.onCreate() call - Activity is already initialised.
MainActivity﹕ call super.onCreate() before
MainActivity﹕ super.onCreate() returned
MainFragment﹕ onAttach() getActivity=com.example.MainActivity#3d54a168
MainFragment﹕ onCreate() getActivity=com.example.MainActivity#3d54a168
Configuration changed.
Fragment onCreate() not called, but onAttach() still called - you need to know, that hosting Activity has changed. But still fragment is already created, so no onCreate() called.
MainActivity﹕ call super.onCreate() before
MainFragment﹕ onAttach() getActivity=com.example.MainActivity#d7b283e
MainActivity﹕ super.onCreate() returned
Two Points from Android developer Site hints at why onAttach() is called before onCreate() in case of Fragment Life cycle.
A fragment must always be embedded in an activity. Now this means for Fragment to EXIST, there has to be a "living" Activity.
Also, When you add a fragment as a part of your activity layout, it lives in a ViewGroup inside the activity's view hierarchy.
So Fragment must FIRST "attach" itself to activity to defines its own view layout
onCreateis Called to do initial creation of a fragment.
It is obvious that you will create something only when its pre-condition of creation is in place (and the pre-condition is A fragment must always be embedded in an activity, and it must be attached to its Activity)

Cannot get Fragment data because of Fragment Life cycle

Using Tabs means using fragments, and for some reason fragments have new steps in their life cycle, like onAttach(Activity).
My fragment fills up some maps from the resources, and it is done on onAttach() instead of the fragment constructor; because in the constructor getResources() throws an exception due to the lack of Activity yet.
The fragment is created on MainActivity like this:
#Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
switch( tab.getPosition() ) {
case 0:
if( fragmentTab0 == null ) {
fragmentTab0 = new MyFragment();
setTabText(0, ((MyFragment)fragmentTab0).getMyName());
}
fragmentTransaction.add(R.id.fragmentContent, fragmentTab0, "TAB0");
break;
Here lies my problem, in the call to the fragment method getMyName() which uses the maps I mentioned before to get a string. The call to getMyName() is executed before the fragment's onAttach() and the maps are not ready yet.
I am sure I can find a convoluted way to get the name (actually I tried already to pass the activity to the fragment's constructor and built the maps there, and it works, but it goes against the fragment philosophy).
I would have thought that the activity should be visible during Fragment constructor, since the fragments are created from the activity they're going to be eventually attached, so there is no point in delaying the activity attachment.
I also would have thought that the call to new MyFragment() should return after onAttach() is done. But it returns right after the constructor is done.
Therefore I feel not comfortable with the situation and I wonder if I am using fragments the wrong way, if so, the question is, how am I supposed to do it right to be able to call getMyName() there.
Note: From the fragment life cycle diagram it is clear that onAttach() and onDetach() are indistiguishable from onCreate() and onDestroy() respectively, so I question if they are really necessary.
onAttach() is not invoked until the fragment transaction is committed. Until then, a fragment has no reference to the creating Activity unless you pass it such a reference. Passing that reference is probably the cleanest way to implement what you're trying to do.
Generally in Android the title is dictated by the containing activity, not the fragment. For example, a PreferenceActivity's xml headers file lists titles and their associated fragments; those titles do not appear in the preference xml files used by the PreferenceFragment.

fragmenttabhost re-createview when tag switch

In my recent project, I use the v4 support lib for FragmentTabHost.
When the tab and fragment init, the fragment bound to the tag is created and is rendered. Its lifecycle is
onCreate ==> onCreateView ==> onActivityCreated
I overrode the onActivityCreated methods, because I need to get some data from api.
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Lg.i("on TagDiscoverListFragment activity created");
new GetDiscoversTask().execute();
}
But every time i switch to this tab, this aysnTask executes again, because when switch occurs, the fragment calls:
onCreateView ==> onActivityCreated
I dont want the data to be refresh again.
Surely, I can extend the FragmentTabHost and override the method doTabChange() and change fragment's Attach/Detach operation to hide/view.
So, any other solutions?
You should override "onSaveInstanceState(Bundle outState)" in your fragment
and store data which from api into "outState"
When tab is switched, the onCreateView will be invoked, at this point, you can retrieve api data from "savedInstanceState" (the third argument)

Categories

Resources