How to control memory usage when calling multiple WebView in Android? - android

My app's main activity is a Activity that contains a Webview to load web pages.
I override the method shouldOverrideUrlLoading(WebView view, String url) to make every URL request call up an Intent and load in a new same activity containing WebView.
Doing this is to provide a better experience when BACK key is pressed, will just finish the current activity and back to the former activity, need no time to render the page again comparing to use goBack() in single webview.
But now the problem is that, after I open many URLs, creating a long queue of activitys in the background, the memory it uses became large.
When I go back to launcher and check the progresses, I can see my app caches more than 200M data. This is not acceptable...
And it's interesting that I can see my app used up my memory, but in the Heap view of DDMS in Eclipse I can see the app allocated no more than 10M memory. So I guess the 200M is webStorage cached by Webview?
Is there any way to control the memory?
I'm considering just save maybe 5 layers of activities at a time and when go back 5 times just jump back to home page. But still don't know how to release memory beside the 5 activities I need, which I'll never use again?
Or if it's because the WebView is keeping web page cached automatically, how can I manager this manually? Such as setting a limit of maximum cache size or page count?

Generally speaking I agree with Kevin's comment. I think keeping multiple Activity objects around to prevent reloading a WebView is counter-intuitive to a mobile environment with such limited resources.
That being said, you have a lot of different question and solution possibilities, so I don't have a single answer in mind. Look through these links to see if anything is helpful:
ActivityManager - has a ton of stuff you might be able to use, check out it's sub-classes.
ActivityManager.RunningTaskInfo - Never used it, but seems to have some useful stuff, especially with determining which Activity's are running.
ActivityManager.MemoryInfo - Can give you info on the available system memory as well as a bottom threshold of memory.
Application.onLowMemory() -Tells you when your app has been a memory hog. You could override this method and start destroying Activity's when this gets called. You probably need to call super.onLowMemory() to make sure the OS handles what it needs to.
One possible solution involving controlling the number of activities:
Override Application and create a public static ArrayList<Activity> that holds 5 Activity objects. Whenever you perform an onCreate() for an Activity you could add the Activity to the ArrayList and then check the size. If size is > 5 then send an intent to the Activity at position 0 that will cause it to handle the intent and call finish(). Then remove the object from the ArrayList.
Shouldn't be too much work, but the down-side is that you have to manually manage things. I am sure there is a more savvy solution possible.

Related

Android activities pile up in backstack causing OutOfMemory error

I have an application that allows a user to visit user profiles from a photo gallery (think of Instagram where you can visit a profile of a user from an image, or visit a profile of a user that left a comment on the image, etc).
PROBLEM:
If a user continues to visit profiles and photo galleries and repeats this cycle about 10 times there are too many instances of ViewProfile and Gallery on the backstack which results in an OutOfMemory error. This is due to the fact that ActivityA calls startActivity() to start ActivityB (who can start ActivityC and so on) so finish() is not called on ActivityA or ActivityB.
WHAT I WANT TO KNOW:
Since the activities call other activities it is possible that the application can go a long time without calling finish() on the activities that are piling up on the backstack.
So my first question is this:
1) Should my onPause() methods be cleaning up as much as possible to reduce the activities footprint in memory?
2) Is it possible to reduce an activity's footprint to zero (or close to zero) if finish (therefore onDestroy) is not called (or can't be expected to be called in a reasonable amount of time if the user decides to wander off and explore images and profiles of other users)?
3) Am I overlooking something when it comes to the management of activities?
4) Is there a way to completely kill the activity but save its state so that when a back button is pressed it can be rebuilt as the user would expect it?
EXAMPLE OF DESIRED BEHAVIOR:
In the Instagram application it seems that a user can endlessly click on user profiles (from comments or likes) and then load images of these users, then visit profiles until the end of time without a crash. Clearly they are doing something different but I'm not sure what, exactly. It would appear that they go long periods of time without calling finish on their activities as well since one activity calls another and they maintain a backstack that can be unwound as a user clicks on the back button.
I feel like I'm doing something wrong but I don't even know if I know the right questions to ask in order to fix it. My application works if I avoid opening lots of profiles of random users or go back "home" that will reset the state of the application. Any help would be greatly appreciated.
The main issue will be your bitmaps in your case. They use the most memory out of everything. Do not ever hold one directly in your class. Use an LRUCache, decide how much memory to use on Bitmaps in general, and only hold the cache keys in the Activity/fragments. This will make sure Bitmaps use a fixed amount of memory and the increase from additional activities is just the size of the key. A 2 level cache (memory cache and a disk cache) is a good idea. Depending on how bad the problem is, you may even want to go to the extreme of nulling out your ImageViews drawables in onPause so that reference isn't kept around either. It may be necessary in your case.
In more general solutions, you need to do just what you think- in onStop you need to jettison what you can from memory and reload it again in onStart, either from disk or from the network, preferring disk where possible. Data structures shared between activities should not be stored multiple places in memory, they should be shared via a service or singleton. You can't 100% stop this problem (the user can always decide to visit 1000 profiles), but if you prevent memory hogging resources from being replicated you can make it one they almost have to try to get to.

