ImageView memory leak when start other activity - android

I have a simple activity named Test1.
This is the layout code.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/test"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ImageView
android:id="#+id/imageview1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:src="#drawable/load"
android:scaleType="fitXY" />
</RelativeLayout>
In my onDestory method, I release the mImageView resource and in Android profile, the memory of mImageView has really be recycled.
#Override
protected void onDestroy() {
super.onDestroy();
releaseImageViewResource(mImageView);
layout.removeView(mImageView);
mImageView.setVisibility(View.GONE);
mImageView.setImageDrawable(null);
mImageView = null;
}
But when I start other simple activity, the memory of mImageView cannot be recycled. Why and how to solve the problem?

To handle images, I suggest for you to use libraries like Glide or Picasso, they will handle everything for you. (memory leaks, caching, etc)

Not sure what you mean by "start other simple activity", but if you are going to another activity within the application, then your current activity (with the imageview) should be paused instead of destroyed. Either call finish() when you go to the other activity or just put that onDestroy() code in the onPause() method

Related

How can I run code slightly later than the onCreate() and onStart() events?

I have a list view that is populated through a string-array in the xml, not run time and I'm trying to set the background color of specific items in the list using:
listView.getChildAt(x).setBackgroundColor(Color.BLACK);
I need this to happen before it's visible, but it gives an error when I use it in onCreate() or onStart(), but works if I run it on a button press. I've tried searching for an answer but can't seem to find any event that happens late enough for it to work.
getView() would be the best place to do this, but you don't have access with the way you are doing this with android:entries=.... Instead, you can post a runnable to the message queue to change the color after layout occurs like the following:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.layout).post(new Runnable() {
#Override
public void run() {
findViewById(R.id.view).setBackgroundResource(android.R.color.holo_red_dark);
}
});
}
Here I have used a simple View for demonstration but the same technique should work for your ListView.
Here is the XML I used if you want to work with it:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<View
android:id="#+id/view"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#android:color/holo_blue_light" />
</LinearLayout>
Here's a link to post. The runnable kicks off before onStart(). So, if that's a requirement, then this way may not work for you.

How to implement indeterminate loading screen

I am building an app that uses a REST interface to fetch data. Usually, I would call setProgressBarIndeterminateVisibility(true); to let the user know the app is waiting for data. However, this seems to be deprecated:
Progress bars are no longer provided in AppCompat.
When looking at the GMail app, I see there is an indeterminate progressbar (loading icon) full screen, where the data is going to come.
How would I implement that correctly? Some of activities are extended from ListViewActivity and use the setEmptyView to show a simple TextView when no data is available. Some are more elaborate layouts.
I could think of using setVisibility(View.GONE), but wondering if this is the "right" way to do it.
Yes. That is the right way. Just layout everything where they should be on the XML and call setVisibility on your progressBar when necessary.
There're usually two ways of kwnoing "when" to call.
on your REST callbacks (e.g. onSuccess(), onStartLoading())
using a DataSetObserver on your adapter (which is exactly what the ListView does). Like the following code:
.
#Override
protected void onStart(){
super.onStart()
observer.onChanged();
adapter.registerDataSetObserver(observer);
}
#Override
protected void onStop(){
super.onStop()
adapter.unregisterDataSetObserver(observer);
}
private DataSetObserver observer = new DataSetObserver(){
#Override
public void onChanged() {
progress.setVisibility(adapter.getCount()==0?View.VISIBLE:View.GONE);
}
}
Wrap your content layout into FrameLayout. Add to FrameLayout something like this:
<LinearLayout
android:id="#+id/loading_progress"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="invisible">
<ProgressBar
android:layout_width="50dp"
android:layout_height="50dp"
android:indeterminate="true"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="Loading data..."/>
</LinearLayout>
And then switch content / progress layers in code using .setVisibility(View.VISIBLE) / .setVisibility(View.INVISIBLE)

Android first Activity not really destroyed

