Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid recreating view on onCreate on Android?

Tags:

android

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.

like image 401
casraf Avatar asked Nov 16 '13 14:11

casraf


3 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.

like image 156
groucho Avatar answered Sep 27 '22 20:09

groucho


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.

like image 21
Dharani Kumar Avatar answered Sep 27 '22 20:09

Dharani Kumar


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

like image 28
Adi Avatar answered Sep 27 '22 20:09

Adi