How to Implement Uncaughtexception Android

how to implement uncaughtException android

You can catch all uncaught exceptions in your Application extension class. In the exception handler do something about exception and try to set up AlarmManager to restart your app. Here is example how I do it in my app, but I only log exception to a db.

public class MyApplication extends Application {
// uncaught exception handler variable
private UncaughtExceptionHandler defaultUEH;

// handler listener
private Thread.UncaughtExceptionHandler _unCaughtExceptionHandler =
new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread thread, Throwable ex) {

// here I do logging of exception to a db
PendingIntent myActivity = PendingIntent.getActivity(getContext(),
192837, new Intent(getContext(), MyActivity.class),
PendingIntent.FLAG_ONE_SHOT);

AlarmManager alarmManager;
alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
15000, myActivity );
System.exit(2);

// re-throw critical exception further to the os (important)
defaultUEH.uncaughtException(thread, ex);
}
};

public MyApplication() {
defaultUEH = Thread.getDefaultUncaughtExceptionHandler();

// setup handler for uncaught exception
Thread.setDefaultUncaughtExceptionHandler(_unCaughtExceptionHandler);
}
}

Need to handle uncaught exception and send log file

Here's the complete solution (almost: I omitted the UI layout and button handling) - derived from a lot of experimentation and various posts from others related to issues that came up along the way.

There are a number of things you need to do:

  1. Handle uncaughtException in your Application subclass.
  2. After catching an exception, start a new activity to ask the user to send
    a log.
  3. Extract the log info from logcat's files and write to your
    own file.
  4. Start an email app, providing your file as an attachment.
  5. Manifest: filter your activity to be recognized by your exception handler.
  6. Optionally, setup Proguard to strip out Log.d() and Log.v().

Now, here are the details:

(1 & 2) Handle uncaughtException, start send log activity:

public class MyApplication extends Application
{
public void onCreate ()
{
// Setup handler for uncaught exceptions.
Thread.setDefaultUncaughtExceptionHandler (new Thread.UncaughtExceptionHandler()
{
@Override
public void uncaughtException (Thread thread, Throwable e)
{
handleUncaughtException (thread, e);
}
});
}

public void handleUncaughtException (Thread thread, Throwable e)
{
e.printStackTrace(); // not all Android versions will print the stack trace automatically

Intent intent = new Intent ();
intent.setAction ("com.mydomain.SEND_LOG"); // see step 5.
intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK); // required when starting from Application
startActivity (intent);

System.exit(1); // kill off the crashed app
}
}

(3) Extract log (I put this an my SendLog Activity):

private String extractLogToFile()
{
PackageManager manager = this.getPackageManager();
PackageInfo info = null;
try {
info = manager.getPackageInfo (this.getPackageName(), 0);
} catch (NameNotFoundException e2) {
}
String model = Build.MODEL;
if (!model.startsWith(Build.MANUFACTURER))
model = Build.MANUFACTURER + " " + model;

// Make file name - file must be saved to external storage or it wont be readable by
// the email app.
String path = Environment.getExternalStorageDirectory() + "/" + "MyApp/";
String fullName = path + <some name>;

// Extract to file.
File file = new File (fullName);
InputStreamReader reader = null;
FileWriter writer = null;
try
{
// For Android 4.0 and earlier, you will get all app's log output, so filter it to
// mostly limit it to your app's output. In later versions, the filtering isn't needed.
String cmd = (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) ?
"logcat -d -v time MyApp:v dalvikvm:v System.err:v *:s" :
"logcat -d -v time";

// get input stream
Process process = Runtime.getRuntime().exec(cmd);
reader = new InputStreamReader (process.getInputStream());

// write output stream
writer = new FileWriter (file);
writer.write ("Android version: " + Build.VERSION.SDK_INT + "\n");
writer.write ("Device: " + model + "\n");
writer.write ("App version: " + (info == null ? "(null)" : info.versionCode) + "\n");

char[] buffer = new char[10000];
do
{
int n = reader.read (buffer, 0, buffer.length);
if (n == -1)
break;
writer.write (buffer, 0, n);
} while (true);

reader.close();
writer.close();
}
catch (IOException e)
{
if (writer != null)
try {
writer.close();
} catch (IOException e1) {
}
if (reader != null)
try {
reader.close();
} catch (IOException e1) {
}

// You might want to write a failure message to the log here.
return null;
}

return fullName;
}

(4) Start an email app (also in my SendLog Activity):

private void sendLogFile ()
{
String fullName = extractLogToFile();
if (fullName == null)
return;

Intent intent = new Intent (Intent.ACTION_SEND);
intent.setType ("plain/text");
intent.putExtra (Intent.EXTRA_EMAIL, new String[] {"log@mydomain.example"});
intent.putExtra (Intent.EXTRA_SUBJECT, "MyApp log file");
intent.putExtra (Intent.EXTRA_STREAM, Uri.parse ("file://" + fullName));
intent.putExtra (Intent.EXTRA_TEXT, "Log file attached."); // do this so some email clients don't complain about empty body.
startActivity (intent);
}

