I have setup a little example application where the idea is to navigate from one Activity to another and to study Memory consumption because I don't really understand when/where memory is released during this process.
Idea is to create an Activity which consume quite a lot of memory to see if memory is released correctly when we leave it before recreating it.
- A HomeActivity only composed of a Button that call a BlogListActivity when button is clicked.
- BlogListActivity is a ListActivity that contain BlogPost objects. This BlogPost contains a Bitmap in order to use some memory.
List of BlogPost is created dynamically in the onCreate method of BlogListActivity and then passed to an Adapter to display each PostBlog object in row of my ListView.
On an emulator with Android 2.3.3 and 128Mo of memory, I manage to move from HomeActivity to BlogListActivity and then come back to HomeActivity two times. On the third try, I get an OutOfMemoryError from BitmapFactory.
This mean I have a Memory Leak: objects that are not used anymore but still have a reference on it so they are not released. But I don't where I do it wrong.
Can someone help me finding it.
Thanks in advance for your help.
Bertrand
Link to complete source code and Eclipse project
Here is an extract of the code we are interested in
HomeActivity source code
public class HomeActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
}
public void onSecondActivityClick(View v) {
startActivity(new Intent(this, BlogListActivity.class));
}
}
BlogListActivity source code
public class BlogListActivity extends ListActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bloglist);
List<BlogPost> items = new ArrayList<BlogPost>();
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.mn);
for (int i = 0; i < 5; i++) {
BlogPost post = new BlogPost();
post.author = String.format("Author%d", i);
post.title = String.format("Title%d", i);
post.date = new Date();
post.imageURL = "https://si3.twimg.com/profile_images/1143791319/MN_BLEU.png";
post.image = bmp;
post.image = BitmapFactory.decodeResource(getResources(), R.drawable.mn);
items.add(post);
}
setListAdapter(new LazyArrayAdapter(this, R.layout.listitem_blog, items));
}
}
LazyArrayAdapter source code
public class LazyArrayAdapter extends ArrayAdapter<BlogPost> {
public LazyArrayAdapter(Context context, int textViewResourceId, List<BlogPost> objects) {
super(context, textViewResourceId, objects);
}
@Override
public View getView(int index, View view, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
if (view == null) {
view = inflater.inflate(R.layout.listitem_blog, parent, false);
}
TextView title = (TextView)view.findViewById(R.id.listitemblog_title);
TextView date = (TextView)view.findViewById(R.id.listitemblog_date);
ImageView icon = (ImageView)view.findViewById(R.id.listitemblog_icon);
BlogPost post = this.getItem(index);
title.setText(post.title);
date.setText(new SimpleDateFormat().format(post.date));
icon.setImageBitmap(post.image);
return view;
}
}
BlogPost source code
public class BlogPost {
public String title;
public String author;
public Date date;
public String imageURL;
public Bitmap image;
}
activity_bloglist Layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ListView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@android:id/list">
</ListView>
<开发者_StackOverflow/LinearLayout>
ListItemBlog Layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:weightSum="100"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout android:layout_width="0px" android:layout_weight="70"
android:id="@+id/linearLayout2"
android:orientation="vertical"
android:layout_height="fill_parent">
<TextView android:id="@+id/listitemblog_title"
android:layout_width="wrap_content"
android:text="TextView"
android:textStyle="bold"
android:layout_height="wrap_content">
</TextView>
<TextView
android:id="@+id/listitemblog_date"
android:layout_width="wrap_content"
android:text="TextView"
android:layout_height="wrap_content"
android:textStyle="bold">
</TextView>
</LinearLayout>
<ImageView
android:id="@+id/listitemblog_icon"
android:layout_width="0px"
android:scaleType="centerInside"
android:layout_weight="30"
android:src="@drawable/icon"
android:layout_height="fill_parent"/>
</LinearLayout>
HomeActivity layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Button
android:layout_height="wrap_content"
android:onClick="onSecondActivityClick"
android:layout_width="wrap_content"
android:id="@+id/button1"
android:text="Button">
</Button>
</LinearLayout>
I have studied memory usage with DDMS + MAT. Here are screenshots of what I see in MAT for the com.webcontentlistview I create: Memory usage after navigating to BlogListActivity one time
Memory usage after navigatin to BlogListActivity several times
As we can see, even after navigating between both Activity, we still have only one BlogListActivity object in memory (with it's associated content). But numbers of java and android objects are increasing (lines 2 and 3).
Could it be that the garbage collector simply does not have time to clean your data before you launch the activity again? How quickly did you do the test? Does it always crash, even if you take some time between starting BlogListActivity? Maybe try to run System.gc() each time the app returns to HomeActivity and see if the crashes resume.
精彩评论