Android app best layout for image buttons? - android

I'm developing a VERY SIMPLE android application that is designed with 5 activites, each with its own RelativeLayout.
1) The Parent (or Home)
2-5) Children of Home which each have their own back buttons, which call finish().
The Problem:
I'm using image assets everywhere and I have stress tested the application by constantly switching from Parent to Child over and over. I RUN OUT OF MEMORY due to the age-old "VM budget exceeded" error.!
What I have tried:
I have tried trimming down the image assets set most of my objects to static references so that they be garbage collected when the Child activity finishes.
I have installed the MAT tool to examine the HPROFS dumps, which was very confusing.
I have read about SoftReferences or WeakReferences and File caching techniques, but have yet to implement them.
EDITED...here's my onDestroy for each child activity.
#Override
protected void onDestroy() {
super.onDestroy();
// unbind all drawables starting from the first viewgroup
unbindDrawables(findViewById(R.id.info_layout));
c2 = null;
runnable = null;
mFont = null;
root = null;
myThread = null;
myImage = null;
v = null;
txtCurrentTime = null;
common = null;
System.gc();
Log.e("Info", "Info destroyed.");
}
ALSO...All of these are declared as private at the start of the class declaration. Do they need to be declared as static or something else?
The question:
Is there a different type of layout I could use that wouldnt involve calling finish() on the activities, like TabHost or ViewFlipper something similar...that would prevent me from having to start a new activity and finish it
OR Perhaps...a way that when I call finish() on my Child activities so that everything gets garbage collected?
XML and/or code examples are welcome.
I am open to either, or any other way that could be suggested.
Thanks.
Rick

If you're running OOM after switching from parent to child repeatedly then you're probably leaking the child activity. I'd run an hprof and make sure that you aren't accumulating child activites, and if you are try to find out what references aren't be freed and make sure they go to null in onDestroy.
This usually happens when the framework has a pointer to your child context that it doesn't get told to release via an unregister, or through a circular reference of some sort in your app, possibly through non-static child classes.

Related

MemoryLeak through onConfigurationChanged()

