Catch "Unfortunately 'app' has stopped working" Error - android

I am using a very unstable library on Android that crashes sporadically. I start it using the startActivity() in my code.
The unstable part of the code is doing a lot of video processing and uploading the result to a server. I do not really mind if the activity crashes, but I need to signal the server that it did.
The crash comes from a memory leak (no time to solve it yet). Is there a way I can catch the error a display a more friendly/funny message instead?
try {
context.startActivity(intent);
} catch (ApplicationCrashedException e) {
server.notifyServerOfCrash();
toast("I really disliked your face...");
}
Edit: Here is the Error:
java.lang.OutOfMemoryError
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at java.nio.MemoryBlock.allocate(MemoryBlock.java:125)
at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:72)
at io.cine.android.streaming.FFmpegMuxer.writeSampleData(FFmpegMuxer.java:151)
at io.cine.android.streaming.AndroidEncoder.drainEncoder(AndroidEncoder.java:128)
at io.cine.android.streaming.TextureMovieEncoder.handleFrameAvailable(TextureMovieEncoder.java:264)
at io.cine.android.streaming.TextureMovieEncoder$EncoderHandler.handleMessage(TextureMovieEncoder.java:409)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at io.cine.android.streaming.TextureMovieEncoder.run(TextureMovieEncoder.java:219)
at java.lang.Thread.run(Thread.java:841)
For some reason once the BroadcastActivity runs out of memory, the Activity gets killed and comes back to the previous one. It then displays the 'app' has stopped working Dialog. If I press OK, it kills the entire application and comes back to the home screen.
Ideally I would just have it come back to the previous activity notifying the server silently. (Not killing the application)

Is there a way I can catch the error a display a more friendly/funny message instead?
If the app is leaking so much memory that it crashes, you cannot easily catch this.
Why you can't always catch this
When you start running out of memory, exceptions are thrown from any part of your app's code that tries to allocate more memory than is available. So it isn't just your library that will throw the exceptions.
How you could detect this
Catch the OutOfMemoryError and make an effort to tell user/server
Your results will vary, but you could wrap the Thread's run() method with a try catch pair and try to catch the out of memory error. You will still be out of memory and any thing you do in the catch code might fail because of it. Still it might work if the process frequently allocates large chunks of memory which would mean there is still a little bit left.
Example:
public void run() {
try {
//video process code
} catch (OutOfMemoryError error) {
//we are still out of memory so these operations might fail
tellServerWeFailed();
triggeruserDialog();
}
}
onTrimMemory() override
You could also try stopping your video tasks when onTrimMemory() is called. Your mileage may vary, I've never been able to get it to work consistently across different Android devices.
Error Reporting Framework
One solution that is pretty involved is to use a third party error reporting framework as they will often let you show your user custom crash messages.
Check out http://www.acra.ch/ and specifically the documentation on User Messages
Started Service
I do not really mind if the activity crashes, but I need to signal the server that it did.
Great! What you might try is to use a started service that android automatically relaunches after the crash. The details on how to tell your server are up to you. You might find that you will need to save some token or other information so that you can tell your server which session has ended so you might, for example, always keep that information saved in Preference entry and read it out after the crash.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.v(LOG_TAG, "Received start id " + startId + ": " + intent);
if (intent == null) {
Log.w(LOG_TAG, "Service was stopped and automatically restarted by the system. ");
//Tell your server about the crash.
stopSelf();
}
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
See http://developer.android.com/reference/android/app/Service.html#ServiceLifecycle
More about running out of memory on Android
To maintain a functional multi-tasking environment, Android sets a hard limit on the heap size for each app. The exact heap size limit varies between devices based on how much RAM the device has available overall. If your app has reached the heap capacity and tries to allocate more memory, it will receive an OutOfMemoryError.
In some cases, you might want to query the system to determine exactly how much heap space you have available on the current device—for example, to determine how much data is safe to keep in a cache. You can query the system for this figure by calling getMemoryClass(). This returns an integer indicating the number of megabytes available for your app's heap. This is discussed further below, under Check how much memory you should use. (source)
Some key points:
Your entire app shares the system-assigned memory heap.
Once you've leaked memory, it is gone until the application is restarted.
You won't be able to segregate memory leaked in one activity by leaving that activity.

If you are looking for the type of Exception to catch, just repeat the steps you took to make your app crash and take a look at the logcat. You should see a message like "SomeTypeOfException thrown" catch that.
Edit: otherwise use the generic Exception

Related

Android lag after OOM crash

