Viewpager fragment reference to new copy after activity restore - android

When my activity containing viewpager is killed by system in background and then restores its state, fragments are correctly created and viewpager adapter can also point to them correctly.
But when I get a fragment reference and try to access its fields, they are all null (checked by using breakpoint).
I checked this by placing breakpoints in fragment onCreateView() and in my activity button's clickListener.
((WelcomeFragment)homeActivityFragmentPageAdapter.getItem(POSITION_HOME)).setdata(myData);
Now this method will through null pointer exception since setdata(data) is internally accessing arraylist field of fragment.
This creates a problem for me since, my activity has to continuously feed network data to the fragment by calling its public method (as suggested by documentation).
How to insure that after state restored; correct instance is pointed in my activity.

Try to use instantiateItem adapter method instead getItem.
((WelcomeFragment)homeActivityFragmentPageAdapter.instantiateItem(mViewPager, POSITION_HOME)).setdata(myData);
Method getItem is overrided method, and common use is creation of child fragments.
EDIT:
In case of the question's scenario, you also need to store the state of FragmentStatePagerAdapter manually:
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState
.putParcelable("pages",homeActivityFragmentPageAdapter.saveState());
super.onSaveInstanceState(savedInstanceState);
}
Then you can retrieve the state in oncreate:
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
homeActivityFragmentPageAdapter.restoreState(savedInstanceState.getParcelable("pages"),this.getClassLoader());
welcomeFragment = (WelcomeFragment) homeActivityFragmentPageAdapter.instantiateItem(mViewPager, POSITION_HOME);
}
else { //simply create a new instance here}
homeActivityFragmentPageAdapter.addFragmentToAdapter(welcomeFragment);
homeActivityFragmentPageAdapter.notifyDataSetChanged();
}

Related

When is fragment finally attached to activity?

I have a main fragment with a viewpager inside it. This viewpager has 2 pages (list fragments). When I start the activty, the main fragment is shown and I also show the first paged fragment. This paged fragment displays data from a db using AsyncTask.
In the main fragment I have:
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
onPageSelected(0);
}
#Override
public void onPageSelected(int position) {
Fragment fragment = (Fragment) pagerAdapter.instantiateItem(viewPager, position);
if (fragment instanceof IPagedFragment) {
((IPagedFragment) fragment).onShown(getActivity());
}
}
And the interface is:
public interface IPagedFragment {
void onShown(FragmentActivity activity);
}
The first issue I have is that I have to pass the activity as a parameter because when onShown gets called, the activity is still null.
Furthermore, the paged fragments use progressbar logic similar to the LoginActivity sample. I also get the following exception:
IllegalStateException: Fragment PagedFragment1{4201f758} not attached to Activity
at android.support.v4.app.Fragment.getResources(Fragment.java:620)
So what is the correct stage to start retrieving data from db once the paged fragment is fully available to the UI?
Issues like yours is the reason some developers are starting to question if fragments are really that good or useful.
Also "the correct" is debatable as you can do it in a variety of places and different developers will give you different answers, But let me try to supply you some useful info.
The attach/detach callbacks:
public void onAttach(Activity activity);
public void onDetach();
between those two methods any call to getActivity() will return the non-null activity the fragments is connected to. You can override them and use a private boolean isAttached to keep track of that call.
Also useful is the:
public void onActivityCreated (Bundle savedInstanceState)
this method is called AFTER the Activity.onCreate method. That is very important if you rely on some initialisation that happened there.
Also it's important to remember that on the moment the fragment transaction happens, the Fragment.onCreate happens after the Activity.onCreate and during rotation it happens before it.
As a general rule of thumb I use the Fragment.onStart() / Fragment.onStop() for getting/listening to data. On those calls, all the UI have been created, the fragment is attached to the activity and those callbacks don't get called if there's a dialog/popup (pause/resume does)
From the documentation:
public void onActivityCreated (Bundle savedInstanceState)
[...] tells the fragment when it is fully associated with the new activity instance.
source: http://developer.android.com/reference/android/app/Fragment.html#onActivityCreated(android.os.Bundle)
To get the reference of your activity, create a local object of fragmentActivity and get your activity reference as shown below.
private FragmentActivity fragmentActivity;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
fragmentActivity=activity;
}

Having a shared Object between Activity and Fragment: Runtime changes?

I have an Activity called BaseActivity, hosting multiple fragments.
The BaseActivity has a public field Object owhich is accessed by the fragments by calling Object o = ((BaseActivity) getActivity()).o; This is initialized in the fragments onCreate.
This works but I have problems with runtime configuration changes. It seems that the Fragments onCreate is called before the BaseActivitys onCreate, so I cant retain the Object from the Bundle i saved in onSaveInstanceState.
Is there a way I can make sure the acitivty can retain its object from the saved Bundle before the Fragment tries to access it?
Try this ..
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Object o = ((BaseActivity) getActivity()).o;
}
This makes sure that the activity's onCreate has completed executing.

SavedInstanceState is always null in fragment

