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).
Related
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.
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+.
I'm writing a simple app with 4 activities. I'll describe it quickly so you understand what I'm trying to achieve.
First activity - MainActivity has some TextEdit fields that collect 2 input parameters - number of reps and length of pause in seconds. A button takes me to the second activity:
WorkActivity - all this does is it starts counting until I press 'done' then it either calls PauseActivity, or if it has been the last rep, calls OverviewActivity.
PauseActivity counts down the seconds until next rep, then beeps at me to let me know it's time, and shows WorkActivity again.
OverviewActivity shows total workout time and times for each rep.
It also features a button that should just end the app. I know exiting your apps is not really in line with the Android application life cycle philosophy, but I need it (or something like it to happen).
I have a singleton controller class that keeps track of the reps and logs the time. I can kill this instance (or fake its death, so a new one will be created), but when I "close" the app and then click it again, I get the OverviewActivity instead of the expected MainActivity.
I expected that calling System.exit(0) would take care of things, simply shut down the application, so it will have to initialize anew when run again. Instead the whole thing started acting really derpy. When I click the button that calls System.exit(0), instead of vanishing my app sort of restarts. It shows the WorkActivity, and starts counting. When I click the done button (which should take me to PauseActivity) I get an exception. The application crashes - and then restarts again. This will repeat until I hit the homescreen button, and the app remains in this useless state until I kill it in application manager.
Also, I'm not exactly sure, but I think the System.exit(0) call (or the subsequent crash) disconnects the debugger, cause I've been unable to get Eclipse to hit any breakpoints afterwards. This means I can't really see the actual exception that occurs.
Can someone shed some light on this? Is there a way to use System.exit(0) correctly?
In the absence of this option, what would be the correct way of handling this?
I need the app to:
- when I click the final 'Done' button the Home button or the Back button, dispose of the Controller, (and everything else if possible), stop counting (if any timer is running) and essentially shut itself down)
- when I click the app's icon again, to show me a new instance (or one that appears new) with the MainActivity to greet me and all other activities in their default state.
Using System.exit(0) is a bad practice.
Calling exit() in this case would terminate the process, killing your
other component and potentially corrupting your data. The OS could
care less of course, but your users might not appreciate it.
Killing voluntarily your process will not help other applications, if
they have exhausted their internal Dalvik heap limit. No matter how
much physical memory a device has, the OS has a limit on how much
memory Dalvik is allowed to use in any process for heap allocations.
Thus, it is possible that the system has free half of its memory and a
particular application still hits OOM.
Do not use System.exit(0); instead you can just use finish().
Intent intent = new Intent(this, Activity2.class);
startActivity(intent);
finish();
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.
I have a complex app that has background threads (that could be in a service) which, when they receive data from the internet, need to notify my main display activity (to update on of several status indicators). All run in the same process (I see no reason to do otherwise).
However, in some circumstances, these events are frequent - 5 per second. Also, the events may occur when the activity is not-visible or even destroyed. I think the only thing novel about this question is the issue of efficiency. I still target the G1, for example.
There are a number of methods mentioned in this thread, but I don't know which of these are efficient enough, and will work if the activity is destroyed. Those methods are the "Android way" which I would prefer to follow.
I have three ugly anti-Android ways that work, but they also have drawbacks:
Have a thread in the activity that is waits on a semaphore, and when released, does the update. Disadvantages: extra thread, how to handle several event types
Like #1, but use a concurrent blocking queue object. Disadvantages: extra thread, same type of event may end up in the queue multiple times (not good)
Keep a static reference to a handler on the activity, and use that to run an updater. Disadvantages: (a) may leak a reference to the activity? (b) what happens when the activity changes state? (c) multiple runnables could end up there when only one is needed.
Also, the events may occur when the activity is not-visible or even destroyed.
If your activity is destroyed, there is nothing to update. If and when the user elects to re-visit that activity, the activity can get the current information in onResume() for display.
If your activity is in the background, there is nothing that needs to be updated, either. Again, if and when the user elects to re-visit that activity, the activity can get the current information in onResume() for display.
The ONLY time you need an activity to be notified of events in real time is if that activity is in the foreground. In that case, any of the solutions I outlined in the answer you linked to could work. The binding option or Messenger are probably the lightest-weight solutions.
I have a complex app that has background threads (that could be in a service)
Not "could be" -- "must be", if they are to live beyond the scope of any given activity instance.
I have three ugly anti-Android ways that work
None of those work without potential memory leaks.