I'm currently serving a kiosk application on a dedicated tablet.
Most of them works well, but in some uncertain cases, an OOM crashed the app.
I use the below library to revive my app with the code inside my Application class.
com.jakewharton:process-phoenix:2.0.0
this.uncaughtException = Thread.UncaughtExceptionHandler { _, e ->
ErrorLogger.saveError(e)
try {
System.gc()
ProcessPhoenix.triggerRebirth(this)
} catch (e: java.lang.Exception) {
ErrorController.showError(e)
} finally {
Runtime.getRuntime().exit(0)
}
}
Thread.setDefaultUncaughtExceptionHandler(uncaughtException)
The app revives but after it, severe lags occur.
When the app starts fresh within a rebooted tablet, it takes the app about three seconds after launch to load everything from the server and internal database. But, after when it launches after OOM, it takes about five minutes to load, as if it's suffering from low memory. AlarmManagers stop working after an ANR of the AlarmManager.
Is there a way to clean the memory or a more fresh way to restart my app?
Yeah- fix your OOM crash. Use less memory. Any kind of library like the one above is a bad idea- especially since you're doing it on any uncaught exception and not just OOM, meaning you could be in any kind of bad state. Just blindly trying to restart your app on any exception is a horrible idea.
In addition, there's no way to fix the slow down. The lag is because it now needs to free and reallocate almost every object in your app. That's going to be slow. Just don't do this.

UI blocking loops behaviours differ( Oreo vs Mashmallow)

I have a small Android application which does a server call to post some User data to a server.
Following is the code :
private boolean completed = false;
public String postData( Data data){
new Thread(new Runnable() {
#Override
public void run() {
try{
String response = callApi(data);
completed = true;
}catch(Exception e){
Log.e("API Error",e.getMessage());
completed = true;
return;
}
}
}).start();
while(!completed){
// Log.i("Inside loop","yes");
}
return response.toString();
}
The above method calls the API to post data and returns the response received which works fine.
The loop at the bottom is a UI blocking loop which blocks the UI until a response is received or an error.
The problem :
I tried the same code for Marshmallow and Oreo device and the results were different.
For Marshmallow : Things moved in line with my expectation. :)
For Oreo (8.1.0) :
The very first API call works good enough after I open the App. However, the subsequent API calls after, cause the UI to block forever although an Error or Response is received from the Server(verified by logging and debugging).
However, on setting breakpoints(running in Debug mode) the App moves with much less trouble.
It seems the system is unable to exit the UI blocking loop although the condition is met.
The second behavior which was noticed is when I log a message in the UI blocking thread, the System is able to exit the loop and return from the Method though the API response is not logged.
Could someone help understand such inconsistency across these two flavors of Android and what could be the change introduced causing such a behavior for Oreo but not for Marshmallow?
Any insight would be extremely helpful.
It's more likely to be differences in the processor cache implementation in the two different hardware devices you're using. Probably not the JVM at all.
Memory consistency is a pretty complicated topic, I recommend checking out a tutorial like this for a more in-depth treatment. Also see this java memory model explainer for details on the guarantees that the JVM will provide, irrespective of your hardware.
I'll explain a hypothetical scenario in which the behavior you've observed could happen, without knowing the specific details of your chipset:
HYPOTHETICAL SCENARIO
Two threads: Your "UI thread" (let's say it's running on core 1), and the "background thread" (core 2). Your variable, completed, is assigned a single, fixed memory location at compile time (assume that we have dereferenced this, etc., and we've established what that location is). completed is represented by a single byte, initial value of "0".
The UI thread, on core 1, quickly reaches the busy-wait loop. The first time it tries to read completed, there is a "cache miss". Thus the request goes through the cache, and reads completed (along with the other 31 bytes in the cache line) out of main memory. Now that the cache line is in core 1's L1 cache, it reads the value, and it finds that it is "0". (Cores are not connected directly to main memory; they can only access it via their cache.) So the busy-wait continues; core 1 requests the same memory location, completed, again and again, but instead of a cache miss, L1 is now able to satisfy each request, and need no longer communicate with main memory.
Meanwhile, on core 2, the background thread is working to complete the API call. Eventually it finishes, and attempts to write a "1" to that same memory location, completed. Again, there is a cache miss, and the same sort of thing happens. Core 2 writes a "1" into appropriate location in its own L1 cache. But that cache line doesn't necessarily get written back to main memory yet. Even if it did, core 1 isn't referencing main memory anyway, so it wouldn't see the change. Core 2 then completes the thread, returns, and goes off to do work someplace else.
(By the time core 2 is assigned to a different process, its cache has probably been synchronized to main memory, and flushed. So, the "1" does make it back to main memory. Not that that makes any difference to core 1, which continues to run exclusively from its L1 cache.)
And things continue in this way, until something happens to suggest to core 1's cache that it is dirty, and it needs to refresh. As I mentioned in the comments, this could be a fence occurring as part of a System.out.println() call, debugger entry, etc. Naturally, if you had used a synchronized block, the compiler would've placed a fence in your own code.
TAKEAWAYS
...and that's why you always protect accesses to shared variables with a synchronized block! (So you don't have to spend days reading processor manuals, trying to understand the details of the memory model on the particular hardware you are using, just to share a byte of information between two threads.) A volatile keyword will also solve the problem, but see some of the links in the Jenkov article for scenarios in which this is insufficient.

