Skip to content

Commit 07900cf

Browse files
committed
Fixed androidannotations#646 sending background exception to the global UncaughtExceptionHandler
1 parent 4920ca6 commit 07900cf

3 files changed

Lines changed: 59 additions & 15 deletions

File tree

‎AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/BackgroundProcessor.java‎

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,16 @@
1515
*/
1616
package org.androidannotations.processing;
1717

18-
import static com.sun.codemodel.JExpr._new;
19-
import static com.sun.codemodel.JExpr.lit;
20-
21-
import javax.lang.model.element.Element;
22-
import javax.lang.model.element.ExecutableElement;
23-
18+
import com.sun.codemodel.*;
2419
import org.androidannotations.annotations.Background;
2520
import org.androidannotations.api.BackgroundExecutor;
2621
import org.androidannotations.api.BackgroundExecutor.Task;
2722
import org.androidannotations.helper.APTCodeModelHelper;
2823

29-
import com.sun.codemodel.JBlock;
30-
import com.sun.codemodel.JClass;
31-
import com.sun.codemodel.JClassAlreadyExistsException;
32-
import com.sun.codemodel.JCodeModel;
33-
import com.sun.codemodel.JDefinedClass;
34-
import com.sun.codemodel.JInvocation;
35-
import com.sun.codemodel.JMethod;
36-
import com.sun.codemodel.JMod;
24+
import javax.lang.model.element.Element;
25+
import javax.lang.model.element.ExecutableElement;
26+
27+
import static com.sun.codemodel.JExpr.*;
3728

3829
public class BackgroundProcessor implements DecoratingElementProcessor {
3930

@@ -60,7 +51,17 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) t
6051
JMethod executeMethod = anonymousTaskClass.method(JMod.PUBLIC, codeModel.VOID, "execute");
6152
executeMethod.annotate(Override.class);
6253

63-
executeMethod.body().add( previousMethodBody );
54+
// Catch exception in user code
55+
JTryBlock tryBlock = executeMethod.body()._try();
56+
tryBlock.body().add(previousMethodBody);
57+
JCatchBlock catchBlock = tryBlock._catch(holder.classes().THROWABLE);
58+
JVar caughtException = catchBlock.param("e");
59+
JStatement uncaughtExceptionCall = holder.classes().THREAD
60+
.staticInvoke("getDefaultUncaughtExceptionHandler")
61+
.invoke("uncaughtException")
62+
.arg(holder.classes().THREAD.staticInvoke("currentThread"))
63+
.arg(caughtException);
64+
catchBlock.body().add(uncaughtExceptionCall);
6465

6566
Background annotation = element.getAnnotation(Background.class);
6667
String id = annotation.id();

‎AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EBeansHolder.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public class Classes {
4343
*/
4444
public final JClass RUNTIME_EXCEPTION = refClass(RuntimeException.class);
4545
public final JClass EXCEPTION = refClass(Exception.class);
46+
public final JClass THROWABLE = refClass(Throwable.class);
4647
public final JClass CHAR_SEQUENCE = refClass(CharSequence.class);
4748
public final JClass CLASS_CAST_EXCEPTION = refClass(ClassCastException.class);
4849
public final JClass SERIALIZABLE = refClass(Serializable.class);

‎AndroidAnnotations/functional-test-1-5-tests/src/test/java/org/androidannotations/test15/ThreadActivityTest.java‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ public class ThreadActivityTest {
4646

4747
private ThreadActivity_ activity;
4848

49+
private volatile boolean propagatedExceptionToGlobalExceptionHandler;
50+
4951
@Before
5052
public void setup() {
5153
activity = new ThreadActivity_();
@@ -320,4 +322,44 @@ public void run() {
320322
Assert.assertTrue("Handler field not associated to the main thread", handler.getLooper() == Looper.getMainLooper());
321323
}
322324

325+
@Test
326+
public void propagateExceptionToGlobalExceptionHandler() {
327+
328+
// Prepare lock on which we'll wait for the
329+
// background exception handler to catch the exception
330+
final Object LOCK = new Object();
331+
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
332+
@Override
333+
public void uncaughtException(Thread thread, Throwable ex) {
334+
synchronized (LOCK) {
335+
propagatedExceptionToGlobalExceptionHandler = true;
336+
LOCK.notify();
337+
}
338+
}
339+
});
340+
341+
propagatedExceptionToGlobalExceptionHandler = false;
342+
activity.backgroundThrowException();
343+
344+
// If the default uncaught exception handler is not called
345+
// after 2 secs this method returns and the following assert will fail.
346+
waitOn(LOCK, 2000);
347+
Assert.assertTrue("Exception should have been caught in the DefaultUncaughtExceptionHandler during @Background call.",
348+
propagatedExceptionToGlobalExceptionHandler);
349+
}
350+
351+
/**
352+
* Call wait() on the given object with the specified timeout.
353+
* Avoid boilerplate code like synchronized or try..catch.
354+
*/
355+
private void waitOn(Object lock, long timeout) {
356+
synchronized (lock) {
357+
try {
358+
lock.wait(timeout);
359+
} catch (InterruptedException e) {
360+
e.printStackTrace();
361+
}
362+
}
363+
}
364+
323365
}

0 commit comments

Comments
 (0)