Android Intent And Memory

I'was wondering a simple thing. I'm making an android app and I started asking my self about memory usage.
What does the android OS make when I call a new intent??
Imagine i have an intent with only one button and this button onclickmethod is making a new intent of the same activity.
If on click I do this??
Intent activityN = new Intent(Activity.this,Activity.class);
startActivity(activityN);
is the firstActivity killed or does android keep it?
And if I click 50 times??
thanks for your answers
Activities life cycle is a tricky topic.
In most cases the activity will be kept in memory, but in some cases Android may decide to destroy it to reclaim resources. You have no control over this behaviour, which may change between OS versions and even hardware configurations. Don't try to fight it - embrace it.
Activity state is saved in onSaveInstanceState(Bundle), which is called before placing the activity in a background state.
When the Activity is about to be shown - but was destroyed to reclaim resources - it can be recreated using savedInstanceState in onCreate() method. You are expected to handle this situation. Most programmers don't care, which leads to strange errors on screen rotations and after longer periods of inactivity.
You may think about this mechanism as a serialization/deserialization scheme, that allows Android to optimize memory usage, discarding the data that can be recreated on demand (such as UI layout) and saving only things that cannot be recreated, such as UI state (entered text, checbox states, etc).
Since Activity destruction is rather unpredictable under normal conditions, Android provides special developer's option to always destroy activities when possible. This allows you to properly handle all corner cases around activity life cycle without too much effort. Explore your device's developer options.
You may want to check out those articles to learn more about the topic:
http://developer.android.com/training/basics/activity-lifecycle/index.html
http://developer.android.com/training/basics/activity-lifecycle/recreating.html
Going back to your question about clicking the button 50 times... it will probably create 50 instances of activity, stacked on top of each other. It may be the case that Android starts to destroy first activities to make room in memory for new ones. Let's say that the device have memory for only 49 activities. You start 49 - all are kept in memory. You start 50th one and the 1st is going to be destroyed. Her state is saved in Bundle, so when you press back 49 times, the first one will be re-created from this saved bundle.
That depends on the flags you are supplying with your Intent.
If no flags then every startActivity creates a new Activity.
However, there can be FLAG_ACTIVITY_CLEAR_TOP orFLAG_ACTIVITYY_SINGLE_TOP flags.
It's boring to repeat it all here, have a look at the docs:
http://developer.android.com/guide/components/tasks-and-back-stack.html
Android keeps the first Activity. That's why when the second Activity is finished you can return (with results even) to the same state. If you clicked the button 50 times, you'd start 50 new activities. YOu generally want to avoid that, so its a good idea to disable the button after the first press. Luckily your new activity should quickly start and cover up the button, so 50 is hard to do (although 2 or 3 isn't).

How to tell who stole the focus from your activity

For reasons outside of my control (so, please no "you're doing it wrong" replies), my Android app needs to play extremely nicely, i.e. when a user hits the home button, my app needs to go away and release all of its resources (which are heayy, more than 1GB RAM consumption etc). It seemed calling finish() in onPause() would do the trick, but here's the problem: onPause() and onStop() also get called when I start an activity of my own, e.g. a preference activity, for which I just want to normally return.
So, my problem is: How can I determine the reason for losing the focus? I can think of two options, neither of which are pretty:
Keep state, i.e. the new activity sets some global flag that I can check in the "covered" activity so it doesn't stop when onStop() gets called. Annoying because every new activity of mine would have to do that.
Use the ActivityManager to check the top activity, and if it's one of mine, don't commit suicide. Maybe better, but the documentation heavily discourages use of the ActivityManager for this type of stuff.
Any ideas?
You are welcome to use onUserLeaveHint(). It may cover some scenarios (e.g., HOME), but not all (e.g., incoming phone call).
please no "you're doing it wrong" replies
IMHO, "more than 1GB RAM consumption" is already "doing it wrong" for a Play Store app. Specialized apps (e.g., specific enterprise scenarios, dedicated hardware) might be able to get away with that.
One approach for option 1 that will make it less annoying to implement and maintain would be to use Application.registerActivityLifecycleCallbacks. At least this way you don't have to implement this logic multiple times in multiple classes or unnecessarily force your activities into a single otherwise unrelated base class.
Not that I necessarily endorse this option. Also note you'll need API 14+.

Life cycle of an android Activity for a ListView using cache?

I have implemented a listView which shows items downloaded on my Webserver. The listView uses 'cache'(holder), I click on one item of my listView and an intent (new activity) is called. It's works perfectly, but when I come back to my listView from this activity, the onStart() is called and the download is called again (whereas I don't need it because I use 'cache').
How can I fix this problem? I need to call the webserver in a function that it doesn't call when I come back in this activity.
Thanks
Florent
If you're trying to cache images between activities, there are two approaches:
Write files out to the SD card - This might be handy if you want the images to still be there even after your activity ends and restarts. The downside is that you now have to manage the lifecycle of the images and make sure that you periodically delete them so that your cache directory doesn't keep growing.
Use a service - I'm not sure how the performance with this method would be, and it tends to be a little more complicated. A service will keep on living while your activities come and go, holding your images in its memory. Read this answer for more about communicating with services.
Store images in a static singleton - The quick and dirty way; Just make sure you don't store too many in memory or you'll get an out of memory exception.
I'd probably prefer method 1.
Also refer to this comprehensive answer.