I have a fragment attached to the activity using XML (and setContentView() in activity). A have a problem because I have very dynamic views in my fragment, so when orientation changes
I must restore all states of views.
I have problem because I'm using something like that:
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("restore", true);
outState.putInt("nAndroids", 2);
}
But after orientation change when methods with param Bundle savedInstanceState are called (like onCreateView etc) my savedInstanceState is always null.
I'm not a noob in the Android but now I'm very angry because of this problem...
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (savedInstanceState == null) {
//smth
} else {
// smthelse THIS IS NEVER REACHED BECAUSE BUNDLE IS ALWAYS NULL
}
getListView().setDivider(getResources().getDrawable(R.drawable.list_divider));
}
All the problem was in that I don't declare android:id for the fragment in XML. Android needs ID or TAG to recognize stored fragment and reproduce all elements in it. So guys, remember - every instance of fragment needs unique id or tag!
Also, when setRetainInstance(true) is declared then bundle should always return null.
I had a similar problem where I was always getting savedInstanceState as null inspite of supplying the bundle to the Fragment.
The only solution that worked for me was to do
myFragment.setArguments(bundle)
with my bundle from the Activity and do a
Bundle bundle = this.getArguments();
in onCreateView of the fragment.
Hope this helps someone else.
For Fragment :-
use this for save state of fragment on orientation.
onCreate(Bundle save)
{
super.onCreate(save);
setRetainInstance(true);
}
See this tutorial :-
http://techbandhu.wordpress.com/2013/07/02/android-headless-fragment/
For Activity:-
When you start your application, in onCreate, your bundle object is null, so you have to put a check like below and when you rotate your screen then onSaveInstance is called and your bundle object is initialized
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
if (savedInstanceState != null) {
boolean t = outState.getBoolean("restore");
int s = outState.getInt("nAndroids");
}
}
First you should put your data, then call super.onSaveInstanceState(outState);
public void onSaveInstanceState(Bundle outState) {
outState.putBoolean("restore", true);
outState.putInt("nAndroids", 2);
super.onSaveInstanceState(outState);
}
And be sure that activity has not nohistory property in AndroidManifest.xml or set it to false.
<activity
android:noHistory="false">
If you are trying to use outState to save the state and destroy the fragment by navigating to another fragment, it will not work, you have in this case to save your state permanently in either sharedPreferences or if it's big and you want to be more organized you can use any persistence lib like Room, Realm, ...etc.
When should use outState and savedInstanceState only to make Fargment/Activity survive config change(rotation for example) or processes being killed by the OS when the app is in background for example.
Ok I know this is an old post but I couldn't find the right answer for me here nor many other places, so I am posting how I fixed my case.
So My Fragment is inside an Activity. And I originally tried to save Bundle only in Fragment and retrieve it at onCreateView. However that was the problem.
I fixed this by initiating myFragment object in activity and put that object to activity Bundle at onSaveInstanceState(). Then retrieved it at onRestoreInstanceState(). I used getSupportFragmentManager().putFragment/getFragment. Then the savedInstanceState in fragment was no longer null.

Saving and Restoring Custom Object in fragment

I have two fragments FragmentA and FragmentB.
From Fragment A i update Views in Fragment B by passing custom object as a parameter to a method in Fragment B.
For example below is the Fragment B:
public class FragmentB extends Fragment {
private ArrayList<String> customObj = new ArrayList<String>();
public void updateViews(ArrayList<String> obj) {
customObj = obj;
}
#Override
public void onSaveInstanceState(Bundle outState) {
System.out.println("Custom Object : "+customObj);//custom object is always empty
}
Now, whenever i rotate the screen, the custom object is al;ways empty.
Note
Here I am just updating the views of Fragement B. The object is passed as a parameter from Fragment A to the method updateViews of Fragment B.
Also, i am not looking forward to define static for custom object.
You can do two things
1. Store that custom object somewhere which is not created every time orientation is changed i.e. Application class, Service etc.
2. Store that object inside the Bundle which you get as a parameter inside OnSaveInstanceState() and use that Bundle to set the Object in OnRestoreInstanceState() or OnCreate()...
For Example,
#Override
protected void onCreate(Bundle savedInstanceState) {
Logger.d("Dash onCreate");
super.onCreate(savedInstanceState);
...
// create fragments to use
if (savedInstanceState != null) {
//get your Object
}
if (yourObject != null)
//restore the View using yourObject
...
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
Now in your Custom Class you will have to either implement Serializable or Parcelable for storing that inside a Bundle. Serialization is simple but if you implement Parcellable you have more control on it.
Some other similar threads on SO related to this topic are
Similar Example
Another good example
You can get a similar Example in Android Documentations for Fragments.

Fragment saveInstanceState is coming as null after orientation change

I have an activity with action bar tab. Each tab contain a fragment. Now when I rotate my device, bundle in my corresponding fragment is coming as null. This is taken care when I using device post android 3.2, but it is happening when device is Andoird3.0. I am having a headache after working on this issue. I crossed check various link on SO, but no help. Although I have given enough details, still will provide some code snippet as at various cases user ask for code snippet.
In my fragment class I am storing this value
#Override
public void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putBoolean("textboxVisible", true);
}
this is storing one boolean variable which it retrived as below.
/**
* Function called after activity is created. Use this
* method to restore the previous state of the fragment
*/
#Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
if (savedInstanceState != null)
{
//restore the state of the text box
boolean textboxVisible = savedInstanceState.getBoolean("textboxVisible");
if (textboxVisible)
{
//do some stuff
}
}
}
but after rotation savedInstanceState is coming as null.
I don't what is going wrong. I have read in some document that below 3.2 the onCreateView() of
fragment is not called with bundle value. But to deal with this. Any help will be appreciated.
if you use setRetainInstance(true) the savedInstance bundle is always gonna be null after orientation changed. SO you cannot really save something with it, but what you can do if you need to save something, is to put it in a data member of the fragment, because setRetainInstance(true) preserves the fragment and doesn't destroy it, so after the device was rotated you gonna have the same values.
Try to get the savedInstanceState in onCreate of the Fragment.
Like
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
if (savedInstanceState != null) {
// IT MUST NOT BE NULL HERE
}
}
Please try... i hope it will work

Categories

Resources