Android - Crashlytics, run code during crash - android

I had a bad crash case that was caused due to some Asyncs doing stuff in improper order in a SQLite and thing blew up. It took me some time to debug all that and access to the internal db would have helped immensely. I know how to access that internal db on a dev device but in case something goes wrong I would like to be able to get an instance of that db no matter the device. For error reporting I am using Crashlytics.
The question is: Is there a way to have Crashlytics run a piece of code (method, etc) during the crash collection/reporting? (For example, get db copy and email it, or something)
Couldn't find something in the documentation.

It is possible to get control prior to Crashlytics logging a crash. You essentially have to create your own uncaught exception handler and call Crashlytics' handler from there. Something like this in your Application class:
private UncaughtExceptionHandler originalUncaughtHandler;
#Override
public void onCreate() {
// initialize Fabric with Crashlytics
originalUncaughtHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
// do the rest of your oncreate stuff
}
#Override
public void uncaughtException(Thread thread, Throwable ex) {
// do your work to add data to Crashlytics log
originalUncaughtHandler.uncaughtException(thread, ex);
}

No you can't. You can however set certain values before initiating Crashlytics. Like adding values to parameters so as to identify user. Like adding email id of user before creating a crashlytics session.

As #basu-singh said, you can add context to the crash, see https://docs.fabric.io/android/crashlytics/enhanced-reports.html
Or you can use your own UncaughtExceptionHandler, and then call Crashlytics. Though your code needs to be extra safe !

Related

What exactly does the Timber library do?

I heard about Timber and was reading github README, but it's quietly confusing me.
Behavior is added through Tree instances. You can install an instance
by calling Timber.plant. Installation of Trees should be done as early
as possible. The onCreate of your application is the most logical
choice.
What behavior?
This is a logger with a small, extensible API which provides utility
on top of Android's normal Log class.
What more does it provide on top of Android's Log?
The DebugTree implementation will automatically figure out from which
class it's being called and use that class name as its tag. Since the
tags vary, it works really well when coupled with a log reader like
Pidcat.
What is DebugTree?
There are no Tree implementations installed by default because every
time you log in production, a puppy dies.
Again, what is a tree implementation? What does it do? And how do I stop killing puppies?
Two easy steps:
Install any Tree instances you want in the onCreate of your
application class.
Call Timber's static methods everywhere throughout your app.
Two easy steps for accomplishing what?
None of this has been explained in the Readme. It's pretty much a description for someone who already knows what it is :/
Problem :-
We do not want to print logs in Signed application as we may sometimes log sensible information . Generally to overcome this developers tend to write if condition before writing log
Example:-
if(BuildConfig.DEBUG) {
Log.d(TAG,userName);
}
so every time you want to print a log you need to write a if condition and a TAG which most times will be class name
Timber tackels these two problems
You just need to check condition once in application class and initialize Timber.plant
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
Timber.plant(DebugTree())
}
}
}
remaining all places we can just write Timber.d("Message") without any tag or if condition .
If you want a different tag then you can use
Timber.tag("Tag").d("message");
Edit :
Also you can plant your own trees in Timber , and do some cool stuff like Log only warnings and error in release , send warnings and error logs to server etc . eg
import timber.log.Timber;
public class ReleaseTree extends Timber.Tree {
#Override
protected void log(int priority, String tag, String message, Throwable t) {
if (priority == ERROR || priority == WARNING){
//Send to server
}
}
}
You can plant different trees for different build flavours .
Check out this article and have a listen to this podcast

Rising error in Robolectric test when you have try-catch in your application code

In my android application I have written try-catch in every event method. So when an exception occurs, the catch gets the exception and a method shows a message box containing the exception details and I can handle and find my application's bugs.
For example:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
}
catch (Exception e) {
MessageBox.showException(this, e);
}
}
Now in Robolectric which there is no device to show the ui results, I cannot find out if an exception occurred. Now I want to do something when my code went to catch part or when MessageBox.showException is called, the test fails.
How can I do that?
The only way I can think of solving this is for you to inject the component that handles the errors into the classes that use it and after that, load a customized one for your tests.
There are several ways to achieve this and probably some better than what I will suggest, but I will try and present the option that requires minimum changes to what I think is your current architecture.
1 - Whatever you use for showing exceptions, instantiate this in your Application class and keep it there. Or at least provide it from your application class, so now whenever you need to use MessageBox, instead of a static method, you fetch it from the Application first. For example:
((MyApplication)getApplication()).getMessageBox().showException(this,e)
2 - Create a TestMessageBox and a TestApplication (that extends your normal Application class). In your TestApplication, override getMessageBox() to return the TestMessageBox instead of the normal MessageBox. In your TestMessageBox do whatever you want to be able to observe the errors in your tests.
3 - In your tests, use the TestApplication. When you run tests, Robolectric will load this instead of the normal application so your tests will now use your TestMessageBox and you can capture the info you need.
#Config(application = TestApplication.class)

How to handle errors in custom AsyncTaskLoader?