How should I deal with persisting an Android WebView if my activity gets destroyed while showing another one?

I'm building an Android application that's based around an enhanced WebView (based on PhoneGap). I've enhanced the WebView so that from JavaScript running inside you can invoke the native contact picker to choose a phone number (which may be supplied by Facebook for example).
The problem I have is that the native contact picker runs in an activity in another process and the Android docs say that while another activity is open my activity may get destroyed due to memory constraints. I haven't actually seen this happen in my application but if it did then I'm guessing my WebView's state would be destroyed and the code that was waiting for the picked contact would be terminated.
It seems a bit crazy that the activity requesting a contact could be destroyed while the contact picker is open. Does anyone know if that does indeed happen? Is there a way to persist the state of the WebView if it does?
Thanks,
-Shaun
Does anyone know if that does indeed
happen?
You're looking at the problem too simply.
You have a WebView. You open the contacts application. While the user is in the contacts application, a phone call comes in. While on the phone call (using a Bluetooth headset), a text message comes in, so the user opens that up from its Notification. While still on the phone call and texting away, a text comes in with a link, so she taps it and brings up the Browser application.
By this time, your activity is surely destroyed, except on maybe some of the most recent phones that have a fair bit of RAM.
Now, is that common? No. However, this also has nothing to do with the contacts application -- if the user presses HOME, at some point in the future, your activity may be destroyed to free up RAM as well.
Is there a way to persist the state of
the WebView if it does?
That depends on what you consider "the state of the WebView" to be. This really is PhoneGap's job, if you are making a PhoneGap-based app. So, you might consider asking them.
There is no way to persist the DOM. There are trivial ways to persist the URL (see onSaveInstanceState()). And there may be stuff in between that you consider part of "the state of the WebView" that may or may not be possible to save.
The long and detailed answer is a bit complicated, but essentially it comes down to a few points:
If the Android OS has to go cleaning up Activities or Services, it knows how to prioritise which ones should go first. It does this based on whether they're currently in the foreground (last to go), in the middle of executing code, waiting for a result, or simply sitting inactive in the background (first to go). You can be reasonably certain that if your WebView Activity launched the contact picker using the startActivityForResult, it won't be killed
There's a whole system for saving data if an activity is killed, such as the onPause method (which are triggered as soon as your activity leaves the foreground), the onSaveInstanceState method which is called when your activity is about to die. Read up on those to get more information, and the configurationChanged method when the screen orientation changes. If you haven't at least skim read the Activity Lifecycle document on the developers page, you must do that.
Lastly, I'm sure this question has been addressed many times, but with slightly varying wording or situations. Have a look around, see what else you can find.

Categories

Resources