I have an AsyncTask which I need to "restart" if the user does configurations such as Switch Color.
When he do so I start the AsyncTask like this:
myWorkerClass.clearMemory();
myWorkerClass = new WorkerClass(getApplicationContext(), gv, searchbar, width, scaleButtonText);
myWorkerClass.execute();
In the AsyncTask I add a onTextChangeListener to my EditText!(Which causes the MemoryLeak later).
To prevent MemoryLeaks I have a Method in my AsyncTask which removes the onTextChangedListener:
public void clearMemory() {
searchbar.removeTextChangedListener(myTextWatcher);
}
Everything works fine except when I rotate my device. When I rotate my Device I do only this in onConfigurationChanged:
myWorkerClass.clearMemory();
myWorkerClass = new WorkerClass(getApplicationContext(), gv, searchbar, width, scaleButtonText);
myWorkerClass.execute();
As you can see I do exactly the same thing as if the user changes a Color. But at rotating device I'm leaking Memory, at switching Color I'm not!
This is after switching the Color:
This is after rotating the Screen a few times (remember I do exactly the same as at switching color:
These are my Leak Suspects from the Heap Dump:
This is my dominator tree:
Why do I know the onTextChangeListener is the Problem?
Because if I comment adding a onTextChangedListener to my EditText out, everything works fine. No Memory Leaks.
My Question:
Why does a Rotation Change leak Memory and a Color Change does not when I start the asynctask the exact same way and do exact the same things within the asynctask?
I searched a little bit: http://developer.android.com/guide/topics/resources/runtime-changes.html
But I can't figure out if that is my Problem. The Rotation must do something different, like creating a new activity because of that creating a new reference to my edittext. And because of that he can't remove the old onTextChangeListener.
Please understand. I don't want to make my whole code public. But I think this isn't necessary anyway in this case.
The Rotation must do something different, like creating a new activity because of that creating a new reference to my edittext.
exactly, it destroys your current activity and creates a new one. If searchbar is a member variable of your AsyncTask then consider putting it into WeakReference:
WeakReference<SearchBar> searchbarPtr;
then access using searchBarPtr.get(), but check if its null, if so then it means it was garbage collected due to config change.
Also remember to not make your AsyncTask an inner class of you activity. If it is nested then make it static. Otherwise your asynctask will keep reference to your activity and it will prevent it from being destroyed - until its thread ends.
Unfortuanately making it all work corectly in all situations can be quite difficult and time consuming.
Hopefully no one will suggest preventing destruction of your activity through android:configChanges, implementing correct behaviour of your activity during rotation will prevent it from crashing/leaking in less common activity lifecycle moments which cannot be prevented by android:configChanges.
In android, the rotation destroys your current activity to start a new one.
To avoid it, you can add android:configChanges="orientation|screenSize" in your manifest file.
Below are tips to avoid memory leaks on Rotation change
Do not keep long-lived references to a context-activity (a reference to an activity should have the same life cycle as the activity itself)
Try using the context-application instead of a context-activity
Avoid non-static inner classes in an activity if you don't control their life cycle, use a static inner class and make a weak reference to the activity inside. The solution to this issue is to use a static inner class with a WeakReference to the outer class, as done in ViewRoot and its W inner class for instance
A garbage collector is not an insurance against memory leaks
Source: Avoiding memory leaks

Add a LinearLayout in bundle

I'm developing an application in which I build a treeView with some data. As the process is quite long I'd like to be able to save my built treeView (a LinearLayout) in some way to restore it when the activity is recalled.
Let's call my activity with the treeview T. I have Home->T->Resource and from Resource I go back Home. I implemented the onSaveInstance in T so there I can save in a bundle the variables I need when from Resource I go back Home and T is destroyed but the problem is that I'm not able to save all the linearLayout as a monolitic information in a bundle, the LinearLayout seems not to be parcelable.
So to summarize my cycle is:
Home-->T-->Resouce-->Home (T is destroyed and onSaveInstance is executed)-->T (I want to avoid the rebuilding of the LinearLayout).
Many thnaks
Don't do this. When you create any View, a layout or otherwise, it uses the inflating Activity's context to get and use system resources and internal app resources. When your Activity is destroyed, this context is no longer active.
If you do manage to save and load the LinearLayout back, you will get a lot of leaked windows, and other crashes, possible including NullPointerExceptions and Dead Objects.
You should always let Android recreate the layout for an Activity if the Activity is destroyed and started up again.
Why not save the resource elements of the 'treeView T' in some ordered data structure such as an ArrayList, HashMap etc... Save this data structure to a SharedPreference/Bundle etc... Whilst building the activity view, create the LinearLayout dynamically by reading the elements from the previously saved SharedPreference/Bundle.
/* Posting an example code below */
LinearLayout linLayout = new LinearLayout(this);
View childView = null; // read this childView in a sequential manner from the sharedPreference/Bundle
linLayout.addView(childView);

Android class design - should I reinstantiate dialog classes in my activity each time I want to show them?

MainActivity:
GeneralDialogFragment history_dialog = new GeneralDialogFragment();
public void showHistory(View view) {
Bundle bdl = new Bundle(1);
bdl.putString("dialog_type", "history");
history_dialog.setArguments(bdl);
history_dialog.show(getSupportFragmentManager(), null);
}
This code lives inside my activity.
Now, I was wandering whether I should put the definition
GeneralDialogFragment history_dialog = new GeneralDialogFragment();
inside or outside the showHistory() function?
What's the difference? Am I conserving memory if I put it outside (so It's instantiated only once?)
Whether or not you do that is primarily dependent on what the Fragment does. The overhead to create a new Fragment object is miniscule, but can add up depending on how many you need to make. If a DialogFragment is static and pops up every second (why would you do that?!?!), then it would probably be worth keeping it around. If a DialogFragment pops up every minute, then the end user probably won't notice if you're recreating the object every time.
Generally speaking though, Android (Google) recommends doing the lazy-loading approach. So if you created the object at the beginning of that method and called show(), you would lose the reference to it. There will still be a reference to it in the application's FragmentManager, so it will stay in memory. Once you call dismiss(), the Fragment will be removed and its memory that it is taking up will be garbage collected. Doing it this way has a slight CPU overhead, but at the gain of minimizing memory usage.
The exception to this would be if there's a lot of resources that need to be allocated for the fragment. In that case, it would be wise to load the resources once then keep them around. Images for example are better cached because they can take a long time to decode. They don't have to be saved in the Fragment itself, but it would be good for large images to stay in memory and passed to new Fragments as they are being created.
EDIT:
Just a side note, if you do decide that the reference needs to be kept, it would be good to have a check to make sure the dialog isn't already shown. The app will currently crash if you call showHistory() again if the dialog is currently up because you're calling setArguments() on a Fragment that is attached to the Activity.
It depends how many times you want to call the showHistoryMethod() and also do you need the object history_dialog outside the showHistory() method.
So assuming the object is not needed outside the showHistory() method and it will be only called once during the Activity, or not called at all, then place it inside the showHistory() method.
If you place it outside then the instance of GeneralDialogFragment will live during the lifetime of your main Acitivity. Whereas if it is inside the showHistory method than the GarabageCollector will free the memory, once it is not needed.

Memory leaks, singleInstance

