rennoDeniro
rennoDeniro

Reputation: 928

Android AsyncTask out of memory via http request

So basically, let me set the scenario:

List<Account> accounts = ...//Initialised with 2 accounts.

for( Account ac : accounts)
{
   new AsyncTask<...>(){ getTwitterTimeLine(amount);}.execute();
}

So you can see here two AsyncTasks get called straight after the other.

Now here's the Interesting part,

If I set the amount variable as 10. The method calls a HTTP Request, requesting 10 latest feed items. This works perfectly fine, the normal behaviour than is that the data returned is put into one of multiple possible list views, (all viewable via a viewpager, so in this scenario 2 list views filled by the 2 async tasks respectively)

BUT

If I put the amount to 100, so basically more JSON data, I get the following;

   04-04 03:11:20.175: E/AndroidRuntime(4574): FATAL EXCEPTION: AsyncTask #1
04-04 03:11:20.175: E/AndroidRuntime(4574):     at android.os.AsyncTask$3.done(AsyncTask.java:299)
04-04 03:11:20.175: E/AndroidRuntime(4574):     at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273)
04-04 03:11:20.175: E/AndroidRuntime(4574):     at java.util.concurrent.FutureTask.setException(FutureTask.java:124)
04-04 03:11:20.175: E/AndroidRuntime(4574):     at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307)
04-04 03:11:20.175: E/AndroidRuntime(4574):     at java.util.concurrent.FutureTask.run(FutureTask.java:137)
04-04 03:11:20.175: E/AndroidRuntime(4574):     at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
04-04 03:11:20.175: E/AndroidRuntime(4574):     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
04-04 03:11:20.175: E/AndroidRuntime(4574):     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
04-04 03:11:20.175: E/AndroidRuntime(4574):     at java.lang.Thread.run(Thread.java:856)
04-04 03:11:20.175: E/AndroidRuntime(4574): Caused by: java.lang.OutOfMemoryError
04-04 03:11:20.175: E/AndroidRuntime(4574):     at 

java.lang.AbstractStringBuilder.enlargeBuffer(AbstractStringBuilder.java:94)
04-04 03:11:20.175: E/AndroidRuntime(4574):     at java.lang.AbstractStringBuilder.append0(AbstractStringBuilder.java:145)
04-04 03:11:20.175: E/AndroidRuntime(4574):     at java.lang.StringBuilder.append(StringBuilder.java:216)
04-04 03:11:20.175: E/AndroidRuntime(4574):     at twitter4j.internal.org.json.JSONArray.join(JSONArray.java:363)
04-04 03:11:20.175: E/AndroidRuntime(4574):     at twitter4j.internal.org.json.JSONArray.toString(JSONArray.java:605)
04-04 03:11:20.175: E/AndroidRuntime(4574):     at twitter4j.internal.http.HttpResponse.asJSONArray(HttpResponse.java:192)
04-04 03:11:20.175: E/AndroidRuntime(4574):     at twitter4j.internal.json.StatusJSONImpl.createStatusList(StatusJSONImpl.java:381)
04-04 03:11:20.175: E/AndroidRuntime(4574):     at twitter4j.internal.json.z_T4JInternalJSONImplFactory.createStatusList(z_T4JInternalJSONImplFactory.java:74)
04-04 03:11:20.175: E/AndroidRuntime(4574):     at twitter4j.TwitterImpl.getHomeTimeline(TwitterImpl.java:127)

and it points directly to my AsyncTask's doInBackground method where the getTwitterTimeLine() is being called.

How can I solve this? I want to be able to bring in lots of data, and pump it to multiple listviews etc. I read up a lot around and I thought that maybe I need to queue the tasks incase one of them is taking a lot of memory, so I researched and worked with Proper use of AsyncTask, but this had no effect and I still got the out of memory problem.

Anyway how can solve/get round this? I read up LoaderManager but I am wanting to hold this off as last call as it would result in a big design change.

Thank you for any help.

Note: The getTwitterTimeLine method calls a twitter4j method that calls the api.

Upvotes: 0

Views: 1427

Answers (3)

rennoDeniro
rennoDeniro

Reputation: 928

It turns out the in the end, it was because of Bitmaps not being memory managed. Despite implementing the inSampleSize framework described by the android dev pages on efficiently loading your bitmaps, it would seem that maintaining and managing them appropriately is just as necessary.

Thank you for all those who replied, with very informative topics.

Upvotes: 0

Deepak Bala
Deepak Bala

Reputation: 11185

You cannot rely on AsyncTasks for parallel execution.

When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting with HONEYCOMB, tasks are executed on a single thread to avoid common application errors caused by parallel execution.

As for twitter4j...

at java.lang.StringBuilder.append(StringBuilder.java:216)
04-04 03:11:20.175: E/AndroidRuntime(4574):     at twitter4j.internal.org.json.JSONArray.join(JSONArray.java:363)

...it uses a StringBuilder internally. Using string concatenation across several threads on a device with limited memory is bound to result in a OOM. If you want to save on the memory taken by these json strings use a JsonReader instead. As for parallel execution you can use Threads backed by a ThreadPool to execute them in parallel. AsyncTasks are executed serially in some versions of android.

Upvotes: 1

You do not want to fire off AsyncTask's like this in a loop. There is a fixed thread pool size and while two accounts or so won't be enough to max this out, it's a good programming practice not to. Instead, determine how you might load all of this data in a single Async task. doInBackground() accepts a parameter list. You could, for instance, create your AsyncTask with an amount set in the constructor and pass accounts in to load data for.

@Override
protected Map<Account, Data> doInBackground(Accounts...params){
    Map<Account, Data> resultMap = new HashMap<Account, Data>();
    for (Account account : params) {
        // do whatever!
    }
    return resultMap;
}

Upvotes: 1

Related Questions