Home » Android » How to avoid recreating view on onCreate on Android?

How to avoid recreating view on onCreate on Android?

Posted by: admin June 15, 2020 Leave a comment

Questions:

I have a FragmentActivity that shows a contacts list.

Here is my onCreate method:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_human_list);

    if (findViewById(R.id.human_detail_container) != null) {
        // The detail container view will be present only in the
        // large-screen layouts (res/values-large and
        // res/values-sw600dp). If this view is present, then the
        // activity should be in two-pane mode.
        mTwoPane = true;

        // In two-pane mode, list items should be given the
        // 'activated' state when touched.
        ((HumanListFragment) getSupportFragmentManager()
                .findFragmentById(R.id.human_list))
                .setActivateOnItemClick(true);
    }

    if (savedInstanceState == null || !savedInstanceState.getBoolean("displayed_contacts"))
        displayContacts();
}

My onSaveInstanceState:

@Override
protected void onSaveInstanceState(Bundle savedInstanceState) {
    super.onSaveInstanceState(savedInstanceState);
    savedInstanceState.putBoolean("displayed_contacts", true);
}

And I’m not sure if this is relevant, but here’s my displayContacts just in case:

private void displayContacts() {

    // Init variables
    String[] SelectColumns = new String[] { Contacts._ID, Contacts.DISPLAY_NAME_PRIMARY, Contacts.PHOTO_URI };
    String rawContactID, displayName, phoneNumber;
    InputStream thumbnailPhoto;
    Cursor c, infoC;

    // Outer cursor (fetches all contact IDs)
    c = getContentResolver().query(
            Contacts.CONTENT_URI,
            SelectColumns,
            Contacts.HAS_PHONE_NUMBER + " = 1 ",
            null,
            Contacts.DISPLAY_NAME_PRIMARY);

    Log.v(getPackageName(), "Found " + (c != null ? c.getCount() : "0") + " contacts");
    try {
        if (c.moveToFirst()) {
            do {
                // Columns
                rawContactID    = c.getString(c.getColumnIndex(SelectColumns[0]));
                displayName     = c.getString(c.getColumnIndex(SelectColumns[1]));
                String[] selectPhone = {CommonDataKinds.Phone.NUMBER};

                thumbnailPhoto = openThumbnail(Long.valueOf(rawContactID));

                infoC = getContentResolver().query(
                        CommonDataKinds.Phone.CONTENT_URI,
                        selectPhone,
                        CommonDataKinds.Phone.CONTACT_ID + " = ?",
                        new String[] {rawContactID},
                        null
                    );
                infoC.moveToFirst();
                phoneNumber = infoC.getString(0);

                // Adds items to ListView
                HumanContent.addItem(new HumanContent.HumanItem(rawContactID, displayName, phoneNumber != "n/a" ? phoneNumber : "", thumbnailPhoto));
                Log.v(getPackageName(), "Cursor position: " + c.getPosition() + ", contact ID: " + rawContactID);
                infoC.close();
            } while (c.moveToNext());
            c.close();
        }
        displayed_contacts = true;
    } catch (Exception e) {
        Log.e(getPackageName(), e.getMessage());
    }
}

Now here’s the thing:

When I use the back key to exit the application, and then open it again via the icon; the list recreates itself even though it is saved in memory: so I get a double list of contacts on the same view.

savedInstanceState is null in that case, so the if condition is reached, but in reality the view already has my previous contact list. What gives? How can I avoid recreating the list? I already tried using instance variables instead, but to no avail.

I’d also like to avoid recreating the list 100% of the times – if I can reuse the existing view, awesome.

How to&Answers:

First, the reason your savedInstanceState is null – The system only saves state of activities that are destroyed due to system constraints. If you back out of an Activity it is destroyed for good, and no state will be saved.

Relevant docs:
http://developer.android.com/training/basics/activity-lifecycle/recreating.html

When your activity is destroyed because the user presses Back or the
activity finishes itself, the system’s concept of that Activity
instance is gone forever because the behavior indicates the activity
is no longer needed. However, if the system destroys the activity due
to system constraints (rather than normal app behavior), then although
the actual Activity instance is gone, the system remembers that it
existed such that if the user navigates back to it, the system creates
a new instance of the activity using a set of saved data that
describes the state of the activity when it was destroyed.

So, it seems like your particular problem is that while your Activity is gone, your static HumanContact class is still in memory, and your new Activity is loading it with another copy of your contacts.