I've got a couple of activities where I display images with help of this lib. The thing is the app is running out of memory. I tried to gc.clean(), null references, call clear on imageloader object but in vain.
In MAT i've found out that I have multiple objects of the same activity and it a default behaviour, if I'm not mistaken. I used singleInstance to suppress multiple instances and it has helped with memory leaks.
Right now, due to singleInstance, I'm having troubles with navigation. Do you think I should continue with singleInstance or try to fix memory leak with multiple instances ?
Here's ImageView gc roots inspection:
UPD:
Bitmap bitmap=null;
URL imageUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();
conn.setConnectTimeout(30000);
conn.setReadTimeout(30000);
conn.setInstanceFollowRedirects(true);
InputStream is=conn.getInputStream();
OutputStream os = new FileOutputStream(f);
Utils.CopyStream(is, os);
os.close();
bitmap = decodeFile(f);
return bitmap;
ImageView imageView = (ImageView) convertView;
if(convertView == null){
imageView = new ImageView(_currentActivity);
}
UPD2(navigation strategies):
I've got a constant header with buttons which start home activity(with gallery) and profile activity; secondly there's a subheader wich also holds 3 buttons which point to another 3 activities with listviews(consisting of imageviews + labels).
These header, subheader elements are available on every activity in application; The link-buttons do nothing but:
startActivity(new Intent(getActivity(), MainActivity.class));
or
Intent activityIntent = new Intent(getActivity(), SomeActivityWithListViewInside.class);
// passing some data like list id
activityIntent.putExtra("list_id", listId);
startActivity(activityIntent);
So, those activity instances are caused by those startActivity calls - do you think I should play with singleTop or any other intent parameter to avoid this issue ?
i would strongly recommend not to use the weird flags of the activities . i also always had problems with navigation (and still have , even with fragments) using the android API.
instead, i suggest that you solve the memory problems .
you should watch this video about memory leaks , and read this about bitmaps.
in short , here are some tips:
try to avoid static references , especially if they reference to context.
try to avoid referencing to context .
if you must reference to context , consider using the ApplicationContext.
remember to close threads and dialogs when closing the activity (if needed). try to close services when they are no longer needed.
prefer static inner classes over non static inner classes (since those have reference to the containing class) .
remember that anonymous classes also have reference to the containing class .
be careful with what you caches . try to avoid caching classes that contain a reference to context , such as views and drawables .
if possible try to use softReference and/or weakReference for referencing to "dangerous" objects that reference to context .
on android API 10 and below , remember to recycle your bitmaps . they usualy take a lot of memory.
if an activity takes too much memory and you go from it to another activity , consider finishing it and re-creating it when needed instead of going back to its old instance.
if you use any third party libraries or you are using native code (using NDK for example) , don't forget to release its memory when not needed . dalvik won't help you much about it .
You shouldn't use singleInstance launchMode. You need to get your navigation to work correctly using standard and/or singleTop launchMode. If you find that you have multiple instances of your activites, but you weren't expecting that, then you have something wrong in your navigation. Having multiple instances of your activities is keeping all your views and images around and that's probably what is causing the out-of-memory problems.
Update your post with your expected navigation and how you are managing it and maybe we can help you fix that.
EDIT: Respond to poster's UPD:
I don't know where you are setting _currentActivity, but that may be your problem. When you create a view inside an adapter you should always use the adapter's context (which is set up when it was created). So try this:
ImageView imageView = (ImageView) convertView;
if(convertView == null){
imageView = new ImageView(getContext());
}
EDIT: Respond to poster's UPD2:
You need to check your navigation to make sure that when the user chooses one of the buttons in your header or subheader that you don't have multiple instances of your activities in the activity stack (if that isn't what you want). If your activities use lots of image space (bitmaps, etc.) then you want to ensure that you don't have multiple instances. You can rethink your navigation or you can use combinations of singleTop, clearTop, reorderToFront, etc. so that you don't keep creating instances of your activities but just rearrange them in the activity stack to get the desired navigation behaviour.
Passing activity as a context when creating a view is not good: this prevents the activity from being "released". Also you may want to scale down bitmaps to the size you really need.

What to do when button assigned to a static variable in order to avoid memory leak

I read the link at http://developer.android.com/resources/articles/avoiding-memory-leaks.html
and need to know what should be done if I create a button
Button button = new Button(this) in an activity
and assign the button to a static variable "sButton"
sButton = button
What should be done in the onDestroy() method of the activity to avoid memory leakage.Will assigning sButton=null do the job sufficiently?Or do we have to call some other methods on sButton.
Suppose we do the same thing as above but instead of button we save the activity class itself or activity context to a static variable,what should be done so that to delete all reference of the activity so that the activity could be destroyed properly incase of memory shortage.
The example states that doing this with a simple drawable will leak the entire activity because of a chain of references. If you, instead of saving the drawable, save the activity you fall into the same problem.
Setting the reference to null should solve the problem, look for the exampled cited in the article. But beware of keeping an unneeded activity in memory.

Categories

Resources