I have a strange situation I am seeing.
I am reading some info from a database , then bringing up a dialog :
PSEUDO
val = DBaseManager.readValue(i,POS_ONE);
if(val == 1)
{
Dialog_Test myDialog = new
Dialog_Test (myContext,"",new addListener(),DBaseManager);
myDialog.show();
}
as you can see I pass into the Dialog the DBaseManager so it can use it also.
then in there I use it like this :
DBaseManager.readValue(k,POS_TWO);
etc.
Now this works 99% of the time, however I have had some crash logs pointing to these lines in the dialog with null pointer exceptions.
To me this is indicating that on some devices my onPause or OnDestory methods are being called in the main activity which closes and nulls DBaseManager. These are 1.6, 2.2 and 2.3 devices.
So the question is why , and how to prevent ? I have added some null pointer checks in to prevent the crashes but it still far from ideal.
UPDATE: On my devices at least when I do a screen rotate the activity is restarted and the dialog disappears - could it be on some the dialogs remains up???
This probably depends on the scope of your Dialog and where in the code you are opening it. The default behavior is for the Activity to go through its lifecycle when the orientation changes unless you explicitly handle orientation changes and override onOrientationChanged. You might want to do something like maintain the state of your dialog at the Activity level (such as adding a boolean isDialogDisplayed as a class variable) and then in onCreate or onResume check that and reopen the dialog.
As far as the DBaseManager object being cleaned up, I have experienced similar things. Large objects getting cleaned up when you don't expect them to (there are still pointers to these objects, but they've been nulled by the system somehow). I've identified these in my applications and just been more careful. Instead of null checking and reloading when null only when it's possible that your code could have nulled the variable (or using lazy loading), I'd always treat this object as if it were lazy loaded. Add a method that returns this object instead of accessing it directly, and always check for null and reload if it is.
For both of these two issues, you might want to override all the lifecycle methods of this Activity and log them. Don't forget to call the lifecycle methods on the base class within your overrides (ie. public void onPause() { super.onPause() ..., etc)!
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.
The above issue arises once in two debug. And the weird thing is app doesn't crashes, it stops responding.
What does this mean? And what wrong is happening inside?
You should really read the documentation on this, it will help you out a lot.
The savedInstanceState field is set when your Activity is being re-created from a previous instance. This happens e.g. when the device changes orientation or when the user navigates away, and then navigates back to your Activity. When the field is set, this allows you to restore the state of the Activity as the user left it.
The savedInstanceState field is null when your Activity has no previous state to restore from.
When your Activity is about to be destroyed, you can add variables to the savedInstanceState by overriding onSaveInstanceState(Bundle bundle) in your Activity, and adding them to the Bundle. The next time your Activity's onCreate(Bundle savedInstanceState) will be called, the fields you set in the onSaveInstanceState() will be available to you in the Bundle.
Finally, the "application not responsive' dialog is shown when you do too much work on your application's main (UI) Thread. However as pelotasplus mentioned this may also occur when you connect the debugger. If this dialog is shown when you have the debugger attached I wouldn't worry about it. However, if this dialog is shown during normal use of your app, this indicates you perform too much processing on the main Thread and you should move the heavy lifting to background threads, e.g. by using Loader, AsyncTask, IntentService, Thread or any of the other options available for this.
There are two things here, first of all
saveInstanceState = null
you see in your debugger is nothing unusual. Sometimes it's null, sometimes it's not. This is how Android framework works ;-)
For more information about that go read official docs about Activity lifecycle: https://developer.android.com/training/basics/activity-lifecycle/recreating.html#RestoreState
Second thing is that ANR (application not responding) window which happens a lot when debugging your apps. Just press WAIT and you are good to go.
I have a stock Nexus 5 running 4.4.2 (using ART if it matters) and I've found an interesting scenario. I have this as my onDestroy():
#Override
protected void onDestroy() {
super.onDestroy();
t.setText("onDestroy");
t.show();
}
It's a GPS oriented app so I'm up and walking around. I am using the technique mentioned in this question to show a lot of debug toast messages.
Anyway, when I rotate my app, the toast appears. I understand that the activity is destroyed and recreated for the new orientation, but how can I know what's really going on? How can I tell when my app is REALLY getting destroyed and not just being rotated? Similar to this question, I want to log out when a particular activity is destroyed.
Since Honeycomb, the isChangingConfigurations() method can be queried to check whether the Activity is being recreated due to configuration changes. Alternatively, the isFinishing() method can be queried on any API level to check whether the Activity is actually being finished, or is only being destroyed temporarily by the system.
As far as I can determine, the two methods should always return mutually consistent results in practice. The only point where they might have diverged is when the system kills the process to clear memory, but there are no callbacks or interaction with the app at that point.
The documentation of the onDestroy() method mentions the use of the isFinishing() method:
Perform any final cleanup before an activity is destroyed. This can happen either because the activity is finishing (someone called finish() on it, or because the system is temporarily destroying this instance of the activity to save space. You can distinguish between these two scenarios with the isFinishing() method.
You can put it in a fragment with setRetainInstanceState(true) set. Place your code in the onDestroy() method of the fragment. Then, the fragment will not be destroyed on orientation changes.
First of all, you should not use onDestroy() to do anything because its not guaranteed to be called. I would put things on the onPause() method; I wouldn't even put things in onStop().
Also, Im not sure why you want to log out a user when they navigate away from the app. I would rather implement some kind of timer on the app or server to log out after x time.
Now, the answer lies in the documentation: http://developer.android.com/reference/android/app/Activity.html#ConfigurationChanges
You might want to override onConfigurationChanged so that your activity is not restarted.
I found a couple of solutions which are really just patterns to detect when the screen rotates. Alternatively, you can determine that the device was actually destroyed by checking some static data member to see if it was initialized or not.
Configuration changed solutions:
The first one involves handling all of the configuration changes in the onConfigurationChanged callback.
"Note that this will only be called if you have selected
configurations you would like to handle with the configChanges
attribute in your manifest."
The second involves listening for Display.getRotation() which returns a Surface.ROTATION_* object. Which is the new orientation of your screen relative to the natural state of the device orientation.
Again, you can use the configuration changes along with the static member.
Add ConfigChanges.UiMode flag to the ConfigurationChanges attribute for your MainActivity class, and this solves the problem.
More details: Android Launcher "OnDestroy" gets called twice
I am trying to make my game multitask friendly, however whenever the screen is turned off and turned back on, or the game is navigated away from and back to, I get a null pointer exception pointing to this part of the code:
GLES20.glUniformMatrix3fv(mTextureMatrixHandle, 1, false, render.mTexMatrix, 0);
I don't have anything apart from super.onResume(); and mGLSurfaceView.onResume(); for the onResume() method and the same for onPause(), except with onPause rather than onResume. Any idea how to fix this?
You must make sure that the objects are re-created when the activity is started again. Where are those variables initialized? Typical case for this kind error is when you store objects in a global variables. When the activity is created again, they are null.
So: check which of mTextureMatrixHandle, render, or render.mTexMatrix is null. Re-create it if it is null.
In general, you should not rely on any data being restored at activity creation. So don't use global variables, and rely only on data passed via the intent. If you really want to use global data, you should be aware that it may be reset, and re-create it when needed.
I am using a separate class with only static fields, to store current application data.
It is partly populated from sharedpreferences on application startup. The rest is data like results of some action, used for further browsing these results (multiple activities that use the results).
I can go to the home screen, start other applications etc. and when I return to my own application it just works correctly.
However, since the new Error Reporting feature I get some bug reports all related to a nullreference error. The object that is null is a reference to the static field in the mentioned separate class.
Since I cannot reproduce the bug I am inclined to think this is due to the application getting killed due to low memory, and when it relaunches it calls the oncreate from the activity that the user was currently in. However all the static data in the separate class is not restored and thus it crashes.
I would like to know: Is there a way to force the application to "restart" completely, and not start with the last used activity if it gets killed? Or is that standard behaviour?
Can I do this programmatically? Like when the static fields are null, restart app?
Restarting the activity where the user was is normal behaviour - the idea is to make it look to the user like the app was never closed. There are two things you can look at:
protected void onSaveInstanceState(Bundle outState){
// This gets called by the system when it's about to kill your app
// Put all your data in the outState bundle
}
That bundle is the same one that gets passed to the activity in onCreate(). You can then get any necessary information out of it and restore the values in the static class.
The other way is to simply check the values in the onResume() method of any of your activities. If the values are null or wrong in some way, then you can call start the original activity and finish() the one being started.