When are the view's 'killed' and no more exist in Android ?
For example, suppose I have an asynctask and I run some network related stuff in the doInBackground() method. Then, I need to update my UI views in the onPostExecute() method.
Assume my doInBackground() took a while and while it was being processed the user moved back or even pressed the home button. The task will continue because doInBackground runs on a seperate thread, however, once it is finished and onPostExecute is called to update the views, the views might not be there.
Even if the activity is not visible (either gone to home screen, or another activity), what happens when the views try to get accessed and modifed ? How long do they stay in the 'heap/memory/whatever', do they get garbage collected after onDestroy ? or they stay around after that ?
I know a way to not get into this hastle is to use a switch that gets turned on and off inside onResume and onStop and check it before updating the views in onPostExecute, but I am unsure if this is solid approach applied in the android apps ? if not, what is the suggested way ?
A view inside an activity can be considered like any other object within the activity class. It will stay in the memory as long as it is referenced by the some other object. This object could be another view or activity. This means the view will be gone if:
1) The activity or the parent view remove it removeView() from the view tree thus no one keeps a reference to it.
2) The activity or parent view that contain the view are destroyed/gone from the memory.
If the activity is not visible (either gone to home screen, or another
activity), what happens when the views try to get accessed and modifed
? How long do they stay in the 'heap/memory/whatever', do they get
garbage collected after onDestroy ? or they stay around after that ?
You can access the view, as long as your activity is available. And you can find more about that by reading the Activity Lifecycle
When you try to access a view that is gone from the memory, you will get a NullPointerException. The simple & solid way how you can handle onPostExecute is by checking for null before updating, example:
// inside onPostExecute
if(textView != null) {
textView.setText("Background Method Finished");
}
The advantage of this approach is:
1) You do not have to explicitly keep track of show/hide.
2) Sometimes, view is not on the screen does not mean that it gone from the memory. For example, let say your AsyncTask finishes while your activity is paused/stopped not destroyed. In this case, you can still update the view, so that when the activity is resumed the update is visible and is not lost.
Cancel async task when user leave from that activity. After cancelled task, onPostExecute will not called and capture cancel event in onCanceled event in async task class.
You have many ways
1.You can cancel the asynctask
2.You can kill the process,so there will be nothing in the menory or heap.
Related
I understand the basic Lifecycle of Activity/Fragment but sometimes, when user puts the app in background state for long time I'm not sure what's going to happen when he opens it again. Recently I've encountered a bug: User gets nullpointer exception by calling method of a view saved in class variable (textView = findViewById(...)), inside fragment's OnResume method. The variable is set in OnViewCreated(). Is that possible that over long period of time fragment might lose it's fields due to lack of memory? When onResume() will be called and when onCreate()?
If the app is in background for a long time its process will be killed by OS, or if the device is running low memory. To test how your app works under these conditions use flag "Do not keep activities" in Developer options on your device. In the described case onCreate will be called when Activity will come to the foreground.
If the process is not yet killed then onResume will be triggered. Normal variables persist, but the problem is that you can never be sure when you're calling onResume and when you're calling onCreate (since you have no control over when Android just goes and tosses stuff on the stack out the window... anything not currently being used is eligible for destruction).
So my solution is to add a null check with if condition: if the variable is null then initialize and perform actions, if not then just preform actions.
I have inherited some code hence I don't have true freedom to change it. :(
I have a main activity, from which other activities (I will refer to these as sub activities from now on) are called. Whenever one of these completes, it calls finish and returns data to the main activity.
Each activity (including the main one) has a bar on the top that displays a custom view. The custom view contains a canvas which has a drawing that is dependant upon the state of the network.. i.e. wifi/mobile etc...
Since that 'state' data never changes, it's held within a singleton and the view gets data from the singleton to define what it draws. That is working with no issues, i.e. the data is always as I expect it.
When I first launch the MainActivity, as the network changes, the data changes and each call to 'invalidate' the view receives a system call to 'onDraw' as I would expect.
In each of the sub activities the same is again true.
Upon finishing a sub activity and returning to the mainActivity, calls to invalidate no longer cause a call to onDraw to occur.
I have looked at this for quite a while now and just cannot figure out what is going wrong.
In my constructor I have:
setWillNotDraw(false);
Whenever the data changes the following methods are called:
invalidate();
requestLayout();
Now, there's one more thing... upon returning to the activity at that immediate point, I refresh and this DOES draw correctly, i.e. invalidate does trigger an onDraw call... any subsequent network changes (which are propogated) fail to result in the onDraw call.
I'm wondering if this is to do with the view somehow being detached. I can see that 'onDetachedFromWindow' is called, however the trigger for this is the destruction of the subactivity, hence I don't see why that should affect the MainActivity but it's the only thing I can think of.
I'm hoping I've provided enough information for someone to help me...
Well, in the end my answer has very little to do with the question and I guess this is an example of how an issue can be solved by going back to absolute basics and checking for the obvious.
My activities all inherit from an abstract activity. Within that activity there is an instance of the view. The views in which I was having trouble were using that declaration as opposed to having their own instance, hence behaviour from one activity was then affecting another inadvertently.
So, if I'd been able to post up all the code, I'm sure someone else would have spotted this but, unfortunately I couldn't in this instance.
Still, whilst this posting doesn't provide a resolution that will help others, maybe it does say... step back and check the obvious first!
what would be the best way to continue the current Activity once its internal BroadcastReceiver onReceived has been called.
is it possible to start another thread from within the onReceived() method, and also can I start a AsyncTask inside the onReceived() ?
is it possible to start the same activity again from within the onReceived method?
thanx
Please note that an Activity does not survive a screen orientation change -- it gets re-created along with the View hierarchy. From the MVC viewpoint, an Activity is a Controller. Data that must survive an orientation change must go to the Model.
If you keep a reference to the Activity that has gone from the screen due to orientation change, that's a resource leak, and if the Activity receives the result of some AsyncTask, that's also a waste of CPU time and battery power.
Having said that, Activity defines runOnUiThread (Runnable action) which most likely is not what you really need, but it will at least work.
I mean, after the receiver's method is called, the normal cycle continues (reaction on events etc.), so you just need to react on the event reported to your BroadcastReceiver and reach the closing brace of the function.
If you ask if you can rely on that the same instance of SomeActivity will be shown when a long operation completes, the answer is no. For example, the user starts a long operation, then changes the screen orientation. The first SomeActivity, the one that started the long operation, will not be shown when the long operation completes.
My Problem: Is it possible to prevent an activity to call OnResume() when it is being created? As I saw after the OnCreate() and onStart() method runs, the next one is the onResume(), although I only want to have it when I resume the activity from the paused state.
Why do I need this: I launch my activity (FragmentActivity, so lets say OnPostResume() ) starting with a thread which takes about 2-3s to be ready getting data from an external database. After the thread is done, I call a method which needs these data and I want to call it everytime that activity gets visible. The thread runs only when the FragmentActivity is created (onCreate()), and I cannot put the method into the onResume() because onResume() would be running way before the thread would finish its task. So it would receive not-ready data.
Anyone has a better idea?
Not sure of the exact application of this but I'll make a suggestion.
If you use an AsyncTask, you can send it off to get the data you need and in the onPostExcecute() method you can call your method that requires the data or update the view as needed. (It runs on the UI thread)
If you happen to already have the data you need in certain scenarios you could also bypass the AsyncTask and directly update the view.
This AsyncTask can be triggered in the onResume() method.
If I'm missing something, please let me know and I can adjust my suggestion.
I didn't understand the purpose of this, but here's a possible solution:
If you only wish to get the even of onResume on states that didn't have the onCreate before, just use a flag.
In the onCreate, set it to true, in the onResume check the flag (and also set it to false). if it was true, it means the onCreate was called before.
I personally would prefer to check if the result available, rather than always executing the getter-code in onResume. If the user somehow resumes your activity before the background thread is finished, you'd have a call on onResume, but don't want to display a result.
Maybe it would be a good idea to calculate/fetch the values in the thread, and let the thread return immediately (and cause the values to get filled in) if the values are already cached somewhere. That way you'd only have one entry point (the thread) for updating your UI instead of two (the thread and the onResume method).
I have seen some discussion here on Stack Overflow related to using Activity.onRetainNonConfigurationInstance() to maintain a background thread started by one instance of an Activity and pass it to the next instance of the Activity which results, for example, when the phone's orientation changes from portrait to landscape.
The discussions do not specify exactly what can be done with the thread wrapped in the Object returned from onRetainNonConfigurationInstance().
For example:
1. Is there a way to actually keep the background thread running using this technique?
2. Do you need to somehow pause the thread when the previous instance of Activity is going away and then restart it again in the new instance?
Can anyone provide a short example?
Any details would be appreciated.
You can return anything you want to onRetainNonConfigurationInstance(). If you have a Thread that you want passed from one instance of the Activity to another, you can either return it directly, or put it inside another object that you return from onRetainNonConfigurationInstance(). You don't need to pause the thread or interact with it in any way. It just keeps running as if nothing happened.
The only thing you need to be concerned about is how the Thread interacts with the Activity (if at all). If the thread will call the Activity back (to indicate progress or something like that) then you somehow need to give the thread a reference to the new Activity, as the old Activity will be dead.
What do you want to do in your background thread?
EDIT (add more details about threads/activities):
Threads have their own lifetimes which are completely disconnected from Activities. If you create a Thread in an Activity and start it, it will run to completion no matter what your Activity does. The only thing that will stop the thread explicitly is if Android decides to kill your process (which it may do if your process contains no active activities).
The thread will continue to run. For an example of what you can do with this, you can check out the android Ignition project and its IgnitedAsyncTask (and related examples).
The idea is that you will maintain a reference to your thread (usually an AsyncTask) somewhere in your Activity, and occasionally your thread (again, especially if it's an AsyncTask) will require a reference to a Context in order to perform some kind of UI update upon the conclusion of its background task. You will need to make sure that the Context (and anything derived from it--like a TextView or the like) to which your thread has a reference is non-null, or else it will crash.
You might use getLastNonConfigurationInstance() to set your Activity's reference to the thread, and then call a setter on the thread to set its Context reference (to avoid any related null pointer crash).