Android Endless list memory management-ThrowExceptions

Exception or error:

I’m implementing an endless listview by loading more items to the arraylist in the onScrollStateChanged(…) method. If I’m implementing this scheme for fetching more than 1 million entries, I will have a million objects added to the arraylist, which is memory intensive. What schemes can I use for efficient memory management ?

PS: The question is about the number of items that can be put into the adapter.
Edit:

More details:

The source of the data is Internet. I have to fetch the data from the Internet and put it into the listview adapter.

How to solve:

I think you should just keep the current entries and the one just before or after them(maybe 100),put this data to a cache.

When you scroll your listview,fetch more entries and update the cache as before(do not get 1 million at one time).

###

In Android the ListView is virtualized. Practically that means that
there’s no actual limit for number of elements inside it. You can put
millions of rows inside the list, it’ll only allocate memory for the
currently visible ones (or a few more tops).

Source

Also check this article Performance Tips for Android’s ListView

###

This question has nothing to do with the Adapter “capacity”. Instead, it is related to the amount of memory allocated by your app.

It has a reserved heap in order to allocate objects, if you pass this limit you will get an Out of Memory Exception

Here a little test, it could give you an idea about the amount of data that you could allocate. But be aware that in this example the object contains just an String, if it were a gorgeous Bitmap the amount of objects to allocate would be much much much less.

//MemoryActivity

public class MemoryActivity extends Activity {

    private List<TestObject> _testObjects = new ArrayList<TestObject>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test_memory);
        coolGuysDoNotLookBackAtExplosion();
        starCountdown();
    }

    private void starCountdown() {
        new CountDownTimer(300000, 500) {

            public void onTick(long millisUntilFinished) {
                TextView tv_watcher = (TextView) findViewById(R.id.tv_watcher);
                tv_watcher.setText(getMemoryUsage());
            }

            public void onFinish() {
                starCountdown();
            }

        }.start();
    }

    private String getMemoryUsage() {
        String heapSize = String.format("%.3f", (float) (Runtime.getRuntime().totalMemory() / 1024.00 / 1024.00));
        String freeMemory = String.format("%.3f", (float) (Runtime.getRuntime().freeMemory() / 1024.00 / 1024.00));

        String allocatedMemory = String
                .format("%.3f", (float) ((Runtime.getRuntime()
                        .totalMemory() - Runtime.getRuntime()
                        .freeMemory()) / 1024.00 / 1024.00));
        String heapSizeLimit = String.format("%.3f", (float) (Runtime.getRuntime().maxMemory() / 1024.00 / 1024.00));

        String nObjects = "Objects Allocated: " + _testObjects.size();

        return "Current Heap Size: "    + heapSize
                + "\n Free memory: "
                + freeMemory
                + "\n Allocated Memory: "
                + allocatedMemory
                + "\n Heap Size Limit:  "
                + heapSizeLimit
                + "\n" + nObjects;
    }

    private void coolGuysDoNotLookBackAtExplosion(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                _testObjects = new ArrayList<TestObject>();
                while (true) {
                    _testObjects.add(new TestObject());
                }
            }
        }).start();
    }
}

//TestObject

public class TestObject {
    private String sampleText = "Lorem Ipsum is simply dummy text of the printing and typesetting industry";
}

###

If your ListView contains only text items, there is not much you need to do. However, if you are loading more memory intense things, like drawables (for example, you have a picture on the right side of your view), then you should do some recycling, for best result. You might receive an OutOfMemoryException very quickly on a weaker device. I could go OOM even on a Nexus 4. Just try to scroll very quickly, up and down, up and down, and repeat until force close.

Take a look at RecyclerListener, it is very easy to implement.

###

You should use paging and Load More button as a footer of ListView. For example:

url = http://your_full_url.php?page=1

let say you have 100 records in each page then very first time get all these 100 records of page 1, display those on ListView and cache them. Now scroll down your ListView and click on Load more button (Load More button should be set as footer of the ListView).

When you click on Load More you will get next 100 records by calling

url = http://your_full_url.php?page=2 and so on for

url = http://your_full_url.php?page=3,

url = http://your_full_url.php?page=4 etc…

each time you will cache those records so that in case of connection loss you could show records available in cache.

###

I guess sqlite Database and streaming parser(GSON).

###

I couldn’t find an exact number mentioned in the docs. However, the return types of all Adapter#getCount() (look at the subclasses) are ints.

Therefore, we can strongly suspect you can add up to Integer.MAX_VALUE items into an adapter, which is 231-1 (over 2 billions). Adapters use Lists and Maps to store the data internally, which have the same limit.

So you should not be worried about the limitations of the adapter rather than using too much memory. I suggest you to load 10-100 elements into the adapter and simply add more items as soon as the user reaches the bottom of the listview.

###

Tianwei’s approach is the way to go.

If the ListView is lazy loaded and since the ListView itself is recycling views it’s best to keep only the visible list entries in memory. You basically do the same in the adapter the ListView does for the Views.

If you’d keep all data in memory what would be the point of lazy loading the ListView anyway? Just load all data and skip the lazy loading part…
Of course with the lazy loading approach that does load only the visible data (and maybe some more) you’d have to implement lazy loading at the bottom and the top of the list to make this work.

Now since there’s no information on the nature of the data (text, images) or the source (Internet, SQLite Db, text file…) I can’t give you code (samples) how to implement this. If you elaborate on the data I can answer the question more accurately.

###

If you need to keep in memory 1M objects, and assuming the object data is small, than theis is just a few MB of memory, and should be fine to just keep in memory. From the question I understand that you will read more items when users scrolls forward, so in practice you will not have 1M rows – users will need to scroll for a long time to get to 1M.
As long as you use the ListView properly, you can have the adapter data grow to be 1M+ rows in memory without any issue

Leave a Reply

Your email address will not be published. Required fields are marked *