When I was debugging my app with AndroidDeviceMonitor, I found that my app's heap was odd. After starting the app (SplashActivity -> MainActivity), the "1-byte array" has been allocated 42MB. I was sure the SplashActivity has been destroyed and I'm using LeakCanary to discover if there is any memory leakage. But I did not find anything.
Then I try to create a new SplashActiviy, just setContentView in onCreate() method without any other code.
The layout xml is like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center">
<ImageView
android:id="#+id/splash"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="#drawable/splash2"/>
<ImageView
android:id="#+id/logo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:scaleType="fitCenter"
android:src="#drawable/splashlogo"/>
</RelativeLayout>
I debug again and the "1-byte array" is still 42MB. Then I try to remove the src tag from my layout. The "1-byte array" was reduced to 35MB. So, I guess the problem is that image resources have not been recycled. Can anyone tell me more about the detail of the first launched Activity. Why doesn't it release those resources?
What you can do to make sure that imge image is freed up for garbage collection is set the image to null in onStop to make sure that nothing has reference to it.
#Override
public void onStop() {
super.onStop();
mImageView.setImageDrawable(null);
}
You can then use AndroidDeviceMonitor to force a garbage collection to make sure it is freed from the heap.

Android CheckBox remains state after destroy

I do have a very simple application with just a single CheckBox. I thought that when android does destroy my activity the state of this checkbox will be list. What actually happens is, that the checkbox keeps checked.
The onDestroy() is actually called (on rotation or when I switch to another app -> my emulator has this dev configuration).
So why does this work without using onRestoreInstanceState() and onSaveInstanceState()?
Android Target is 4.2 Jelly Bean.
Here my Activity:
public class CoffeeMixerActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_coffee_mixer);
}
protected void onDestroy() {
super.onDestroy();
Log.d("MyMessage", "Destroy my app");
}
}
Here my Layout file:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".CoffeeMixerActivity" >
<CheckBox
android:id="#+id/checkBoxSprinkles"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sprinkles" />
</RelativeLayout>
Ok.. found the solution by myself:
Android CheckBox -- Restoring State After Screen Rotation
Description:
Every View does have the android:saveEnabled flag on true.. which means that when the View does have an ID the state is automatically saved when the view is freezed...
Here there attribute documentation:
http://developer.android.com/reference/android/R.attr.html#saveEnabled

Out of memory error in Android - Why does the GC not work?

I am working on an application which consists of different screens, which I am designing as different activities with different layouts. For each screen, there are two, large (1536x2048) png files, used as overlay backgrounds (they are alpha blended). For now, the layout xml files of the activities are something like that:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/backgroundPic0"
tools:context=".TakvimActivity" >
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/backgroundPic1"
/>
</RelativeLayout>
For the time being, I am able to travel from the main activity to three different activities. I am doing this simply with
Intent intent = new Intent(this, TakvimActivity.class);
startActivity(intent);
I am aware that the background images consume approximately 24 MBs of memory each time a new Activity is started and the setContentView method is called. When I travel to a new screen for the first time, return back to the main screen and travel to a second,new activity, the application crashes due to Out of Memory exception, saying "Out of memory on a blabla-byte allocation." (Note that it does not crash exactly at the second Activity transition, sometimes at the third one, or so.) It seems to me that the application does not release the resources (most importantly, the image files) when I travel back from an Activity. I checked that whether the current activity is properly destroyed by overriding the onDestroy method and I saw that it is properly called. Shouldn't the GC clear all UI related memory of an Activity as it is being destroyed, since the reference to its view hierarchy gets erased? Is there something I am missing, for example, is there an explicit way to clear an Activity's memory which I did not include in my code?
If the imageview itself is GC'ed then the problem goes away for me. Its not enough to call unbindDrawables on the imageview itself.
See also my answer here
unbindDrawables at PagerAdapter Android Lollipop don't work
and my similar but Android Studio focused question here
Leaked unreferenced byte[] originally from bitmap but recycled() causing memory-leak (until activity stopped)
Try adding the following method to your application and call it from your activities as such:
private void unbindDrawables(View view) {
try
{
if (view.getBackground() != null) {
view.getBackground().setCallback(null);
}
if (view instanceof ViewGroup) {
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
unbindDrawables(((ViewGroup) view).getChildAt(i));
}
((ViewGroup) view).removeAllViews();
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
#Override
protected void onDestroy()
{
super.onDestroy();
//R.id.LayoutId is the id of the root layout of your activity
unbindDrawables(findViewById(R.id.LayoutId));
System.gc();
}

Categories

Resources