Android Endless list memory management
Asked Answered
P

9

20

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.

Pendleton answered 10/3, 2014 at 9:15 Comment(7)
Regarding just the ListView itself, you should make use of the recycling mechanism: https://mcmap.net/q/151323/-how-listview-39-s-recycling-mechanism-works.Behn
@Behn - I understand about the recycling done in the views. But the question is about the source of the listview, which is an arraylist.Pendleton
Yes I know, just wanted to add this in case you didn't knowBehn
Why would you want to load all 1 million items into an ArrayList if you lazy load the list view anyway (and since ListView itself recycles Views)? I'd suggest to keep only the items in memory that are shown on the screen and dynamically load new items. Of course I don't know the source of your data and that might be not so easy. If you elaborate on the actual data I could give you some recommendations.Godship
I'd consider to store the data into a SQLite DB, and fill the ListView with a Cursor (using a CursorAdapter). More specifically, the benefit is that a SQLiteCursor extends from AbstractWindowedCursor, which takes advantage of exposing data through a CursorWindow (basically a buffer). As such, you should not have to worry too much about storing a gazillion items in memory, and appropriate management for all those different Android devices out there. That being said, do you really want to display a list with over a million items to the user?Senega
If the data is in an SQLite DB the way to go is to use a CursorLoader not just a "naked" CursorAdapterGodship
@EmanuelMoecklin: Correct, a Loader would be the way to go to query the DB and retrieve a Cursor. I just didn't mention it because the loading mechanism isn't really what this question is about; it's more about "where to put the data with respect to the memory footprint. Nevertheless, a good addition.Senega
R
11

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

Regality answered 10/3, 2014 at 9:24 Comment(4)
If I have a adapter with 100 elements, when I load the 101th element, I should replace the first entry with the 101th is what you mean ?Pendleton
Use a buffer mind. I mean you put the 101th to 200th in your cache(current is 100),if you scroll 130,nothing need to be down,when the current is 160,maybe you can load the 201th to 300th at background.Just update the cache at some idle time.Regality
I guess this is the approach that makes sense for theoretically infinite dataset, such as social network streams.Carolinacaroline
Could you provide please real code example of this suggestion?Intimacy
B
3

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

Baleen answered 10/3, 2014 at 9:23 Comment(2)
I understand the optimization done in the ListView. The question is about how many objects can be put into the adapter for the ListView.Pendleton
You have a memory limit which is device specific. As long as you don't exhaust your memory limit, you can store as many items as you wish.Baleen
F
2

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";
}
Frankiefrankincense answered 11/9, 2014 at 1:2 Comment(0)
D
1

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.

Diskson answered 10/3, 2014 at 9:30 Comment(0)
K
1

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.

Kinzer answered 11/9, 2014 at 9:41 Comment(0)
R
0

I guess sqlite Database and streaming parser(GSON).

Rodrickrodrigez answered 10/3, 2014 at 9:20 Comment(4)
Can you explain it further ? The answer is vague and I'm heading nowherePendleton
According to your question you don't want to use array list as it will take much more memory so what i am suggesting instead of using arraylist use sqllite database and use content provider when ever you want to use that information.Rodrickrodrigez
Streaming Parser parse the stream of data one by one so that there is no need to download million entries at once to the device memory. Do it one by one and save it to database. That way you can save the whole a lot memory. ListView in itself is highly optimised it use that much memory which is required. So download the data using streaming parser one by one save it to database one by one and connect that database to the listview. No need to store any thing in memory cache.Rodrickrodrigez
Let me try. I guess the issue of size will be in size of sqllite db.Pendleton
W
0

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.

Wongawonga answered 5/9, 2014 at 11:36 Comment(0)
G
0

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.

Godship answered 5/9, 2014 at 15:15 Comment(0)
D
-1

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

Deshawndesi answered 10/9, 2014 at 4:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.