Memory leaks, singleInstance - android

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.

Related

How do you pass the MethodChannel Result to a new Activity?

fellow Flutter enthusiasts, pros, and future friends. I've been really banging my head from trying to pass the MethodChannel.Result from the MainActivity to a new activity (to handle the result handler after performing some work there).
In iOS, I've stuffed the FlutterResult into a struct with other pertinent values and passed that into a new ViewController, and have been successfully running the result handler from there.
How do I go about doing so in Android? Since the result isn't serializable I can't add it as an extra in an intent.
In my MainActivity, I'm running 'setMethodCallHandler', and in the proper call.method case, I'm creating a new intent and starting it. I'm successful adding in and pulling out String values in the new activity but it's that Flutter Result that I'm needing to pass over.
What is the recommended way to achieve this? Am I going about it the wrong way?
I ask if I'm doing it incorrectly because when I finish() this new activity, there is a half-second black screen that takes over the screen when the activity is dismissed.
Not sure if it matters, but I'm writing in Kotlin. I would be happy to hear any Java recommendations though.
Thank you all in advance.
Without a little bit more context about what exactly you're doing in the new activity, it's a little bit hard to tell why you'd be getting a black screen. Theoretically, the flutter activity should still be running and should show up when you finish the new activity - unless you're doing something blocking on the UI thread.
However, for passing the result - what you want to be doing is to keep a reference to the result handler wherever you're receiving the message from flutter, and making use of android's startActivityForResult method. See the android docs on getting a result from an activity. Your 'worker' activity should do whatever it needs to do, then pass the result back by calling setResult before finish. The data you pass back must be serializable.
There is a slight added wrinkle - you're not necessarily working with your own activity here (if you're writing a plugin anyways). If that's the case, you'll need to implement ActivityResultListener and call registrar.addActivityResultListener in your plugin's registerWith function. If you're just doing this in an app, you can simply override onActivityResult - just make sure to call super or you might break other flutter plugins.
There is another possible solution to this, with various levels of hacky-ness depending on how in-depth you want to be. The solution is to simply use a Singleton or Global to store what you need between activities. For anyone out there who's reading this - I don't endorse doing this, I'm just providing an alternative. There are caveats to go with this - among them is that globals and to a lesser extent singletons are seen to be a bad idea for many reasons, among them code maintainability. If you absolutely must go down this route, I suggest using a Registry pattern rather than a simple singleton - i.e. you create a key that is serializable and store the Result in essentially a global/singleton map using the key, pass the key to the new activity, then in the new activity retrieve the value with that key (and make sure to remove the object from the map).
Note that the global/singleton/registry option won't work properly if the app is stopped by android as the activity could be recreated from its intent, but the object in memory may not persist. Then again - the flutter callback won't persist anyways so that point might be moot anyways.
If you're still seeing the black screen after finishing the new activity, that sounds more like a bug or something to do with the implementation of your new activity than a flutter problem.

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

Android app best layout for image buttons?

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.

How to avoid OutofMemoryException?

Activites in my app contain fragments, which in turn contain listview/gridviews that're full of bitmap data. Eventually a user would run out of memory because views of the previous activities and their fragments don't get destroyed. So, when user has reached, say, 10th activity - 9 previous hold a plenty of bitmap data.
I'm already using weakrefences, however MAT says that some fragment's view holds reference to, for instance, Gallery which in turn holds adapter etc. So ImageViews retain alive and so do bitmaps.
So far I've experemented with completely removing fragments, removing adapters. Sometimes it works, but I wonder why should this be so complicated and if there's any simpler way to free/acquire without much coding ?
UPD
I would appreciate an example of open-source app where the same problems are challenged.
UPD2
Blueprint for most of my activities is: activity holds fragment. fragment holds AbslistView that are full of imageviews.
Thanks.
It is difficult to get it done without using up all the memory.
That requires on demand (re)loading, freeing memory on view destruction and careful design of your fragments and classes.
https://developer.android.com/training/displaying-bitmaps/index.html has some valuable information about loading images that way.
If you load all your images through some sort of asynchronous caching loader, clear the cache on onViewDestroyed or onDetached depending on your needs and don't keep other references to those bitmaps you should have removed most of your problems.
The lifecycle is pretty symmetrical (onCreate<>onDestroy, ...) so it's a good idea to null any references that you created in exactly the other side of that lifecycle part. Assuming you use appropriate places in the lifecycle you get a lot of memory management for free. In your case you should check that in case your fragments are retained you don't keep references to the Gallery or ImageViews (should only exists between onCreateView -> onDestroyView)
I would recommend keeping only that what you need in memory and destroy everything else. It is bad form to use up all available memory. I would look at the activity life cycle and understand it completely to resolve your issue:
https://developer.android.com/reference/android/app/Activity.html
I recommend watching the Memory management for Android apps Google IO 2011 presentation.
You should also examine your app's workflow to determine when you can start destroying old activities or freeing other resources.
You can also use ActivityManager.getProcessMemoryInfo() to retrieve memory usage information for your process to aid in determining whether you need to free some old resources.
If your outofmemoryexception happens in your adapter's getView method,
You could isolate the line it usually happens and surround it with a try-catch like this :
try {
// load image (or whatever your loadimage is)
mViewHolder.thumbImage.loadImage();
} catch (OutOfMemoryError e) {
// clear your image cache here if you have one
// call gc
System.gc();
// load image retry
mViewHolder.thumbImage.loadImage();
}
Its not the most elegant solution in the world but it should help.

Method to pass an image value?

How would i go about passing an image to the next page for android?
You have a few different choices here.
I assume by "next page" you mean the next Activity? I also assume you've created a Bitmap out of the image. Bitmap implements Parcelable so you could just put it directly into the extras of the intent. This also lets you can put it in the Bundle in your activity's onSaveInstanceState() method, so it is restored whenever that activity is rotated or is recreated for some other reason.
However, if you have more than one image, this is less than ideal, since you have to serialize and deserialize all your images each time you switch activities. If you obtained the image from the file system somewhere, or if you got it from a ContentProvider like the MediaStore, then you'll have a Uri for it. So, you could just put the Uri in the intent, and then recreate the bitmap each time you load the activity. This has the advantage of being a smaller amount of serialized data, but it's even worse in terms of processing because now you have to read from the filesystem and decompress the image every time.
Therefore, if you are concerned with performance, the only good method is to store the image(s) to a static variable so that it can be accessed by your other activities. This way, both activities actually use the same image instead of duplicating it, thereby saving memory. The only disadvantage with this approach is that you will not be able to start the activity in a new task. All activities that use the image must run in the same process. Also, if you are manually recycling the image when you're done (via the Bitmap.recycle() method), then you'll have to make sure no one else is using the image before you recycle it.
Personally, a lot of my apps download images from a server and I store all HTTP responses in a cache, so whenever I need one of these images I re-request it from the cache. The cache is a singleton so it can be accessed from any of my activities.
Do you have a sample code for passing an image value?
Will this work?
Intent i = new Intent(Moods.this, New_Entry.class);
Bundle f = new Bundle();
f.putString("image", img);

Categories

Resources