I have set an UncaughtExceptionHandler, so that I can write out stack traces to disk when my app crashes. I set this handler like this:
if (!(Thread.getDefaultUncaughtExceptionHandler() instanceof CustomExceptionHandler)) {
exceptionHandler = new CustomExceptionHandler(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString(),
null, this);
Thread.setDefaultUncaughtExceptionHandler(exceptionHandler);
}
where CustomExceptionHandler implements UncaughtExceptionHandler. I keep the instance in my Activity, so I can use it for some other functionality (deleting the stack traces, retrieving them, etc).
I call the above piece of code in the onCreate of my Activity, but it seems to only trigger the first time any Activity is run.
I see the Thread.setDefaultUncaughtExceptionHandler call is static though, does that mean I can only set that handler only once in my app? Or can I set it per thread?
From docs
* Sets the default uncaught exception handler. This handler is invoked in
* case any Thread dies due to an unhandled exception.
Yep, this handler is global and you need to set it once per app
Is UncaughtExceptionHandler set application wide?
Yes. If you set it in an activity and the activity is destroyed the handler code in the activity may not exist any more.
I have set the handler in the Application-onCreate (not in the Activity) so it works for all Activities that belong to the Application to write crash logs.
For details see How to change crash message in android(if possible)
Here is the gpl-v3+ code for my crashlogger that writes logcat entries to file.
It is initialized in Application.onCreate like this
public class AndroFotoFinderApp extends Application {
private LogCat mCrashSaveToFile = null;
#Override public void onCreate() {
super.onCreate();
mCrashSaveToFile = new LogCat(this, Global.LOG_CONTEXT, HugeImageLoader.LOG_TAG,
PhotoViewAttacher.LOG_TAG, CupcakeGestureDetector.LOG_TAG,
FotoLibGlobal.LOG_TAG, ThumbNailUtils.LOG_TAG, IMapView.LOGTAG,
ExifInterface.LOG_TAG, ImageMetaReader.LOG_TAG);
}
}
where the constants Global.LOG_CONTEXT, HugeImageLoader.LOG_TAG, ...
are android logging tags of different moduls of my code use like this
Log.d(HugeImageLoader.LOG_TAG, "some log message from modul HugeImageLoader)
Related
Whenever I start the app I want to know if the app recovered from a crash. Can I store it in a flag ?
Do the crash and regular app exit scenario go through the same steps(lifecycle) in android ?
You can override your crash exception by using Thread.setDefaultUncaughtExceptionHandler. But do not forget, If you would not close your application it will freeze the screen by OS.
Example code :
//variable that inside the application class
private Thread.UncaughtExceptionHandler defaultUEH;
public void onCreate(){
super.onCreate();
defaultUEH = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler(){
#Override
public void uncaughtException(Thread t, Throwable e) {
handleUncaughtException(t,e);
defaultUEH.uncaughtException(thread,e);
}
});
}
private void handleUncaughtException(Thread thread,Throwable e){
//do whatever you like!
}
NOTE : There is no way to understand how is your program is opened
You don't get this information at all from the Android SDK. There are two options you could try, first is would be to have extensive logging/tracking of the apps life-cycle. In all of your activities keep track of when you activities are started, resumed, paused, stopped, and destroyed. (Also handle special usecase of when the device is rotated and the activity will experience a tear-down and restart). From here you will have information of when an activity has been stopped and you check the last state in the onCreate, onStart, or onResume of you activities to make sure that you're expected life-cycles where hit. If not, then you more than likely experienced a crash. Also note, on older version of Android certain teardown life-cycle callbacks weren't guaranteed to be called.
Second option would be to try using Thread.setDefaultUncaughtExceptionHandler. I have never personally used it but it may help in the usecase where an uncaught exception occurs. You could log such an event and check that flag once, the app is resumed. I am not sure of all of the side effects of using this mechanism such as, will this override the default uncaught exception behavior and remove the default stack trace that gets printed out, which would be very very very bad.
There is no way for the app to know whether it started from a crash; this is indistinguishable from starting the application for the first time after booting. In both cases you will see the onCreate lifecycle event for your activity.
If your application crashes, it stops processing lifecycle events. So no, lifecycle events will not be processed in the same way.
If you want to know whether your application is crashing, perhaps a better approach is to directly record when the application crashes. There are tools like Errbit that help record this when the application is running on general users' devices.
Try to add Exception handling to the code to whatever is causing a crash.
try{
//code causing the crash
} catch (Exception e){
//code to set flags whenever an event causing crash occurs.
}
In Android, runtime error dialog is prompted when error occurs. Is it possible to hide it? Or just hide it in production mode? Since it is showing some error message about the app which should not be exposed to users.
you should avoid your app to reaching the force close, when your app ends up in force close so your main thread has been killed and your app is no longer running and it's not accessible in coding so you can make another thread and handle situation and show custom dialog to the user.
Maybe Acra in Github would help you handle this issue.
You can set a custom handler for uncaught exceptions using Thread.setDefaultUncaughtExceptionHandler(..) on your main thread. There you can do what you need to do, including calling the default behaviour if you use the following pattern:
public class ExceptionHandler implements UncaughtExceptionHandler
{
/**
* The default handler.
*/
private UncaughtExceptionHandler defaultUEH;
/**
* Constructs the instance and sets the default handler to the one currently
* active at the time of construction.
*/
public ExceptionHandler()
{
this.defaultUEH = Thread.getDefaultUncaughtExceptionHandler();
}
#Override
public void uncaughtException(Thread thread, Throwable ex)
{
// do your stuff
// if you want to resume with the default behaviour, you call this
defaultUEH.uncaughtException(thread, ex);
}
}
In your Activity/Application:
Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler());
I'm using ACRA in my android application.
But I find that when exception happens within Application#onCreate() method,it only save the report file,rather than raising the dialog to send it.
It there something wrong with my code?
#ReportsCrashes(formKey="")
public class MyAndroidApplication extends Application
{
public void onCreate()
{
ACRAConfiguration config = ACRA.getConfig();
config.setMailTo("test#test.com");
config.setResToastText(R.string.acra_toast);
config.setResDialogText(R.string.acra_dlg_txt);
config.setResDialogCommentPrompt(R.string.acra_dlg_comment_prpmpt);
try
{
config.setMode(ReportingInteractionMode.DIALOG);
}
catch (ACRAConfigurationException e)
{
logger.error("fail to config ACRA", e);
return;
}
ACRA.setConfig(config);
ACRA.init(this);
someMethodThrowsException();
}
}
The onCreate of the Application is called before any Activity is created and does not have a UI, therefore ACRA cannot display a dialog. From the android docs for onCreate
Called when the application is starting, before any activity, service,
or receiver objects (excluding content providers) have been created.
Implementations should be as quick as possible (for example using lazy
initialization of state) since the time spent in this function
directly impacts the performance of starting the first activity,
service, or receiver in a process. If you override this method, be
sure to call super.onCreate().
So, be sure to call super.onCreate(), which your example above is missing, and you should not be doing a whole lot in there that would cause exceptions.
I'm seeing two problems with your code.
You don't call super.onCreate() after initializing ACRA
Your class should have tha annotation #Reportscrashes even if the parameters are set at runtime. Otherwise you will get an error in logcat saying ACRA#init called but no ReportsCrashes annotation on Application
Also, I'm not sure if the Application can show a dialog because it has no UI layout associated with it. Toast reporting works fine if you change both points above.
I have an activity in whose onCreate method an Init function is called (the function calls some native code involving lot of stuffs and calls to the openSLES audio api). The point is that this Init function makes the app crash when called again, which happens on a screen rotation or when i close the activity using Back button and i launch it again (but if in the meanwhile the process is killed, i have no troubles). I can't change the beaviour of the Init function.
I see that the process isn't killed when the activity is destroyed, I expected this after reading the docs, and it's a good thing since - if there is some audio signal playing - that continues playing after the activity has been destroyed, which is good for my purposes.
I tried to perform a check on the initialization state using onSaveInstanceState, but that works well only on screen-rotation, that's when onSaveInstanceState is called. The callback is not called when i push the Back button.
So i tried to use Shared Preferences, performing the state saving in onPause. But at this point i have the opposite problem: if the process is killed, the Shared Preferences values are kept, but in that case i need to perform Init again for the app to work properly.
I guess i need a way to know for sure if my activity is created after a process kill or not, but at the moment i can't see how. I thought about using the bundle instance in onPause method, but i can't figure how and whether this is possible. Any kind of hint would be really appreciated.
You can store pid of your process in shared preferences. If you compare in YourActivity.onCreate your current pid with stored one, you can determine when you must initialize OpenSLES.
You can initialize OpenSLES in Application-derived class, in YourApplication.onCreate - it's called only once.
edit:
I.e. declare following class:
public class YourApplication extends Application {
static private native synchronized void InitOpenSLES();
public YourApplication() {}
// see http://developer.android.com/reference/android/app/Application.html#onCreate() for details
#Override
public void onCreate() {
super.onCreate();
InitOpenSLES();
}
}
There's a simple solution to this problem. You don't need to save things in SharedPreferences to accomplish this. Just use a static (class) variable. Like this:
public class Globals {
public static boolean initialized = false;
}
The variable initialized will be set to false when the class is loaded. Only once. In your code, you then check and set the variable like this:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Initialize (but only once per process)
if (!Globals.initialized) {
init(); // Call init function that does things one time per process
Globals.initialized = true; // Remember we are initialized so we don't
// do it again
}
...
}
Even if all your activities are finished, if the OS doesn't kill your process the variable initialized will still be "true" if the application is started again. Once the OS kills the process, the variable will be set to "false" the next time the application is started and a new process is created.
For each and every process you have pid or process id. In your init function you can easily get the thread id and can save it in any integer value.
Thread.currentThread().getId()));
whenever your activity will restart just check that thread id is same or different. If thread id is different then call your function init function. Otherwise you have already done.
The application in question has already been deployed. I have found out about ACRA, and will be adding that in to find out why the application crashes are happening.
My problem however, is that the application will crash (the dialog will show up giving you the option to either "Force Close" or "Wait"), but instead of restarting the application completely, it resumes from a point before the crash occurred. This causes problems however, that are only fixed by restarting the application (eg. sudden loss of data, crashes that don't make sense, etc.).
So how can I force my application, once crashed, to just end? (NOT restart).
Edit: The biggest issue is that after the application crashes, all the data in my preferences file disappears, until the app is restarted when it comes back.
Android will re-start the last active activity by default. Instead of forcing it to restart, the better strategy would be to fix your application, so that activities don't crash or exit gracefully even if no data is available. If you absolutely depend on some data (intent extras) and there are no defaults, you can just call finish() if it is not available, or start the main/parent activity.
Another way is to have your own default exception handler which re-starts the main activity, etc. after reporting the crash using ACRA (I believe there is a feature request for this in ACRA's bugtracker).
I know this question is a bit old, but for future references and googlers, here is a complete answer from another, hard to find, stackoverflow question: Android App Restarts upon Crash/force close
create a class used to handle unCaughtException
public class MyExceptionHandler implements
java.lang.Thread.UncaughtExceptionHandler {
private final Context myContext;
private final Class<?> myActivityClass;
public MyExceptionHandler(Context context, Class<?> c) {
myContext = context;
myActivityClass = c;
}
public void uncaughtException(Thread thread, Throwable exception) {
StringWriter stackTrace = new StringWriter();
exception.printStackTrace(new PrintWriter(stackTrace));
System.err.println(stackTrace);// You can use LogCat too
Intent intent = new Intent(myContext, myActivityClass);
String s = stackTrace.toString();
//you can use this String to know what caused the exception and in which Activity
intent.putExtra("uncaughtException",
"Exception is: " + stackTrace.toString());
intent.putExtra("stacktrace", s);
myContext.startActivity(intent);
//for restarting the Activity
Process.killProcess(Process.myPid());
System.exit(0);
}
}
Then in every thread (usually you have just one, unless you start a new thread (Async or ... , ofcourse you know about threads if you are able to make new ;) ), set this class as the DefaultUncaughtExceptionHandler
Thread.setDefaultUncaughtExceptionHandler(new MyExceptionHandler(this,
YourCurrentActivity.class));
Remember though!
Do it in the very last step of developing your application, you HAVE to at least try handle all of your exceptions one by one before leaving them for DefaultUncaughtExceptionHandler