I have an Android app written in Java, and recently noticed many crash reports occurring in an AsyncTask. The stack trace makes no sense, as it is inside a try/catch so there is no way for the exception to be thrown. How can a caught exception cause an app crash?
13: public class HttpGoogleSignInAction...
#Override
protected String doInBackground(Void... voids) {
try {
// Check if user exists
UserConfig user = null;
31: user = MainActivity.connection.fetch(this.config);
if (user == null) {
} else {
// User exists, try to sign in user
this.config = MainActivity.connection.connect(this.config);
}
} catch (Exception userDoesNotExist) {
try {
// User does not exist, try to create user
this.config = MainActivity.connection.create(this.config);
} catch (Exception exception) {
this.exception = exception;
}
}
return "";
}
public String POST(String url, String xml) {
if (this.debug) {
System.out.println("POST: " + url);
System.out.println("XML: " + xml);
}
String result = "";
try {
HttpClient httpClient = new DefaultHttpClient();
HttpContext localContext = new BasicHttpContext();
HttpPost httpPost = new HttpPost(url);
StringEntity content = new StringEntity(xml, "utf-8");
content.setContentType("application/xml");
httpPost.setEntity(content);
HttpResponse response = httpClient.execute(httpPost, localContext);
HttpEntity entity = response.getEntity();
if (entity != null) {
result = EntityUtils.toString(entity, HTTP.UTF_8);
}
if ((response.getStatusLine().getStatusCode() != 200) && (response.getStatusLine().getStatusCode() != 204)) {
this.exception = new SDKException(""
+ response.getStatusLine().getStatusCode()
+ " : " + result);
2205: throw this.exception;
}
} catch (Exception exception) {
if (this.debug) {
exception.printStackTrace();
}
this.exception = new SDKException(exception);
2213: throw this.exception;
}
return result;
}
The stack trace shows the crash occurring in the line,
user = MainActivity.connection.fetch(this.config);
It is a normal RuntimeException subclass exception being thrown, which should be caught. If I try to force an exception to test it the try/catch works fine and no crash, but in 1% of users using the app I am seeing crashes, others it works fine.
Makes no sense.
This is the stack trace from Google Play, SDKException is a subclass of RuntimeException, the line of code it occurs in is inside try/catch
org.botlibre.sdk.SDKException:
at org.botlibre.sdk.SDKConnection.POST (SDKConnection.java:2213)
at org.botlibre.sdk.SDKConnection.fetch (SDKConnection.java:259)
at ...HttpGoogleSignInAction.doInBackground (HttpGoogleSignInAction.java:31)
at ...HttpGoogleSignInAction.doInBackground (HttpGoogleSignInAction.java:13)
at android.os.AsyncTask$3.call (AsyncTask.java:394)
at java.util.concurrent.FutureTask.run (FutureTask.java:266)
at android.os.AsyncTask$SerialExecutor$1.run (AsyncTask.java:305)
at java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:641)
at java.lang.Thread.run (Thread.java:923)
Caused by: org.botlibre.sdk.SDKException:
at org.botlibre.sdk.SDKConnection.POST (SDKConnection.java:2205)
org.botlibre.sdk.SDKException:
at org.botlibre.sdk.SDKConnection.POST (SDKConnection.java:2213)
at org.botlibre.sdk.SDKConnection.fetch (SDKConnection.java:259)
Shows that you're throwing the exception captured here in doInBackground():
try {
// User does not exist, try to create user
this.config = MainActivity.connection.create(this.config);
} catch (Exception exception) {
**this.exception = exception;**
}
}
Even though this execption is within a catch block, since it's persisted and then thrown later it is now outside of a try-catch block and can crash the app. That is
try {
...
} catch (Exception exception) {
if (this.debug) {
exception.printStackTrace();
}
this.exception = new SDKException(exception);
// This line will throw an execption that will have the message from the original exception.
2213: throw this.exception;
}
The source for SDKException's single exception constructor shows it reuses the message from the exception parameter.
You are very clearly re-throwing the caught exception on line 2213. If you throw an exception from a catch block, it escapes the try/catch because you are throwing it from a scope of code that is not surrounded by try { }.
As for why fetch() might be throwing, we would have to see the code to be able to guess.
Even though you have your code wrapped in try catch, there is a possibility of a crash if different threads are involved. try catch does not catch exceptions from child threads, so if any of your methods(like .fetch or .connect) used in doInBackground are internally using/creating different threads and an exception is thrown within that thread it might cause the app to crash. Looking at the logs, it is possible the .fetch method is asynchronous and performing operations in a different thread.
Related
void connectOverNetwork() throws Exception {
try {
final JSONObject response = make network call;
if (!response.getBoolean(SUCCESS)) {
LOG.error("--- foo message ---");
throw new Exception("message replied with error");
}
} catch (final Exception e) {
LOG.error("---- bar message ---");
throw new SvcException("failed to connect over network");
}
}
In the code above, I am throwing an exception with a failure message.
At the same time, I am also throwing an error on failure to connect over the network.
But, if I throw an exception for !success it gets caught again, causing duplicate logging. I do not want to print bar message, if I only want to log foo message.
How to prevent it from happening?
Validate the response after the try-catch statement.
JSONObject response = null;
try {
response = /* make network call */;
} catch (final Exception e) {
LOG.error("---- bar message ---");
throw new SvcException("failed to connect over network");
}
if (!response.getBoolean(SUCCESS)) {
LOG.error("--- foo message ---");
throw new Exception("message replied with error");
}
I don't recommend catching Exception - it's too general, I suggest you narrow it down to a more specific exception type.
What if you move it outside the try block.. Anyway, the reason for the first try..catch is to catch any Exception from the network call.
JSONObject response = null;
try {
response = make network call;
} catch (final Exception e) {
LOG.error("---- bar message ---");
throw new SvcException("failed to connect over network");
}
if (!response.getBoolean(SUCCESS)) {
LOG.error("--- foo message ---");
throw new Exception("message replied with error");
}
First, let me point out a mistake in your code. Your method declares that it throws Exception, but it doesn't. It throws SvcException. So that's what the "throws" clause should say. (You should never say "throws Exception" anyway. You should state explicitly what kind of exception it throws.) The rest of the answer depends on whether or not your vague description "make network call" throws an exception or not.
If it doesn't, your method should look like this:
void connectOverNetwork() throws SvcException {
final JSONObject response = makeNetworkCall();
if (!response.getBoolean(SUCCESS)) {
LOG.error("--- foo message ---");
throw new SvcException("message replied with error");
}
}
But that's unrealistic. Chances are, your "make network call" code throws something like IOException. In this case your code should look like this:
void connectOverNetwork() throws SvcException {
try {
final JSONObject response = makeNetworkCall(); // throws IOException
if (!response.getBoolean(SUCCESS)) {
LOG.error("--- foo message ---");
throw new SvcException("message replied with error");
}
} catch (final IOException e) {
LOG.error("--- foo message ---");
throw new SvcException("failed to connect", e); // wrap e inside SvcException
}
}
Notice that I wrap the caught IOException inside the SvcException. If your SvcException doesn't do that, either rewrite it so that it can, or call its initCause() method before throwing it. You should always include the original exception when reThrowing a different exception.
Also notice that I don't bother throwing, then catching and rethrowing an IOException. When I detect failure. I just throw the exception that I need to throw. This means I need to log the foo message in two separate places. For most things, repeating a line of code should be avoided, but for logging, this is fine.
But this code is a bit messy. I would clean it up by separating the test for success from the possible IOException. So I would write it like this:
void connectOverNetwork() throws SvcException {
JSONObject response; // no need to initialize this.
try {
response = makeNetworkCall(); // throws IOException
} catch (final IOException e) {
LOG.error("--- foo message ---");
throw new SvcException("failed to connect", e); // wrap e inside SvcException
}
if (!response.getBoolean(SUCCESS)) {
LOG.error("--- foo message ---");
throw new SvcException("message replied with error");
}
}
Notice here that response is declared before the try loop. It's not initialized because there's no way it will reach the !response.getBoolean(SUCCESS) test without a value. If makeNetworkCall() throws an exception, it won't even reach that line.
Create your own exception type and don't catch it.
try {
do stuff
if (condition)
throw new MyCustomException("error")
} catch (IOException e) {
log and rethrow
}
I am using SonarQube for code quality. I got one issue related to exception handling, which says remove throw clause from finally block.
} catch(Exception e) {
throw new MyException("request failed : ", e);
} finally {
try {
httpClient.close();
} catch (IOException e) {
throw new MyException("failed to close server conn: ", e);
}
}
Based on my understanding above code looks good. If I remove throw clause and suppress exception in finally then caller of this method will not be able to know server's status. I am not sure how we can achieve same functionality without having throw clause.
Your best shot is to use the Automatic Resource Management feature of Java, available since Java 7. If that is for some reason not available to you, then the next best thing is to replicate what that syntactic sugar expands into:
public static void runWithoutMasking() throws MyException {
AutoClose autoClose = new AutoClose();
MyException myException = null;
try {
autoClose.work();
} catch (MyException e) {
myException = e;
throw e;
} finally {
if (myException != null) {
try {
autoClose.close();
} catch (Throwable t) {
myException.addSuppressed(t);
}
} else {
autoClose.close();
}
}
}
Things to note:
your code swallows the original exception from the try block in case closing the resource fails. The original exception is surely more important for diagnostic;
in the ARM idiom above, closing the resource is done differently depending on whether there already was an exception in the try-block. If try completed normally, then the resource is closed outside any try-catch block, naturally propagating any exception.
Generally, methods in the finally block are 'cleanup' codes (Closing the Connection, etc) which the user does not necessarily need to know.
What I do for these exceptions is to absorb the exception, but log the details.
finally{
try{
connection.close();
}catch(SQLException e){
// do nothing and just log the error
LOG.error("Something happened while closing connection. Cause: " + e.getMessage());
}
}
You're getting a warning because this code could potentially throw an exception while dealing with a thrown exception. You can use the try with resource syntax to close the resource automatically. Read more here.
In the case that the "request failed : " exception is thrown and you fail to close the httpclient, the second exception is the one that would bubble up.
I am not sure how we can achieve same functionality without having
throw clause.
You could nest the two try blocks differently to achieve the same result:
HttpClient httpClient = null; // initialize
try {
try {
// do something with httpClient
} catch(Exception e) {
throw new MyException("request failed : ", e);
} finally {
httpClient.close();
}
} catch (IOException e) {
throw new MyException("failed to close server conn: ", e);
}
I have been given the task of reducing try-catch blocks in my Java code for increasing performance. But each try block is checking for a entirely different kind of exception and that too custom exceptions. How to reduce the try-catch blocks.
The sample of a part of my code is as follows:-
// Get a test engine and use that to initialize and save the test
// taker
TestEngine testEngine = null;
try {
testEngine = objFactory.getTestEngine(login.getTestengine());
} catch (NoTestEngineException e) {
// Add an error message, then throw the exception to struts to
// handle
request.setAttribute("errmsg", "Cannot create test engine: " + login.getTestengine());
request.setAttribute("errcause", "exception.notestengine.cause");
throw e;
}
//added for null check of variable testEngine
if(testEngine==null)
{
request.setAttribute("errmsg", "Could not obtain a testengine");
}
// Do we need to save the session id?
String saveSessionId = objFactory.getConfigValue("testengine." + login.getTestengine() + ".recordjessionid", "false");
String sessionId = null;
if (saveSessionId.trim().equals("true")) {
sessionId = request.getSession().getId();
}
Testtaker testTaker = null;
try {
testTaker = testEngine.buildTestTaker(login, null, sessionId, null, null);
} catch (Exception e) {
request.getSession().removeAttribute(ConstantLibrary.SESSION_LOGIN);
CaslsUtils.outputLoggingData(log_, request);
// Add an error message, then throw the exception to struts to
// handle
request.setAttribute("errmsg", "Cannot build a test taker.");
request.setAttribute("errcause", "exception.testtakerbuildfailed.cause");
//throw new NoTestTakerException("Failed to build testtaker.");
throw e;
}
If your exception type is different for each block in that case you can join your try block to one and add multiple catch block to a with single try block
try {
TestEngine testEngine = objFactory.getTestEngine(login.getTestengine());
//added for null check of variable testEngine
if(testEngine==null) {
request.setAttribute("errmsg", "Could not obtain a testengine");
}
// Do we need to save the session id?
String saveSessionId = objFactory.getConfigValue("testengine." + login.getTestengine() + ".recordjessionid", "false");
String sessionId = null;
if (saveSessionId.trim().equals("true")) {
sessionId = request.getSession().getId();
}
Testtaker testTaker = testEngine.buildTestTaker(login, null, sessionId, null, null);
} catch (NoTestEngineException e) {
// Add an error message, then throw the exception to struts to
// handle
request.setAttribute("errmsg", "Cannot create test engine: " + login.getTestengine());
request.setAttribute("errcause", "exception.notestengine.cause");
throw e;
} catch (Exception e) {
request.getSession().removeAttribute(ConstantLibrary.SESSION_LOGIN);
CaslsUtils.outputLoggingData(log_, request);
// Add an error message, then throw the exception to struts to
// handle
request.setAttribute("errmsg", "Cannot build a test taker.");
request.setAttribute("errcause", "exception.testtakerbuildfailed.cause");
//throw new NoTestTakerException("Failed to build testtaker.");
throw e;
}
You can change two try-catch blocks into one:
TestEngine testEngine = null;
Testtaker testTaker = null;
try {
testEngine = objFactory.getTestEngine(login.getTestengine());
String saveSessionId =
objFactory.getConfigValue("testengine." + login.getTestengine() + ".recordjessionid", "false");
String sessionId = saveSessionId.trim().equals("true") ?
request.getSession().getId() : null;
testTaker = testEngine.buildTestTaker(login, null, sessionId, null, null);
}
catch (NoTestEngineException e) {
// Add an error message, then throw the exception to struts to handle
request.setAttribute("errmsg", "Cannot create test engine: " + login.getTestengine());
request.setAttribute("errcause", "exception.notestengine.cause");
throw e;
}
catch (Exception e) {
request.getSession().removeAttribute(ConstantLibrary.SESSION_LOGIN);
CaslsUtils.outputLoggingData(log_, request);
// Add an error message, then throw the exception to struts to handle
request.setAttribute("errmsg", "Cannot build a test taker.");
request.setAttribute("errcause", "exception.testtakerbuildfailed.cause");
throw e;
}
I would write it like this
public void someMethod(... args) throws Exception {
// taker
TestEngine testEngine = objFactory.getTestEngine(login.getTestengine());
// Do we need to save the session id?
String saveSessionId = objFactory.getConfigValue("testengine." + login.getTestengine() + ".recordjessionid", "false");
String sessionId = null;
if (saveSessionId.trim().equals("true")) {
sessionId = request.getSession().getId();
}
Testtaker testTaker = testEngine.buildTestTaker(login, null, sessionId, null,
}
And I would have the caller handle any exceptions.
Unlike C++, try-catch-finally blocks (exceptions) are essential parts of Java; they should be used and used properly. I also don't think they have significant performance effects; they will be thrown anyway even if you don't catch them (will be caught by the main thread finally). But for aesthetical reasons, you may reorganize them or use a single one for the whole method as in:
method() {
try {
} catch (Ex1 e1) {
} catch (Ex2 e2) {
} finally {
}
}
you can also consider processing them, not throwing again and processing in every method in the call stack (this may have -a bit of?- performance impact..)..
As the comments have noted, the number of try/catch blocks aren't the problem - it's the fact you're hitting them so often that is.
Assuming that you've analysed the performance and it is actually this method that is giving you issues, you should take the obvious steps to avoid exceptions being thrown (and the stack unwound as a result).
For example, you're not returning from the method if testEngine is null after the getTestEngine() call, but you're going to immediately get a NPE after testEngine.buildTestTaker(), hitting one of your catch blocks. Instead you should return from the method (with an appropriate error code) if testEngine is null, avoiding the stack unwind penalty.
in my android app I have an AsyncTask which downloads photos from my server. If I get an exception (mainly for connection time out) I show the user a message. My problem is that my code works MOST of the times, (meaning there are times when I interrupt the WiFi connection that I get an exception shown in my logcat but the message won't appear so I ended it up thinking that there might be an exception that I don't handle ) and I can't figure out the exact reason. I'll post the code run in my AsyncTask and the function that does the essential work. Hope you spot out something I'missing
#Override
protected String doInBackground(String... args) {
JSONParser jParser = new JSONParser();
JSONObject jsonObj = jParser.getJSONFromUrl(url);
Log.d("check1",url);
try {
list.addAll(processJsonData(jsonObj));
} catch (JSONException e) {
e.printStackTrace();
onDownloadFailed(this);
return "failed";
} catch (SocketException e) {
Log.e("Exception", e.getLocalizedMessage());
onDownloadFailed(this);
return "failed";
} catch (IOException e) {
Log.e("Exception", e.getLocalizedMessage());
onDownloadFailed(this);
return "failed";
}finally {
jsonObj=null;
}
return "done";
}
process JsonData is actually bigger that's the part for downloading the photos, the other part is about mapping string to an large Json File
private ArrayList<Monument> processJsonData(JSONObject jsonObj) throws IOException, SocketException, JSONException{
if(attachments!=null){
int lengthSize;
if(attachments.length()<3)
lengthSize=attachments.length();
else
lengthSize=3;
for(int j=0;j<lengthSize;++j){
JSONObject atta = attachments.getJSONObject(j);
JSONObject images = atta.optJSONObject(TAG_IMAGES);
if(images!=null){
JSONObject medium = images.getJSONObject(TAG_MEDIUM);
String url_image = medium.getString(TAG_URL_IMAGE);
String id = atta.getString("id");
String filename =title.replace(" ","")+id+".nomedia";
File destination = new File(MyApplication.getPhotoStorage() ,filename);
URL url = new URL (url_image);
InputStream is = url.openStream();
OutputStream os = new FileOutputStream(destination);
byte[] b = new byte[2048];
int length;
while ((length = is.read(b)) != -1) {
os.write(b, 0, length);
}
is.close();
os.close();
localPhotosUrl.add(destination.getAbsolutePath());
}
}
}
Maybe you could name the actual exception that is beeing thrown?
It might be a RuntimeException and therefore unchecked.
For further information about checked/unchecked exceptions see: Oracle Docs - Exceptions
The API for InteruptedException says the following;
Thrown when a thread is waiting, sleeping, or otherwise occupied, and
the thread is interrupted, either before or during the activity.
Occasionally a method may wish to test whether the current thread has
been interrupted, and if so, to immediately throw this exception.
As described in the comments of the question, cancelling your AsyncTask only after checking that it has finished should prevent this issue.
Alternatively (but I would recommend against it), you could catch the InteruptedException in the method that cancels your AsyncTask to define your custom catch behavior there. Using catch to work around program logic flaws should only really be a last resort after reconsidering the logical flow of your code.
This is my class that extends AsyncTask
public class JSONFunctions extends AsyncTask<Void, Void, String> {
#Override
protected String doInBackground(Void... urls) {
String line = "";
HttpClient httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet("http://appDev.milasevicius.com/server/homeresults.php");
try {
HttpResponse response = httpclient.execute(httpget);
if(response != null) {
InputStream inputstream = response.getEntity().getContent();
line = convertStreamToString(inputstream);
} else {
line = "Unable to complete your request";
}
} catch (ClientProtocolException e) {
line = "Caught ClientProtocolException";
} catch (IOException e) {
line = "Caught IOException";
} catch (Exception e) {
line = "Caught Exception";
}
return line;
}
protected void onProgressUpdate(Integer... progress) {
}
protected void onPostExecute(String result) {
}
}
And this is my call:
String output = new JSONFunctions().execute().get();
But compilator says that
error: unreported exception InterruptedException; must be caught or declared to be thrown
sorry for my noobish question, but how to fix that? And am I doing right call to get result?
public final Result get ()
Added in API level 3
Waits if necessary for the computation to complete, and then retrieves its result.
Returns
The computed result.
Throws
CancellationException If the computation was cancelled.
ExecutionException If the computation threw an exception.
InterruptedException If the current thread was interrupted while waiting.
get() throws InterrruptedException. Your log says you need to catch those.
Also you should not call get() coz its blocks the ui thread waiting fro the result. You should not block the ui thread.
Remove get() and invoke as new JSONFunctions().execute().
To get the result in the activity use a interface
Example #
How do I return a boolean from AsyncTask?
Javadoc
A bare InterruptedException being thrown from .get() indicates that the current thread of execution
(the one calling .get()) was interrupted before calling get(), or while blocked in .get().
This is not the same thing as an executor thread or one of its tasks being interrupted.
Someone or
something is interrupting your "main" thread -- or at least the thread calling .get().
Use
new JSONFunctions().execute()
instead of
new JSONFunctions().execute().get();