Deal with Firebase crash reporting and custom Application class with custom UncaughtExceptionHandler

My Android app currently uses a custom UncaughtExceptionHandler that aims to capture any crash, and schedules an app restart for several seconds in the future with AlarmManager before manually calling Process.killProcess(Process.myPid()) to avoid Android's Force Close popup as in my app's use case, the user will not be able to interact with the device to tap "ok" on the FC dialog and restart the app.
Now, I'd like to integrate with Firebase Crash reports, but I fear wrong behaviors, so here are my questions:
How should I make my code so my custom UncaughtExceptionHandler passes the exception to Firebase Crash Report before killing it's process? Would calling Thread.getDefaultUncaughtExceptionHandler() give me the Firebase Crash report UncaughtExceptionHandler so I can just call uncaughtException(...) on it?
May Process.killProcess(Process.myPid()) prevent Firebase Crash reporting library to do it's reporting work? Would Firebase have started it's crash report in a separated process before it's uncaughtException(...) returns? Does Firebase own UncaughtExceptionHandler calls back to Android default's UncaughtExceptionHandler, showing the FC dialog?
May Process.killProcess(Process.myPid()) kill Firebase Crash Reporting process in addition to the default process?
How can my custom Application class detect if it is instantiated in Firebase Crash Reporting process? Treating both processes the same way would probably lead to inconsistent states.
Thanks to anyone that tries to help me!
If you kill the process in your exception handler, you will not be able to receive crashes. It will interfere with the ability to persist the crash for either immediate or delayed transmission. It will possibly interfere with any library that has registered uncaught exception handlers that behave well.
In fact, Process.killProcess(Process.myPid()) is very much an anti-pattern for Android development. Android apps should not be concerned at all with the overall lifecycle if the process that hosts the app. The fact that Android manages the process for you is an optimization designed for the benefit of the users.
I strongly recommend, for uncaught exceptions in your app, to simply let the app die as it normally would. Masking the proper effect of the crash is like sweeping dirt under a rug. You might be resolving a short term problem, but what really needs to happen is the normal logging and handling of the error so you can fix it.
Don't depend on the fact that Firebase Crash Reporting transmits exceptions in another process. That other process will be removed in the full non-beta release.
The best situation for your Application subclass is to not depend at all which process it's operating. In fact, the Android team at Google does not recommend use of Application subclasses at all since it only leads to trouble for multi-process apps. If you must use an Application subclass, it should expect to run within multiple processes.
After some testing, I finally found a way to both ensure my app restarts properly after an UncaughtException.
I attempted three different approaches, but only the first, which is my original code, with just a little tweak to pass the uncaught Throwable to `FirebaseCrash, and ensure it is considered as a FATAL error.
The code that works:
final UncaughtExceptionHandler crashShield = new UncaughtExceptionHandler() {
private static final int RESTART_APP_REQUEST = 2;
#Override
public void uncaughtException(Thread thread, Throwable ex) {
if (BuildConfig.DEBUG) ex.printStackTrace();
reportFatalCrash(ex);
restartApp(MyApp.this, 5000L);
}
private void reportFatalCrash(Throwable exception) {
FirebaseApp firebaseApp = FirebaseApp.getInstance();
if (firebaseApp != null) {
try {
FirebaseCrash.getInstance(firebaseApp)
.zzg(exception); // Reports the exception as fatal.
} catch (com.google.firebase.crash.internal.zzb zzb) {
Timber.wtf(zzb, "Internal firebase crash reporting error");
} catch (Throwable t) {
Timber.wtf(t, "Unknown error during firebase crash reporting");
}
} else Timber.wtf("no FirebaseApp!!");
}
/**
* Schedules an app restart with {#link AlarmManager} and exit the process.
* #param restartDelay in milliseconds. Min 3s to let the user got in settings force
* close the app manually if needed.
*/
private void restartApp(Context context, #IntRange(from = 3000) long restartDelay) {
Intent restartReceiver = new Intent(context, StartReceiver_.class)
.setAction(StartReceiver.ACTION_RESTART_AFTER_CRASH);
PendingIntent restartApp = PendingIntent.getBroadcast(
context,
RESTART_APP_REQUEST,
restartReceiver,
PendingIntent.FLAG_ONE_SHOT
);
final long now = SystemClock.elapsedRealtime();
// Line below schedules an app restart 5s from now.
mAlarmManager.set(ELAPSED_REALTIME_WAKEUP, now + restartDelay, restartApp);
Timber.i("just requested app restart, killing process");
System.exit(2);
}
};
Thread.setDefaultUncaughtExceptionHandler(crashShield);
Explanation of why and unsuccessful attempts
It's weird that the hypothetically named reportFatal(Throwable ex) method from FirebaseCrash class has it's name proguarded while being still (and thankfully) public, giving it the following signature: zzg(Throwable ex).
This method should stay public, but not being obfuscated IMHO.
To ensure my app works properly with multi-process introduced by Firebase Crash Report library, I had to move code away from the application class (which was a great thing) and put it in lazily loaded singletons instead, following Doug Stevenson's advice, and it is now multi-process ready.
You can see that nowhere in my code, I called/delegated to the default UncaughtExceptionHandler, which would be Firebase Crash Reporting one here. I didn't do so because it always calls the default one, which is Android's one, which has the following issue:
All code written after the line where I pass the exception to Android's default UncaughtExceptionHandler will never be executed, because the call is blocking, and process termination is the only thing that can happen after, except already running threads.
The only way to let the app die and restart is by killing the process programmatically with System.exit(int whatever) or Process.kill(Process.myPid()) after having scheduled a restart with AlarmManager in the very near future.
Given this, I started a new Thread before calling the default UncaughtExceptionHandler, which would kill the running process after Firebase Crash Reporting library would have got the exception but before the scheduled restart fires (requires magic numbers). It worked on the first time, removing the Force Close dialog when the background thread killed the process, and then, the AlarmManager waked up my app, letting it know that it crashed and has a chance to restart.
The problem is that the second time didn't worked for some obscure and absolutely undocumented reasons. The app would never restart even though the code that schedules a restart calling the AlarmManager was properly run.
Also, the Force Close popup would never show up. After seeing that whether Firebase Crash reporting was included (thus automatically enabled) or not didn't change anything about this behavior, it was tied to Android (I tested on a Kyocera KC-S701 running Android 4.4.2).
So I finally searched what Firebase own UncaughtExceptionHandler called to report the throwable and saw that I could call the code myself and manage myself how my app behaves on an uncaught Throwable.
How Firebase could improve such scenarios
Making the hypothetically named reportFatal(Throwable ex) method non name-obfuscated and documented, or letting us decide what happens after Firebase catches the Throwable in it's UncaughtExceptionHandler instead of delegating inflexibly to the dumb Android's default UncaughtExceptionHandler would help a lot.
It would allow developers which develop critical apps that run on Android to ensure their app keep running if the user is not able to it (think about medical monitoring apps, monitoring apps, etc).
It would also allow developers to launch a custom activity to ask users to explain how it occurred, with the ability to post screenshots, etc.
BTW, my app is meant to monitor humans well-being in critical situations, hence cannot tolerate to stop running. All exceptions must be recovered to ensure user safety.

How to force Android app to deallocate all memory?

I have an annoying problem:
I'm fetching a lot of GeoJSON data from a server. This works, even with 16MB heap and even if I start and stop the app several times. The memory consumption stays constant and never exceeds. But there is a case where I exceed the 16MB heap. I'd like to describe it shortly:
The app is used and "quit" by home button, so the app resides in the background and is not destroyed yet. When the app is resumed, my "controller" which is a part of my app, checks for new GeoJSON data. If there is a GeoJSON data update, the app downloads and processes it and here the problem begins. If the app was already started before and is resumed from background the heap size of 16MB is not enough for the following code (if and only if the app is resumed from background instead of a fresh start):
private synchronized String readUrlData(HttpURLConnection urlConnection) throws IOException {
Log.i(TAG, "Start reading data from URL");
urlConnection.setReadTimeout(1000 * 45); //45 sec
Reader reader = new InputStreamReader(urlConnection.getInputStream());
StringBuilder sb = new StringBuilder(1024 * 16);
char[] chars = new char[1024 * 16]; //16k
int len;
while((len = reader.read(chars)) >= 0) {
sb.append(chars, 0, len);
}
reader.close();
Log.i(TAG, "Finished reading data from URL");
return sb.toString();
}
I get OutOfMemory either in append() or in toString(). Obviously the app takes little to much memory for this when it's somehow used before. I already tried to find a more resource friendly way for the code above but there is no solution. Again, if the app is started from new, there are never problems. And I'm absolutely sure that I don't have any memory leaks because
I checked this part and more with MAT and there was never more than 1.6MB occupied (this is the GeoJSON data).
I performed this use case several times consecutively with 24MB heap size.
If there would be a memory leak, it would have been crashed after the 3rd ot 4th time with 24MB heap, but it ran without problems.
I know how to avoid the crash. I could show an AlertDialog to the user which tells him that there is new GeoJSON data available and he needs to restart the app. But there is a catch. If the application is "terminated" by Activity's finish(), the application still remains in memory so when it restarts, the crash comes again because the memory is never deallocated (at least I can't rely on it in most cases). I already figured out that System.exit(0); instead of finish would free all memory because it kills the whole app, so no crash occures after restart with the new GeoJSON data. But I know this is no good solution. I already tried System.gc() on important parts but this doesn't work either. Any ideas how to deal with this problem? Probably I need something like restarting the app with deallocing all used memory.
Another solution could be to redesign the code above but I don't think that it's possible to get more MBs out of this.
If I don't find a reasonable solution for this, I will use System.exit(0) when heap is 16MB (I think there is a way to check that) to restart the app.
here are some ideas :
as soon as you know there is anything to read , set the old data to null so that it will be GC-ed .
use a service that does this task , that will run on a different process (thus having at least 16 mb just for this task) . once it handles the data , move it in some way to whatever component that you need , while null-ing the old data before.
decode the data as you get it , instead of getting all of it and then decode it .
compress the data so that the while decoding it , it will decompress it on the way .
use an alternative to json , such as google's protocol buffers or your own customized data type .
most of the devices out there have more than 16 mb in their heap memory . you could simply set the min sdk version to 8 or 10 since most devices that have more memory than this also have a higher API .
Thanks for your answers. At the end I decided to let System.exit(0) in this one special case because the data is hardly ever updated and even if, it must be coincidence that it happens exactly when someone has the app running in background.

App dies with "Sending signal." but no exception or other info

I'm working on an app that is recording data via Bluetooth, but it intermittently crashes after hours of collecting data (making it hard to track down the bug).
The logcat output isn't very helpful:
http://i.imgur.com/EalnX.png
There are no exceptions thrown and no clues for what caused the process to be terminated.
How can I figure out what went wrong? Is there an exception being thrown that isn't being shown by logcat? How can I track this bug down?
Signal 9 is SIGKILL, which will terminate a process immediately (no handlers inside the process will run). From the log line, the process is killing itself, so its not an external agent that is issuing the SIGKILL.
My guess (and its really a guess) is that the memory management code running inside your process (as part of the infrastructure, not code that you wrote) is deciding that you've exhausted some resource and the only recourse is to die. I would expect there to be more messages before this point is reached in the log, so it may be worth browsing the log history to see if there are useful warnings from the process before this point.
The line immediately before this is a GC log, which implies that some sort of memory resource is running low. But it looks like the heaps are not full, so failing allocations seems unlikely. You can still get allocation failures if the object being allocated was too large to fit on the heap, or fragmentation prevented it from being allocated. I'd expect to see more relevant log messages in this case, though.
I think capturing more of the log (perhaps filtering it by your app's PID if necessary) will help you make progress.
In my case there was no warnings or any clues in the log.
Eventually I found that my problem was that one of the activities I was going into (lets say Activity X) was registering to a broadcast receiver but never unregistered from it.
Therefor by closing the activity (Activity X) and coming back to it caused registering Again to the same broadcast receiver - which caused the mess!
Simply adding unregisterReceiver(mybroadcast); (in Activity X) solved it.
(I added mine to onDestroy. make sure you unregister in the right location).
And if you are super desperate I recommend seeing this slide share which explains Android crash debugging your errors.
this problem happens when using RXjava and not implement the onError callback method

Categories

Resources