There are a couple ways you could solve this. First, you could implement a method on HumanContent to clear out all its items, and call it whenever you launch a new instance of your Activity. This would have the benefit of ensuring your data is up to date, but would mean you have to reload your contacts.

Secondly, if you wanted to truly avoid reloading the data, I’d recommend creating some sort of cache for the contacts that is independent of the Activity. You should consider your Activity as fairly transient, it can and will be destroyed and recreated frequently, whereas a cache can persist.

HumanContent seems to be filling this responsibility already, to an extent. You’re using it to store your data, and it is persisting beyond the lifecycle of your Activity. You could additionally add a method to it that checks to see if it has contacts loaded, and if not loads them itself. This way it has complete control over loading and caching your data, and Activities can be responsible solely for the display of this data. Be careful with this type of solution that you’re not storing too much data in memory, that you’re reloading your cache anytime you expect your data to have changed, and be aware that your process might be restarted by the system in some cases, so your cache must be prepared to reload your data in case it is destroyed.

As for preserving your Views, if the user is backing out of your Activity then the finish() is being called, and your Activity is going to be destroyed. Remember, in this case the system no longer has any concept of the Activity, so there will be no way to preserve these views for reuse.

Answer:

Its simple. follow these steps in your Activity

  • Declare your listview object and listview_adapter object in side your Activity class.
  • Initialise your listview object and listview_adapter object in OnCreate() method and add the contents in your displayContacts().

Then definitely you wont get content replication.

Answer:

It is always adviced to use caching to retain the data . as mentiond by Groucho u can go with storage options
Check below link for details.

http://developer.android.com/guide/topics/data/data-storage.html

Answer:

I have faced similar issue once. I am not sure how legitimate this is but the code I have used is below and it fixed a lot:

private static ViewGroup view1;
@Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        mCurrentActivity = getActivity();
        if (view1 == null) {
            settleFragment(inflater);
        } else {
            if (view1.getParent() != null && view1.getParent() instanceof ViewGroup) {
                ((ViewGroup) view1.getParent()).removeView(view1);
            }
        }
        return view1;
    }

You can adapte settleFragment(inflater); method with yours.
The magic is about static variable named view1. Once the fragment is initialised it is filled up with the data. The other instances of Fragments will simply rip apart view1 from its ex-parent and will paste it on themself.

It is pretty fast with respect to file storage cache since the object is kept in ram. If OS garbage collects the object, static variable will be null again and while recreating the view no duplicates will occur.

Answer:

There’s no way you can stop onCreate function from executing. However, you could apply a work-around to avoid duplicate list, you could remove all items from the listview and then let it repopulate the listview.

Answer:

@Override
public void onPause ()
{
    super.onPause();
    //...........
}

@Override
public void onStop ()
{
    super.onPause();
    //............
}

Check if you can use these methods in your fragments ?

Answer:

The most simple way to achieve this is to do a Nullcheck
i.e.

if(yourcomponent==null) {
    initializecomponent();
}

That may prevent your views from recreating.

Answer:

when you are done with loading of the data in displayContacts(), you can save a flag(displayed_contacts) in shared preferences. In onCreate() check the preference value, and call your displayContacts() accordingly.

Answer:

I faced same problem while using Fragments.

We have existing methods as stated above. If you are finding yourself in difficult position for using that then you can try as mentioned below :-

  • Initiate a Boolean value with false.
  • Set that Boolean value to true after you get your data.
  • Now on re-initiate of your OnCreate() check for that Boolean value to be true or false. if false then you need to pull that data again else you need not to get that data. Put condition for same.

Answer:

You are going to check controller therefor.

Instead of that try to check value you stored.
First take one variable as Bundle.
Save it in onCreate method.

Bundle bdlSaveInstant;   // Declare at top before onCreate() method.
/* Initialise in onCreate() method. After 

super.onCreate(savedInstanceState);
setContentView(R.layout.activity_human_list);
bdlSaveInstant = savedInstanceState;
*/

@override
onBackPressed()
{
  bdlSaveInstant.putBoolean("displayed_contacts", true);
}

@override
onDestroy()
{
  bdlSaveInstant.putBoolean("displayed_contacts", true);
}

Try this will work.

Answer:

In your on create method just clear your list.Because every time it will go to on create method and make list clear(which) is your previous data loading.

OR

check your list if(list.size==null) then call your API otherwise in else block (list.setAdapter).

hope it will work.