I am extending AsyncTaskLoader which I use later in a Fragment through LoaderManager. Is there suggested way to handle errors which occur during async task? I would like to popup message asking user to cancel or retry when internet connection error occurs.
Only way which I can think of now is to make Loader to return null on error or wrap loaded object into another which would have status code.
What we did in our last project: Create a Wrapper that holds a generic and an excpetion like this:
public class AsyncTaskResult<T> {
private final T result;
private final Exception error;
//getter & setter
}
Catch all Exceptions in your doInBackground(...) and pack them into the result wrapper (or the result if no error). In your UI check the wrapper if it's an exception, then show according error message, otherwise populate the fields with the result.
For us it was also good practice to define what unique types of exceptions, there are (e.g. exception with a recoverable error where you only show a dialog or an app failure where you need to kick the user to the main menu) and only throw these kinds (on catching the specific in your asynctask), so you don't have to bother with hundreds of different exceptions and also abstract your error handling. You could also provide String keys with the correct I18n error message so you only have to write e.getMessage()
I've seen good results when returning a composite object that contains the payload (if any) and a status code, as you suggested. Then the Fragment that's hosting the AsyncTaskLoader can display an appropriate and informative error. This approach has the added advantage that it uses the built-in loader lifecycle.
Another option is to register a listener that your AsyncTaskLoader will notify when errors occur. The Facebook SDK has an example of using error listeners with loaders.

net.sqlcipher.database.SQLiteException while opening database after resuming app

I'm getting the following error when using the encrypted SQLCipher database in my Android app, but only off and on:
net.sqlcipher.database.SQLiteException: not an error
at net.sqlcipher.database.SQLiteDatabase.dbopen(Native Method)
at net.sqlcipher.database.SQLiteDatabase.<init>(SQLiteDatabase.java:1950)
at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:900)
at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:947)
at net.sqlcipher.database.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:195)
at com.android.storage.DatabaseHelper.getReadable(DatabaseHelper.java:99)
...
I've got the proper files in the assets/ and libs/ folders because the database works fine most of the time. However, every once in awhile I'll see this error. I've seen this twice now on my phone and it's always been after resuming the app after hours of inactivity (I check for user's oauth token in db if it gets cleared from memory).
I call "SQLiteDatabase.loadLibs(this)" only from the Application::onCreate() method so my hunch is that this isn't getting called on a resume and is throwing the error. Does this sound possible? If so, where should I call loadLibs? A user could enter the app in any activity and I access the db if the token isn't in memory. I see my options as either calling loadLibs on each Activity::onCreate or calling it each time I attempt to open the db. Would it cause any harm or performance issues if I called it multiple times like this?
You might consider moving the SQLiteDatabase.loadLibs(this); to your application subclass of net.sqlcipher.database.SQLiteOpenHelper. You can then pass the static instance of your Application subclass as its argument. Something like the following might be an example:
public class SchemaManager extends net.sqlcipher.database.SQLiteOpenHelper {
private static SchemaManager instance;
public static synchronized SchemaManager getInstance() {
if(instance == null) {
SQLiteDatabase.loadLibs(YourApplication.getInstance());
instance = new SchemaManager(…)
}
return instance;
}
}
With regard to the exception that was provided, the Java routine calls into a JNI layer that calls sqlite3_open_v2, setting the soft heap limit and setting the busy timeout. I would suggest adding logging locally to verify you are passing a valid path and a non null passphrase when attempting to acquire the SQLiteDatabase instance when you get a crash. Calling SQLiteDatabase.loadLibs(this); multiple times shouldn't cause a noticeable performance impact, much of what occurs are calls to System.loadLibrary(…) which get mapped into Runtime.getRuntime().loadLibrary(…), once a dynamic library has been loaded, subsequent calls are ignored.

Android - Testing a simple Activity method

i'm writing you today because i have a real and concrete question about how testing an activity method. Let me explain the situation.
I have an activity (A) which use a method save(param) from a service (S). This method throws a specific exception if the parameter is invalid.
Unit tests are written for this part, and now, i would like to test the Activity part :
In my Activty, i use the following code to call the save() method
public void OnSaveClicked()
{
try{
if ( S.save(my_object) > 0 ) // Object Saved
{
ShowToast(this, "Your object has been saved successfully !");
}
else { // Error occured with the database
ShowToast(this, "An error occured with the database");
}
catch (MyException ex)
{
ShowToast(this, "The object you are trying to save is not valid... Please check information you entered");
}
}
The test that i'm trying to write has to check if the save() method raised the exception or not. To do that, i simulate a click on the UI Button, but, i don't really know what do i have to check. I read that's not possible to write an assert on a toast message. I'm currently thinking about using a dialog but i am not sure that the way i'm thinking is the right one. Is it normal to test that or not ?
Would you give me your feedback about that, and discuss about the right way to test activity ?
Thanks,
t00f
You might want to look at Robolectric. They have support for assertions like this http://groups.google.com/group/robolectric/browse_thread/thread/87952e620ce1bb37?pli=1
Thanks,
Ian

Categories

Resources