From 50ecf5070837dc7e6e6eec4130d1a12e68895f73 Mon Sep 17 00:00:00 2001 From: rockytriton Date: Mon, 27 May 2013 11:58:50 -0500 Subject: [PATCH 1/7] added check for UI Thread --- .../processing/UiThreadProcessor.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java index 16e1c00c59..ed0e5c64bb 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java @@ -24,11 +24,14 @@ import org.androidannotations.annotations.UiThread; import org.androidannotations.helper.APTCodeModelHelper; +import com.sun.codemodel.JBlock; import com.sun.codemodel.JClass; import com.sun.codemodel.JClassAlreadyExistsException; import com.sun.codemodel.JCodeModel; +import com.sun.codemodel.JConditional; import com.sun.codemodel.JDefinedClass; import com.sun.codemodel.JExpr; +import com.sun.codemodel.JExpression; import com.sun.codemodel.JMethod; import com.sun.codemodel.JMod; @@ -61,6 +64,13 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) t holder.handler = holder.generatedClass.field(JMod.PRIVATE, handlerClass, "handler_", JExpr._new(handlerClass)); } + JExpression jx = JExpr.direct("java.lang.Thread.currentThread() == android.os.Looper.getMainLooper().getThread()"); + + JConditional con = delegatingMethod.body()._if(jx); + JBlock block = con._then(); + block.add(JExpr._super().invoke(delegatingMethod)); + block._return(); + if (delay == 0) { delegatingMethod.body().invoke(holder.handler, "post").arg(_new(anonymousRunnableClass)); } else { From 46263f97e4f3bdcf22cd7701db434f1227e6f336 Mon Sep 17 00:00:00 2001 From: rockytriton Date: Mon, 27 May 2013 17:03:15 -0500 Subject: [PATCH 2/7] got rid of the direct() call --- .../processing/UiThreadProcessor.java | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java index ed0e5c64bb..373c3fea88 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java @@ -24,6 +24,8 @@ import org.androidannotations.annotations.UiThread; import org.androidannotations.helper.APTCodeModelHelper; +import android.os.Looper; + import com.sun.codemodel.JBlock; import com.sun.codemodel.JClass; import com.sun.codemodel.JClassAlreadyExistsException; @@ -34,9 +36,14 @@ import com.sun.codemodel.JExpression; import com.sun.codemodel.JMethod; import com.sun.codemodel.JMod; +import com.sun.codemodel.JOp; public class UiThreadProcessor implements DecoratingElementProcessor { + private static final String METHOD_CUR_THREAD = "currentThread"; + private static final String METHOD_MAIN_LOOPER = "getMainLooper"; + private static final String METHOD_GET_THREAD = "getThread"; + private final APTCodeModelHelper helper = new APTCodeModelHelper(); @Override @@ -64,12 +71,7 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) t holder.handler = holder.generatedClass.field(JMod.PRIVATE, handlerClass, "handler_", JExpr._new(handlerClass)); } - JExpression jx = JExpr.direct("java.lang.Thread.currentThread() == android.os.Looper.getMainLooper().getThread()"); - - JConditional con = delegatingMethod.body()._if(jx); - JBlock block = con._then(); - block.add(JExpr._super().invoke(delegatingMethod)); - block._return(); + addUIThreadCheck(delegatingMethod, codeModel); if (delay == 0) { delegatingMethod.body().invoke(holder.handler, "post").arg(_new(anonymousRunnableClass)); @@ -80,4 +82,26 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) t } + /** + * Add the pre-check to see if we are already in the UI thread. + * + * @param delegatingMethod + * @param codeModel + * @throws JClassAlreadyExistsException + */ + private void addUIThreadCheck(JMethod delegatingMethod, JCodeModel codeModel) throws JClassAlreadyExistsException { + // Get the Thread and Looper class. + JClass tClass = codeModel.ref(Thread.class); + JClass lClass = codeModel.ref(Looper.class); + + // invoke the methods. + JExpression lhs = tClass.staticInvoke(METHOD_CUR_THREAD); + JExpression rhs = lClass.staticInvoke(METHOD_MAIN_LOOPER).invoke(METHOD_GET_THREAD); + + // create the conditional and the block. + JConditional con = delegatingMethod.body()._if(JOp.eq(lhs, rhs)); + JBlock block = con._then(); + block.add(JExpr._super().invoke(delegatingMethod)); + block._return(); + } } From 6b7d71622394332a799ddd369553288cf9c8a734 Mon Sep 17 00:00:00 2001 From: Rocky Pulley Date: Tue, 28 May 2013 14:40:16 -0500 Subject: [PATCH 3/7] Fixed an issue when the method had parameters --- .../processing/UiThreadProcessor.java | 45 +++++++++++++++---- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java index 373c3fea88..364c021bc8 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java @@ -37,6 +37,7 @@ import com.sun.codemodel.JMethod; import com.sun.codemodel.JMod; import com.sun.codemodel.JOp; +import com.sun.codemodel.JStatement; public class UiThreadProcessor implements DecoratingElementProcessor { @@ -58,11 +59,14 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) t JMethod delegatingMethod = helper.overrideAnnotatedMethod(executableElement, holder); + //Need to clone the body to be put in the check. This is done here because the next line + //will remove the body from the method. + JBlock clonedBody = cloneMethodBody(delegatingMethod); + JDefinedClass anonymousRunnableClass = helper.createDelegatingAnonymousRunnableClass(holder, delegatingMethod); { - // Execute Runnable - + //Execute Runnable UiThread annotation = element.getAnnotation(UiThread.class); long delay = annotation.delay(); @@ -71,7 +75,8 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) t holder.handler = holder.generatedClass.field(JMod.PRIVATE, handlerClass, "handler_", JExpr._new(handlerClass)); } - addUIThreadCheck(delegatingMethod, codeModel); + //Put in the check for the UI thread. + addUIThreadCheck(delegatingMethod, clonedBody, codeModel); if (delay == 0) { delegatingMethod.body().invoke(holder.handler, "post").arg(_new(anonymousRunnableClass)); @@ -82,26 +87,48 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) t } + /** + * Clone a method body. + * + * @param method the method to clone. + * @return A new JBlock containing the method body. + */ + private JBlock cloneMethodBody(JMethod method) { + JBlock clonedBody = new JBlock(false, false); + + for (Object statement : method.body().getContents()) { + clonedBody.add((JStatement) statement); + } + + return clonedBody; + } + /** * Add the pre-check to see if we are already in the UI thread. - * + * * @param delegatingMethod * @param codeModel * @throws JClassAlreadyExistsException */ - private void addUIThreadCheck(JMethod delegatingMethod, JCodeModel codeModel) throws JClassAlreadyExistsException { - // Get the Thread and Looper class. + private void addUIThreadCheck(JMethod delegatingMethod, JBlock clonedBody, JCodeModel codeModel) throws JClassAlreadyExistsException { + //Get the Thread and Looper class. JClass tClass = codeModel.ref(Thread.class); JClass lClass = codeModel.ref(Looper.class); - // invoke the methods. + //invoke the methods. JExpression lhs = tClass.staticInvoke(METHOD_CUR_THREAD); JExpression rhs = lClass.staticInvoke(METHOD_MAIN_LOOPER).invoke(METHOD_GET_THREAD); - // create the conditional and the block. + //create the conditional and the block. JConditional con = delegatingMethod.body()._if(JOp.eq(lhs, rhs)); JBlock block = con._then(); - block.add(JExpr._super().invoke(delegatingMethod)); + + //Put the cloned method body in the if() block, this is necessary because the + //method may have parameters and a simple super.methodCall() won't work. + for (Object statement : clonedBody.getContents()) { + block.add((JStatement) statement); + } + block._return(); } } From acd3972301a0e014cb72bce906b3fe1e5b1c6b2f Mon Sep 17 00:00:00 2001 From: Rocky Pulley Date: Tue, 28 May 2013 15:28:54 -0500 Subject: [PATCH 4/7] added useDirect variable to UiThread annotation --- .../annotations/UiThread.java | 13 +++++- .../processing/UiThreadProcessor.java | 40 ++++++++++++------- 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/UiThread.java b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/UiThread.java index 39b94bde2e..dbe125deda 100644 --- a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/UiThread.java +++ b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/UiThread.java @@ -22,13 +22,22 @@ /** * Should be used on method that must be run in the Ui thread - * + * * The annotation parameter delay is the delay (in milliseconds) until the * method will be executed. The default is 0 (no delay). - * + * */ @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) public @interface UiThread { long delay() default 0; + + /** + * If useDirect = true, the method will check first if it is inside the UI + * thread already. If so, it will directly call the method instead of using + * the handler. + * + * @return + */ + boolean useDirect() default false; } diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java index 364c021bc8..fcb8bcf300 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java @@ -56,18 +56,23 @@ public String getTarget() { public void process(Element element, JCodeModel codeModel, EBeanHolder holder) throws JClassAlreadyExistsException { ExecutableElement executableElement = (ExecutableElement) element; + UiThread annotation = element.getAnnotation(UiThread.class); + boolean useDirect = annotation.useDirect(); JMethod delegatingMethod = helper.overrideAnnotatedMethod(executableElement, holder); + JBlock clonedBody = null; - //Need to clone the body to be put in the check. This is done here because the next line - //will remove the body from the method. - JBlock clonedBody = cloneMethodBody(delegatingMethod); + if (useDirect) { + // Need to clone the body to be put in the check. This is done here + // because the next line + // will remove the body from the method. + clonedBody = cloneMethodBody(delegatingMethod); + } JDefinedClass anonymousRunnableClass = helper.createDelegatingAnonymousRunnableClass(holder, delegatingMethod); { - //Execute Runnable - UiThread annotation = element.getAnnotation(UiThread.class); + // Execute Runnable long delay = annotation.delay(); if (holder.handler == null) { @@ -75,8 +80,10 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) t holder.handler = holder.generatedClass.field(JMod.PRIVATE, handlerClass, "handler_", JExpr._new(handlerClass)); } - //Put in the check for the UI thread. - addUIThreadCheck(delegatingMethod, clonedBody, codeModel); + if (useDirect) { + // Put in the check for the UI thread. + addUIThreadCheck(delegatingMethod, clonedBody, codeModel); + } if (delay == 0) { delegatingMethod.body().invoke(holder.handler, "post").arg(_new(anonymousRunnableClass)); @@ -89,8 +96,9 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) t /** * Clone a method body. - * - * @param method the method to clone. + * + * @param method + * the method to clone. * @return A new JBlock containing the method body. */ private JBlock cloneMethodBody(JMethod method) { @@ -105,26 +113,28 @@ private JBlock cloneMethodBody(JMethod method) { /** * Add the pre-check to see if we are already in the UI thread. - * + * * @param delegatingMethod * @param codeModel * @throws JClassAlreadyExistsException */ private void addUIThreadCheck(JMethod delegatingMethod, JBlock clonedBody, JCodeModel codeModel) throws JClassAlreadyExistsException { - //Get the Thread and Looper class. + // Get the Thread and Looper class. JClass tClass = codeModel.ref(Thread.class); JClass lClass = codeModel.ref(Looper.class); - //invoke the methods. + // invoke the methods. JExpression lhs = tClass.staticInvoke(METHOD_CUR_THREAD); JExpression rhs = lClass.staticInvoke(METHOD_MAIN_LOOPER).invoke(METHOD_GET_THREAD); - //create the conditional and the block. + // create the conditional and the block. JConditional con = delegatingMethod.body()._if(JOp.eq(lhs, rhs)); JBlock block = con._then(); - //Put the cloned method body in the if() block, this is necessary because the - //method may have parameters and a simple super.methodCall() won't work. + // Put the cloned method body in the if() block, this is necessary + // because the + // method may have parameters and a simple super.methodCall() won't + // work. for (Object statement : clonedBody.getContents()) { block.add((JStatement) statement); } From a5c4b7fa9b21239b8d5592fbc7f8bcda332801ee Mon Sep 17 00:00:00 2001 From: Rocky Pulley Date: Mon, 10 Jun 2013 12:44:04 -0500 Subject: [PATCH 5/7] added Propagation enum instead of boolean --- .../annotations/UiThread.java | 17 +++++++++++------ .../processing/UiThreadProcessor.java | 7 ++++--- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/UiThread.java b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/UiThread.java index dbe125deda..20839d689e 100644 --- a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/UiThread.java +++ b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/UiThread.java @@ -22,10 +22,10 @@ /** * Should be used on method that must be run in the Ui thread - * + * * The annotation parameter delay is the delay (in milliseconds) until the * method will be executed. The default is 0 (no delay). - * + * */ @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) @@ -33,11 +33,16 @@ long delay() default 0; /** - * If useDirect = true, the method will check first if it is inside the UI - * thread already. If so, it will directly call the method instead of using + * If propagation = REUSE, the method will check first if it is inside the + * UI thread already. If so, it will directly call the method instead of + * using the handler. The default value is ENQUEUE, which will always call * the handler. - * + * * @return */ - boolean useDirect() default false; + Propagation propagation() default Propagation.ENQUEUE; + + public enum Propagation { + ENQUEUE, REUSE + } } diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java index fcb8bcf300..dba03ac2de 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java @@ -22,6 +22,7 @@ import javax.lang.model.element.ExecutableElement; import org.androidannotations.annotations.UiThread; +import org.androidannotations.annotations.UiThread.Propagation; import org.androidannotations.helper.APTCodeModelHelper; import android.os.Looper; @@ -57,12 +58,12 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) t ExecutableElement executableElement = (ExecutableElement) element; UiThread annotation = element.getAnnotation(UiThread.class); - boolean useDirect = annotation.useDirect(); + Propagation propagation = annotation.propagation(); JMethod delegatingMethod = helper.overrideAnnotatedMethod(executableElement, holder); JBlock clonedBody = null; - if (useDirect) { + if (propagation == Propagation.REUSE) { // Need to clone the body to be put in the check. This is done here // because the next line // will remove the body from the method. @@ -80,7 +81,7 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) t holder.handler = holder.generatedClass.field(JMod.PRIVATE, handlerClass, "handler_", JExpr._new(handlerClass)); } - if (useDirect) { + if (propagation == Propagation.REUSE) { // Put in the check for the UI thread. addUIThreadCheck(delegatingMethod, clonedBody, codeModel); } From a4816de9f9b3575c52bcaa0d9674072658497eb5 Mon Sep 17 00:00:00 2001 From: Rocky Pulley Date: Mon, 17 Jun 2013 12:13:31 -0500 Subject: [PATCH 6/7] Removed clone body for a better method. --- .../processing/UiThreadProcessor.java | 45 +++---------------- 1 file changed, 7 insertions(+), 38 deletions(-) diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java index dba03ac2de..f88d9a18b2 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java @@ -38,7 +38,6 @@ import com.sun.codemodel.JMethod; import com.sun.codemodel.JMod; import com.sun.codemodel.JOp; -import com.sun.codemodel.JStatement; public class UiThreadProcessor implements DecoratingElementProcessor { @@ -61,14 +60,6 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) t Propagation propagation = annotation.propagation(); JMethod delegatingMethod = helper.overrideAnnotatedMethod(executableElement, holder); - JBlock clonedBody = null; - - if (propagation == Propagation.REUSE) { - // Need to clone the body to be put in the check. This is done here - // because the next line - // will remove the body from the method. - clonedBody = cloneMethodBody(delegatingMethod); - } JDefinedClass anonymousRunnableClass = helper.createDelegatingAnonymousRunnableClass(holder, delegatingMethod); @@ -83,7 +74,7 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) t if (propagation == Propagation.REUSE) { // Put in the check for the UI thread. - addUIThreadCheck(delegatingMethod, clonedBody, codeModel); + addUIThreadCheck(delegatingMethod, codeModel, holder); } if (delay == 0) { @@ -95,31 +86,15 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) t } - /** - * Clone a method body. - * - * @param method - * the method to clone. - * @return A new JBlock containing the method body. - */ - private JBlock cloneMethodBody(JMethod method) { - JBlock clonedBody = new JBlock(false, false); - - for (Object statement : method.body().getContents()) { - clonedBody.add((JStatement) statement); - } - - return clonedBody; - } - /** * Add the pre-check to see if we are already in the UI thread. * * @param delegatingMethod * @param codeModel + * @param holder * @throws JClassAlreadyExistsException */ - private void addUIThreadCheck(JMethod delegatingMethod, JBlock clonedBody, JCodeModel codeModel) throws JClassAlreadyExistsException { + private void addUIThreadCheck(JMethod delegatingMethod, JCodeModel codeModel, EBeanHolder holder) throws JClassAlreadyExistsException { // Get the Thread and Looper class. JClass tClass = codeModel.ref(Thread.class); JClass lClass = codeModel.ref(Looper.class); @@ -130,16 +105,10 @@ private void addUIThreadCheck(JMethod delegatingMethod, JBlock clonedBody, JCode // create the conditional and the block. JConditional con = delegatingMethod.body()._if(JOp.eq(lhs, rhs)); - JBlock block = con._then(); - - // Put the cloned method body in the if() block, this is necessary - // because the - // method may have parameters and a simple super.methodCall() won't - // work. - for (Object statement : clonedBody.getContents()) { - block.add((JStatement) statement); - } + JBlock thenBlock = con._then(); + + helper.callSuperMethod(delegatingMethod, holder, thenBlock); - block._return(); + thenBlock._return(); } } From f48bdd955d4690f7602051d96722fc2382fe297f Mon Sep 17 00:00:00 2001 From: Rocky Pulley Date: Mon, 17 Jun 2013 12:21:53 -0500 Subject: [PATCH 7/7] Changed to only reuse thread if delay == 0 --- .../processing/UiThreadProcessor.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java index f88d9a18b2..1b4e78caf7 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/UiThreadProcessor.java @@ -72,12 +72,12 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) t holder.handler = holder.generatedClass.field(JMod.PRIVATE, handlerClass, "handler_", JExpr._new(handlerClass)); } - if (propagation == Propagation.REUSE) { - // Put in the check for the UI thread. - addUIThreadCheck(delegatingMethod, codeModel, holder); - } - if (delay == 0) { + if (propagation == Propagation.REUSE) { + // Put in the check for the UI thread. + addUIThreadCheck(delegatingMethod, codeModel, holder); + } + delegatingMethod.body().invoke(holder.handler, "post").arg(_new(anonymousRunnableClass)); } else { delegatingMethod.body().invoke(holder.handler, "postDelayed").arg(_new(anonymousRunnableClass)).arg(lit(delay));