Reputation: 13
I'm quite new to Android development and was about to finish my first application when I noticed that I have a huge memory leak.
The leak occurs when I start a new activity from within a thread:
private static class CatalogRowOnClickListener implements OnClickListener
{
List<CatalogRow> catRows;
WeakReference<CatalogViewActivity> mActivity;
CatalogRowOnClickListener(WeakReference<CatalogViewActivity> mActivity)
{
this.mActivity = mActivity;
catRows = new ArrayList<CatalogRow>();
}
public void addCatalogRow(CatalogRow row)
{
catRows.add(row);
}
public void onClick(View v)
{
v.setBackgroundColor(Color.argb(150, 255, 255, 255));
final ProgressBar linProgressBar = (ProgressBar) v.findViewById(R.id.CatProgressBar);
linProgressBar.setVisibility(View.VISIBLE);
final View fv = v;
final fItem fhitem = findFItem(fv);
try
{
new Thread()
{
public void run()
{
initializeApp();
}
public void initializeApp()
{
// Initialize application data here
FItemListStore.getItemsFromWebservice(fhitem);
boolean isArticleOverview = false;
for(FItem item: FItemListStore.fItemViewStorage)
{
if( item instanceof fiArticle )
{
isArticleOverview = true;
break;
}
}
Intent intent = new Intent();
intent.setClassName("nl.project.Android", "nl.project.Android.Activities.HomeViewActivity");
Bundle bundle = new Bundle();
if(isArticleOverview)
{
bundle.putInt("OverviewNr", 1);
}
else
{
bundle.putInt("OverviewNr", 0);
}
intent.putExtras(bundle);
//if( mActivity.get() != null )
mActivity.get().startActivity(intent);
mActivity.clear();
}
}.start();
}
catch (Exception e) {}
//Debug.stopMethodTracing();
//android.os.Debug.stopMethodTracing();
}
A HPROF dump of the app after executing this code shows that the thread will always be retained in the heap and grow exponentially. Researching this points towards referencing a context inside a thread causing leaks. So I changed this code a few times until it became this version with only a weak reference in a static class (that's some things that were mentioned in other posts)
The reason why I want to start the Activity in a thread is because I need to read data from a slow and big webservice, which takes some time. I displayed a loading bar in the activity, highlighting the selected item until the XMLparser
is done, and started a new activity afterwards.
Might it be that there is no correct way to start an activity within a thread without causing a memory leak? And, if so, is there another way for me to have the intent started after the xml parser finishes his job?
On advice of Vladimir and Heiko, I changed my thread to a AnyncTask. The grow of my retained heap has stopped but the AsyncTask threads I started still remain in the heap. Here's my new code:
private static class QueryFHTask extends AsyncTask<FredHopperItem, Integer, Boolean>
{
WeakReference<CatalogViewActivity> mActivity;
QueryFHTask(WeakReference<CatalogViewActivity> mActivity)
{
this.mActivity = mActivity;
}
protected void onPreExecute()
{
}
protected Boolean doInBackground(FItem... items) {
int count = items.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
FItemListStore.getItemsFromWebservice(items[i]);
boolean isArticleOverview = false;
for(FItem item: FItemListStore.fhItemViewStorage)
{
if( item instanceof FArticle )
{
isArticleOverview = true;
break;
}
}
Intent intent = new Intent();
intent.setClassName("nl.project.Android", "nl.project.Android.Activities.HomeViewActivity");
Bundle bundle = new Bundle();
if(isArticleOverview)
{
bundle.putInt("OverviewNr", 1);
}
else
{
bundle.putInt("OverviewNr", 0);
}
intent.putExtras(bundle);
mActivity.get().startActivity(intent);
}
return true;
}
protected void onProgressUpdate(Integer... progress) {
//setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
//showDialog("Downloaded " + result + " bytes");
}
}
private static class CatalogRowOnClickListener implements OnClickListener
{
List<CatalogRow> catRows;
WeakReference<CatalogViewActivity> mActivity;
CatalogRowOnClickListener(WeakReference<CatalogViewActivity> mActivity)
{
this.mActivity = mActivity;
catRows = new ArrayList<CatalogRow>();
}
public void addCatalogRow(CatalogRow row)
{
catRows.add(row);
}
public void onClick(View v)
{
v.setBackgroundColor(Color.argb(150, 255, 255, 255));
final ProgressBar linProgressBar = (ProgressBar) v.findViewById(R.id.CatProgressBar);
linProgressBar.setVisibility(View.VISIBLE);
final View fv = v;
final fItem fhitem = findFItem(fv);
new QueryFHTask(mActivity).execute(fhitem);
}
To clarify my problem, here's a copy of one of my HPROF thread overview heap dumps (it's very hard to read because I was not allowed to post html with links and images like in MAT because of my user level...). The AsyncTask threads retain a heap of 376 or 344. As I browser the application longer, there will be more and more retained heaps from AsyncActivity
threads...
Name Instance Shallow Heap Retained Heap Context Class Loader
main java.lang.Thread @ 0x40027550 80 1.464 dalvik.system.PathClassLoader @ 0x4051ce30
JDWP java.lang.Thread @ 0x40515838 80 384
AsyncTask #6 java.lang.Thread @ 0x406ea788 80 376 dalvik.system.PathClassLoader @ 0x4051ce30
AsyncTask #7 java.lang.Thread @ 0x40690178 80 376 dalvik.system.PathClassLoader @ 0x4051ce30
AsyncTask #4 java.lang.Thread @ 0x4068a630 80 376 dalvik.system.PathClassLoader @ 0x4051ce30
AsyncTask #3 java.lang.Thread @ 0x406851f0 80 376 dalvik.system.PathClassLoader @ 0x4051ce30
AsyncTask #5 java.lang.Thread @ 0x405d9f28 80 376 dalvik.system.PathClassLoader @ 0x4051ce30
AsyncTask #2 java.lang.Thread @ 0x40539db0 80 376 dalvik.system.PathClassLoader @ 0x4051ce30
AsyncTask #1 java.lang.Thread @ 0x40517180 80 376 dalvik.system.PathClassLoader @ 0x4051ce30
AsyncTask #8 java.lang.Thread @ 0x4068cdb0 80 344 dalvik.system.PathClassLoader @ 0x4051ce30
AsyncTask #9 java.lang.Thread @ 0x4068b558 80 344 dalvik.system.PathClassLoader @ 0x4051ce30
AsyncTask #10 java.lang.Thread @ 0x4068a178 80 344 dalvik.system.PathClassLoader @ 0x4051ce30
AsyncTask #11 java.lang.Thread @ 0x406639f0 80 344 dalvik.system.PathClassLoader @ 0x4051ce30
AsyncTask #12 java.lang.Thread @ 0x40661b00 80 344 dalvik.system.PathClassLoader @ 0x4051ce30
Binder Thread #2 java.lang.Thread @ 0x40519b20 80 344
Binder Thread #1 java.lang.Thread @ 0x40516868 80 344
Thread-2 java.util.logging.LogManager$2$1 @ 0x40195298 80 168 dalvik.system.PathClassLoader @ 0x400277f8
Signal Catcher java.lang.Thread @ 0x40515778 80 160
Compiler java.lang.Thread @ 0x405158e8 80 152
HeapWorker java.lang.Thread @ 0x40515618 80 152
GC java.lang.Thread @ 0x405156d0 80 136
Total: 21 entries 1.680 7.656
Upvotes: 1
Views: 2415
Reputation: 43108
Oh my, there is a special thing to start an activity after the some work in background is done. It is AsyncTask with ProgressDialog bounded. See an example.
Upvotes: 1
Reputation: 30994
You should look at AsyncTask , where you do the loading in doInBackground()
, set up the status bar in onPreExecute
, publish progress updates with publishProgess
and then remove the progress bar in onPostExecute
.
Upvotes: 0