(3 & 4) Here's what SendLog looks like (you'll have to add the UI, though):

public class SendLog extends Activity implements OnClickListener
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature (Window.FEATURE_NO_TITLE); // make a dialog without a titlebar
setFinishOnTouchOutside (false); // prevent users from dismissing the dialog by tapping outside
setContentView (R.layout.send_log);
}

@Override
public void onClick (View v)
{
// respond to button clicks in your UI
}

private void sendLogFile ()
{
// method as shown above
}

private String extractLogToFile()
{
// method as shown above
}
}

(5) Manifest:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ... >
<!-- needed for Android 4.0.x and eariler -->
<uses-permission android:name="android.permission.READ_LOGS" />

<application ... >
<activity
android:name="com.mydomain.SendLog"
android:theme="@android:style/Theme.Dialog"
android:textAppearance="@android:style/TextAppearance.Large"
android:windowSoftInputMode="stateHidden">
<intent-filter>
<action android:name="com.mydomain.SEND_LOG" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>

(6) Setup Proguard:

In project.properties, change the config line. You must specify "optimize" or Proguard will not remove Log.v() and Log.d() calls.

proguard.config=${sdk.dir}/tools/proguard/proguard-android-optimize.txt:proguard-project.txt

In proguard-project.txt, add the following. This tell Proguard to assume Log.v and Log.d have no side effects (even though they do since they write to the logs) and thus can be removed during optimization:

-assumenosideeffects class android.util.Log {
public static int v(...);
public static int d(...);
}

That's it! If you have any suggestions for improvements to this, please let me know and I may update this.

Android UncaughtExceptionHandler to finish app

At last i resolved this making a class implementing the Thread.UncaughtExceptionHandler interface:

public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {

private BaseActivity activity;
private Thread.UncaughtExceptionHandler defaultUEH;

public MyUncaughtExceptionHandler(BaseActivity activity) {
this.activity = activity;
this.defaultUEH = Thread.getDefaultUncaughtExceptionHandler();
}

public void setActivity(BaseActivity activity) {
this.activity = activity;
}

@Override
public void uncaughtException(Thread thread, Throwable ex) {

//LOGGING CODE
//........

defaultUEH.uncaughtException(thread, ex);

}
}

In the BaseActivity i added the following code:

//exception handling
private static MyUncaughtExceptionHandler _unCaughtExceptionHandler;

@Override
protected void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);

if(_unCaughtExceptionHandler == null)
_unCaughtExceptionHandler = new MyUncaughtExceptionHandler(this);
else
_unCaughtExceptionHandler.setActivity(this);

if(Thread.getDefaultUncaughtExceptionHandler() != _unCaughtExceptionHandler)
Thread.setDefaultUncaughtExceptionHandler(_unCaughtExceptionHandler);
}

I know its the same code i have in the question, but somehow its working now. When i have more free time i will look this deeply to find the root cause and post it

Ideal way to set global uncaught exception Handler in Android

That should be all you need to do. (Make sure you cause the process to halt afterward -- things could be in an uncertain state.)

The first thing to check is whether the Android handler is still getting called. It's possible that your version is being called but failing fatally and the system_server is showing a generic dialog when it sees the process crash.

Add some log messages at the top of your handler to see if it's getting there. Print the result from getDefaultUncaughtExceptionHandler and then throw an uncaught exception to cause a crash. Keep an eye on the logcat output to see what's going on.

How do I handle uncaught exceptions and then delegate handling back to the system?

The uncaught exception can be delegated back to system by storing the old exception handler and passing uncaught exceptions to it.

First create an Application class as below:

public class Controller extends Application {

private static Thread.UncaughtExceptionHandler defaultHandler;

@Override
public void onCreate() {
super.onCreate();
if (defaultHandler == null) {
defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
}
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
FirebaseCrash.report(e); //or whatever
defaultHandler.uncaughtException(t, e); //this will show crash dialog.
}
});
}

}

Then use this class as application in the manifest as:

<application
android:name=".Controller"
... />

Use UncaughtExceptionHandler to do what Android usually does plus extra functionality I need

I had a similar problem when tracking to GoogleAnalytics - most Exceptions got lost when i tried to report them in the default handler. So i cached them and reported them on the next startup.

The second trick (and this hopefully answers your question) to let the Application crash as it should is to store the 'old' DefaultUncaughtExceptionHandler and pass the exception to it.

The onCreate method below is the one of my Application class

@Override
public void onCreate()
{
super.onCreate();
trackCachedException();
setupExceptionHandling();
}

private void setupExceptionHandling()
{
_systemExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.UncaughtExceptionHandler myHandler = new Thread.UncaughtExceptionHandler()
{

@Override
public void uncaughtException(Thread thread, Throwable ex)
{
cacheException(thread.getName(), ex);
if (_systemExceptionHandler != null)
{
_systemExceptionHandler.uncaughtException(thread, ex);
}
}
};
// Make myHandler the new default uncaught exception handler.
Thread.setDefaultUncaughtExceptionHandler(myHandler);
}


Related Topics



Leave a reply



Submit