Slide 1

Slide 1 text

support library 22.2.0 Annotations takahirom

Slide 2

Slide 2 text

ࣗ෼ʹ͍ͭͯ • takahirom(@new_runnable) ! • ॳϙςνLTͰ͢ • ༏͍ͯͩ͘͘͠͠͞

Slide 3

Slide 3 text

Support Annotations Google I/O What’s New in Android Development Tools - 13ݸͷ৽͍͠Ξϊςʔγϣϯͷ௥Ճ

Slide 4

Slide 4 text

Support Annotations compile 'com.android.support:support-annotations:22.2.0' !

Slide 5

Slide 5 text

Support Annotations ͖ͬͱ͋ͳͨͷϓϩδΣΫτʹ΋ɺɺ ! ./gradlew app:dependencies +--- com.android.support:appcompat-v7:22.2.0 | \--- com.android.support:support-v4:22.2.0 | \--- com.android.support:support-annotations:22.2.0

Slide 6

Slide 6 text

ΞϊςʔγϣϯҰཡ @WorkerThread
 @UiThread @MainThread @BinderThread
 Thread @CallSuper @CheckResult @RequiresPermission @Keep @VisibleForTesting Ҿ਺νΣοΫ @Size
 @IntRange @FloatRange @ColorInt ϝιου

Slide 7

Slide 7 text

Thread Annotations • @WorkerThread • @UiThread • @MainThread • @BinderThread

Slide 8

Slide 8 text

ྫ:@WorkerThread

Slide 9

Slide 9 text

Ҿ਺νΣοΫ • @Size จࣈྻ΍഑ྻͷ௕͞ͷ࠷େ஋ ࠷খ஋ͳͲΛઃఆ ߹க ͠ͳ͍ͳΒܯࠂ • @IntRange ਺ࣈͷൣғ ൣғ֎ͳΒܯࠂ • @FloatRange float൛ • @ColorInt ৭ͷintͰͳ͚Ε͹ܯࠂ

Slide 10

Slide 10 text

ྫ:@Size

Slide 11

Slide 11 text

ϝιου • @CallSuper superϝιουΛݺͼग़͍ͯ͠ͳ͍ͱܯࠂ • @CheckResult ϝιουͷฦΓ஋Λ࢖͍ͬͯͳ͍ͱܯࠂ • @RequiresPermission ඞཁͳύʔϛογϣϯ͕ͳ͍ͱܯࠂ • @Keep ProGuard͕͚͍ͭͯΔϝιουͰແޮʹͳΔ(ࠓ ͸ಈ͔ͳ͍ʁ) • @VisibleForTesting TestͰ࢖͏ϝιου͔ͩΒpublicͳͲ ʹ͍ͯ͠Δͱ͍͏දࣔ

Slide 12

Slide 12 text

ඪ४ϝιουͷΞϊςʔγϣϯ • Ͳͷϝιου΍ΫϥεʹΞϊςʔγϣϯ͕෇͍͍ͯΔ͔Θ͔ Δ • https://android.googlesource.com/platform/tools/adt/ idea/+/master/android/annotations/android/ ! 
 
 


Slide 13

Slide 13 text

@MainThread͕Α͘෼͔Βͳ͍

Slide 14

Slide 14 text

Android Studio಺ͷThreadνΣοΫ

Slide 15

Slide 15 text

private static void checkThreadAnnotation(PsiCallExpression methodCall, ProblemsHolder holder, PsiMethod method, String qualifiedName) {
 String threadContext = getThreadContext(methodCall);
 if (threadContext != null && !isCompatibleThread(threadContext, qualifiedName)) {
 String message = String.format("Method %1$s must be called from the %2$s thread, currently inferred thread is %3$s",
 method.getName(), describeThread(qualifiedName), describeThread(threadContext));
 holder.registerProblem(methodCall, message);
 } } checkThreadAnnotation private static void checkThreadAnnotation(PsiCallExpression methodCall, ProblemsHolder holder, PsiMethod method, String qualifiedName) {
 String threadContext = getThreadContext(methodCall);
 if (threadContext != null && !isCompatibleThread(threadContext, qualifiedName)) {
 String message = String.format("Method %1$s must be called from the %2$s thread, currently inferred thread is %3$s",
 method.getName(), describeThread(qualifiedName), describeThread(threadContext));
 holder.registerProblem(methodCall, message);
 } } ifจͷத͕࣮ߦ͞ΕΔͱܯࠂ͕දࣔ͞Ε·͢

Slide 16

Slide 16 text

private static void checkThreadAnnotation(PsiCallExpression methodCall, ProblemsHolder holder, PsiMethod method, String qualifiedName) {
 String threadContext = getThreadContext(methodCall);
 if (threadContext != null && !isCompatibleThread(threadContext, qualifiedName)) {
 String message = String.format("Method %1$s must be called from the %2$s thread, currently inferred thread is %3$s",
 method.getName(), describeThread(qualifiedName), describeThread(threadContext));
 holder.registerProblem(methodCall, message);
 } } checkThreadAnnotation private static void ProblemsHolder holder String threadContext = getThreadContext(methodCall) if String message = String.format( thread, currently inferred thread is %3$s" describeThread(threadContext)) } } ifจͷத͕࣮ߦ͞ΕΔͱܯࠂ͕දࣔ͞Ε·͢

Slide 17

Slide 17 text

private static void checkThreadAnnotation(PsiCallExpression methodCall, ProblemsHolder holder, PsiMethod method, String qualifiedName) {
 String threadContext = getThreadContext(methodCall);
 if (threadContext != null && !isCompatibleThread(threadContext, qualifiedName)) {
 String message = String.format("Method %1$s must be called from the %2$s thread, currently inferred thread is %3$s",
 method.getName(), describeThread(qualifiedName), describeThread(threadContext));
 holder.registerProblem(methodCall, message);
 } checkThreadAnnotation private static void checkThreadAnnotation(PsiCallExpression methodCall, ProblemsHolder holder, PsiMethod method, String qualifiedName) {
 String threadContext = getThreadContext(methodCall);
 if (threadContext != null && !isCompatibleThread(threadContext, qualifiedName)) {
 String message = String.format("Method %1$s must be called from the %2$s thread, currently inferred thread is %3$s",
 method.getName(), describeThread(qualifiedName), describeThread(threadContext));
 holder.registerProblem(methodCall, message);
 } } https://android.googlesource.com/platform/tools/adt/idea/+/master/android/src/org/jetbrains/android/ inspections/ResourceTypeInspection.java ͳͷͰ৚݅จΛݟͯߦ͖·͠ΐ͏

Slide 18

Slide 18 text

private static void checkThreadAnnotation(PsiCallExpression methodCall, ProblemsHolder holder, PsiMethod method, String qualifiedName) {
 String threadContext = getThreadContext(methodCall);
 if (threadContext != null && !isCompatibleThread(threadContext, qualifiedName)) {
 String message = String.format("Method %1$s must be called from the %2$s thread, currently inferred thread is %3$s",
 method.getName(), describeThread(qualifiedName), describeThread(threadContext));
 holder.registerProblem(methodCall, message);
 } checkThreadAnnotation private static void checkThreadAnnotation(PsiCallExpression methodCall, ProblemsHolder holder, PsiMethod method, String qualifiedName) {
 String threadContext = getThreadContext(methodCall);
 if (threadContext != null && !isCompatibleThread(threadContext, qualifiedName)) {
 thread, currently inferred thread is %3$s" describeThread(threadContext)) } } https://android.googlesource.com/platform/tools/adt/idea/+/master/android/src/org/jetbrains/android/ inspections/ResourceTypeInspection.java ͳͷͰ৚݅จΛݟͯߦ͖·͠ΐ͏

Slide 19

Slide 19 text

private static void checkThreadAnnotation(PsiCallExpression methodCall, ProblemsHolder holder, PsiMethod method, String qualifiedName) {
 String threadContext = getThreadContext(methodCall);
 if (threadContext != null && !isCompatibleThread(threadContext, qualifiedName)) {
 String message = String.format("Method %1$s must be called from the %2$s thread, currently inferred thread is %3$s",
 method.getName(), describeThread(qualifiedName), describeThread(threadContext));
 holder.registerProblem(methodCall, message);
 } isCompatibleThread private static void checkThreadAnnotation(PsiCallExpression methodCall, ProblemsHolder holder, PsiMethod method, String qualifiedName) {
 String threadContext = getThreadContext(methodCall);
 if (threadContext != null && !isCompatibleThread(threadContext, qualifiedName)) {
 String message = String.format("Method %1$s must be called from the %2$s thread, currently inferred thread is %3$s",
 method.getName(), describeThread(qualifiedName), describeThread(threadContext));
 holder.registerProblem(methodCall, message);
 } } isCompatibleThreadͱ͍͏ϝιουͰνΣοΫ͍ͯ͠ΔΑ͏Ͱ͢

Slide 20

Slide 20 text

private static void checkThreadAnnotation(PsiCallExpression methodCall, ProblemsHolder holder, PsiMethod method, String qualifiedName) {
 String threadContext = getThreadContext(methodCall);
 if (threadContext != null && !isCompatibleThread(threadContext, qualifiedName)) {
 String message = String.format("Method %1$s must be called from the %2$s thread, currently inferred thread is %3$s",
 method.getName(), describeThread(qualifiedName), describeThread(threadContext));
 holder.registerProblem(methodCall, message);
 } isCompatibleThread private static void checkThreadAnnotation(PsiCallExpression methodCall, ProblemsHolder holder, PsiMethod method, String qualifiedName) { String threadContext = getThreadContext(methodCall) if thread, currently inferred thread is %3$s",
 method.getName(), describeThread(qualifiedName), describeThread(threadContext));
 holder.registerProblem(methodCall, message);
 } } isCompatibleThreadͱ͍͏ϝιουͰνΣοΫ͍ͯ͠ΔΑ͏Ͱ͢

Slide 21

Slide 21 text

isCompatibleThread public static boolean isCompatibleThread(@NonNull String thread1, @NonNull String thread2) {
 if (thread1.equals(thread2)) {
 return true;
 }
 // Allow @UiThread and @MainThread to be combined
 if (thread1.equals(UI_THREAD_ANNOTATION)) {
 if (thread2.equals(MAIN_THREAD_ANNOTATION)) {
 return true;
 }
 } else if (thread1.equals(MAIN_THREAD_ANNOTATION)) {
 if (thread2.equals(UI_THREAD_ANNOTATION)) {
 return true;
 }
 }
 return false;
 } thread1΍thread2ʹ͸MainThreadͳͲͷจࣈྻ͕ೖ͍ͬͯ·͢

Slide 22

Slide 22 text

isCompatibleThread public static boolean isCompatibleThread(@NonNull String thread1, @NonNull String thread2) {
 if (thread1.equals(thread2)) {
 return true;
 }
 // Allow @UiThread and @MainThread to be combined
 if (thread1.equals(UI_THREAD_ANNOTATION)) {
 if (thread2.equals(MAIN_THREAD_ANNOTATION)) {
 return true;
 }
 } else if (thread1.equals(MAIN_THREAD_ANNOTATION)) {
 if (thread2.equals(UI_THREAD_ANNOTATION)) {
 return true;
 }
 }
 return false;
 } ಉ͡ͳΒ͹͙͢ʹtrueΛฦ͠·͢

Slide 23

Slide 23 text

isCompatibleThread public static boolean isCompatibleThread(@NonNull String thread1, @NonNull String thread2) {
 if (thread1.equals(thread2)) {
 return true;
 }
 // Allow @UiThread and @MainThread to be combined
 if (thread1.equals(UI_THREAD_ANNOTATION)) {
 if (thread2.equals(MAIN_THREAD_ANNOTATION)) {
 return true;
 }
 } else if (thread1.equals(MAIN_THREAD_ANNOTATION)) {
 if (thread2.equals(UI_THREAD_ANNOTATION)) {
 return true;
 }
 }
 return false;
 } MainThreadͱUIThread͸ಉ͡ѻ͍Λ͠ɺଞ͸ҟͳΔͷͰfalseΛฦ͠·͢

Slide 24

Slide 24 text

private static void checkThreadAnnotation(PsiCallExpression methodCall, ProblemsHolder holder, PsiMethod method, String qualifiedName) {
 String threadContext = getThreadContext(methodCall);
 if (threadContext != null && !isCompatibleThread(threadContext, qualifiedName)) {
 String message = String.format("Method %1$s must be called from the %2$s thread, currently inferred thread is %3$s",
 method.getName(), describeThread(qualifiedName), describeThread(threadContext));
 holder.registerProblem(methodCall, message);
 } } getThreadContext private static void checkThreadAnnotation(PsiCallExpression methodCall, ProblemsHolder holder, PsiMethod method, String qualifiedName) {
 String threadContext = getThreadContext(methodCall);
 if (threadContext != null && !isCompatibleThread(threadContext, qualifiedName)) {
 String message = String.format("Method %1$s must be called from the %2$s thread, currently inferred thread is %3$s",
 method.getName(), describeThread(qualifiedName), describeThread(threadContext));
 holder.registerProblem(methodCall, message);
 } } ࣍ʹgetThreadContextͰͲͷThreadͰ࣮ߦ͍ͯ͠Δ͔ͷ൑ఆΛݟͯߦ͖·͠ΐ͏

Slide 25

Slide 25 text

private static void checkThreadAnnotation(PsiCallExpression methodCall, ProblemsHolder holder, PsiMethod method, String qualifiedName) {
 String threadContext = getThreadContext(methodCall);
 if (threadContext != null && !isCompatibleThread(threadContext, qualifiedName)) {
 String message = String.format("Method %1$s must be called from the %2$s thread, currently inferred thread is %3$s",
 method.getName(), describeThread(qualifiedName), describeThread(threadContext));
 holder.registerProblem(methodCall, message);
 } } getThreadContext private static void checkThreadAnnotation(PsiCallExpression methodCall, ProblemsHolder holder, PsiMethod method, String qualifiedName) { String threadContext = getThreadContext(methodCall) if thread, currently inferred thread is %3$s",
 method.getName(), describeThread(qualifiedName), describeThread(threadContext));
 holder.registerProblem(methodCall, message);
 } } ࣍ʹgetThreadContextͰͲͷThreadͰ࣮ߦ͍ͯ͠Δ͔ͷ൑ఆΛݟͯߦ͖·͠ΐ͏

Slide 26

Slide 26 text

@Nullable
 private static String getThreadContext(PsiCallExpression methodCall) {
 PsiMethod method = PsiTreeUtil.getParentOfType(methodCall, PsiMethod.class, true);
 if (method != null) {
 PsiAnnotation[] annotations = getAllAnnotations(method);
 for (PsiAnnotation annotation : annotations) {
 String qualifiedName = annotation.getQualifiedName();
 if (qualifiedName == null) {
 continue;
 }
 
 if (qualifiedName.startsWith(SUPPORT_ANNOTATIONS_PREFIX) && qualifiedName.endsWith(THREAD_SUFFIX)) {
 return qualifiedName;
 }
 } …….
 } getThreadContext (1/2) @Nullable
 private static String getThreadContext(PsiCallExpression methodCall) {
 PsiMethod method = PsiTreeUtil.getParentOfType(methodCall, PsiMethod.class, true);
 if (method != null) {
 PsiAnnotation[] annotations = getAllAnnotations(method);
 for (PsiAnnotation annotation : annotations) {
 String qualifiedName = annotation.getQualifiedName();
 if (qualifiedName == null) {
 continue;
 }
 
 if (qualifiedName.startsWith(SUPPORT_ANNOTATIONS_PREFIX) && qualifiedName.endsWith(THREAD_SUFFIX)) {
 return qualifiedName;
 }
 } …….
 } ϝιουʹ͍͍ͭͯΔΞϊςʔγϣϯΛऔಘͯͦ͠ΕΛฦ͠·͢

Slide 27

Slide 27 text

@Nullable
 private static String getThreadContext(PsiCallExpression methodCall) {
 PsiMethod method = PsiTreeUtil.getParentOfType(methodCall, PsiMethod.class, true);
 if (method != null) {
 PsiAnnotation[] annotations = getAllAnnotations(method);
 for (PsiAnnotation annotation : annotations) {
 String qualifiedName = annotation.getQualifiedName();
 if (qualifiedName == null) {
 continue;
 }
 
 if (qualifiedName.startsWith(SUPPORT_ANNOTATIONS_PREFIX) && qualifiedName.endsWith(THREAD_SUFFIX)) {
 return qualifiedName;
 }
 } …….
 } getThreadContext (1/2) @Nullable
 private static PsiMethod method = PsiTreeUtil.getParentOfType(methodCall if PsiAnnotation[] annotations = getAllAnnotations(method) for String qualifiedName = annotation.getQualifiedName() if 
 } ……. } ϝιουʹ͍͍ͭͯΔΞϊςʔγϣϯΛऔಘͯͦ͠ΕΛฦ͠·͢

Slide 28

Slide 28 text

@Nullable
 private static String getThreadContext(PsiCallExpression methodCall) {
 PsiMethod method = PsiTreeUtil.getParentOfType(methodCall, PsiMethod.class, true);
 if (method != null) {
 ……
 // See if we're extending a class with a known threading context
 PsiClass cls = method.getContainingClass();
 if (cls != null) {
 annotations = getAllAnnotations(cls);
 for (PsiAnnotation annotation : annotations) {
 String qualifiedName = annotation.getQualifiedName();
 if (qualifiedName == null) {
 continue;
 }
 
 if (qualifiedName.startsWith(SUPPORT_ANNOTATIONS_PREFIX) && qualifiedName.endsWith(THREAD_SUFFIX)) {
 return qualifiedName;
 }
 }
 }
 } getThreadContext (2/2) @Nullable
 private static String getThreadContext(PsiCallExpression methodCall) {
 PsiMethod method = PsiTreeUtil.getParentOfType(methodCall, PsiMethod.class, true);
 if (method != null) {
 ……
 // See if we're extending a class with a known threading context
 PsiClass cls = method.getContainingClass();
 if (cls != null) {
 annotations = getAllAnnotations(cls);
 for (PsiAnnotation annotation : annotations) {
 String qualifiedName = annotation.getQualifiedName();
 if (qualifiedName == null) {
 continue;
 }
 
 if (qualifiedName.startsWith(SUPPORT_ANNOTATIONS_PREFIX) && qualifiedName.endsWith(THREAD_SUFFIX)) {
 return qualifiedName;
 }
 }
 }
 } ͦΕ͚ͩͰͳ͘ɺΫϥεʹ͍͍ͭͯΔΞϊςʔγϣϯΛऔಘ͠·͢

Slide 29

Slide 29 text

@Nullable
 private static String getThreadContext(PsiCallExpression methodCall) {
 PsiMethod method = PsiTreeUtil.getParentOfType(methodCall, PsiMethod.class, true);
 if (method != null) {
 ……
 // See if we're extending a class with a known threading context
 PsiClass cls = method.getContainingClass();
 if (cls != null) {
 annotations = getAllAnnotations(cls);
 for (PsiAnnotation annotation : annotations) {
 String qualifiedName = annotation.getQualifiedName();
 if (qualifiedName == null) {
 continue;
 }
 
 if (qualifiedName.startsWith(SUPPORT_ANNOTATIONS_PREFIX) && qualifiedName.endsWith(THREAD_SUFFIX)) {
 return qualifiedName;
 }
 }
 }
 } getThreadContext (2/2) @Nullable
 private static PsiMethod method = PsiTreeUtil.getParentOfType(methodCall if ……
 // See if we're extending a class with a known threading context
 PsiClass cls = method.getContainingClass() if annotations = getAllAnnotations(cls) for String qualifiedName = annotation.getQualifiedName() if 
 }
 }
 } ͦΕ͚ͩͰͳ͘ɺΫϥεʹ͍͍ͭͯΔΞϊςʔγϣϯΛऔಘ͠·͢

Slide 30

Slide 30 text

getThreadContext private static void checkThreadAnnotation(PsiCallExpression methodCall, ProblemsHolder holder, PsiMethod method, String qualifiedName) {
 String threadContext = getThreadContext(methodCall);
 if (threadContext != null && !isCompatibleThread(threadContext, qualifiedName)) {
 String message = String.format("Method %1$s must be called from the %2$s thread, currently inferred thread is %3$s",
 method.getName(), describeThread(qualifiedName), describeThread(threadContext));
 holder.registerProblem(methodCall, message);
 } } ͭ·Γݺͼग़͠ݩͷϝιουͱΫϥεͷΞϊςʔγϣϯΛݟ͍ͯ·͢

Slide 31

Slide 31 text

@WorkerThreadΛ͚ͭΕ͹OK

Slide 32

Slide 32 text

AnnotationͷϧʔϧΛڧ੍͢Δ rootͷbuild.gradleͰ ! classpath 'com.android.tools.build:gradle:1.2.3' ↓ classpath 'com.android.tools.build:gradle:1.3.0-beta1' !

Slide 33

Slide 33 text

AnnotationͷϧʔϧΛڧ੍͢Δ ./gradlew lint ! :app:lint FAILED ! FAILURE: Build failed with an exception. ! * What went wrong: Execution failed for task ':app:lint'. > Lint found errors in the project; aborting build.

Slide 34

Slide 34 text

·ͱΊ • ґଘؔ܎Ͱೖͬͯ͘ΔͷͰҙࣝͤͣ؆୯ʹ࢖͑Δ • Android StudioͷதΛ೷͚͹ͳΜͰܯࠂ͕ग़ͳ͍͔෼͔Δ(ͦ͜· Ͱݡ͘ͳ͍) • AnnotationͰઃఆͨ͠ϧʔϧΛڧ੍Ͱ͖Δ • ݁ߏ࠷ۙίʔυ͕มΘ͍͍ͬͯͬͯΔͷͰɺ͜Ε͔Βݡ͘ͳΔ͔ ΋ʁ • (ࣗ෼Ͱ΋ਅࣅͯ͠ϓϥάΠϯͱͯ͠ಉ͡Α͏ͳ΋ͷ͕࡞ΕΔ)

Slide 35

Slide 35 text

Hungarian Inspection Plugin

Slide 36

Slide 36 text

Thanks w "OESPJE5PPMT1SPKFDU4JUF w IUUQUPPMTBOESPJEDPNUFDIEPDTTVQQPSUBOOPUBUJPOT ! w "OESPJE0QFO4PVSDF1SPKFDU w IUUQTBOESPJEHPPHMFTPVSDFDPNQMBUGPSNUPPMTBEUJEFB w IUUQTBOESPJEHPPHMFTPVSDFDPNQMBUGPSNUPPMTCBTF ! w 5IF$POGFSFODF4QFBLFS*OWFTUNFOU w IUUQKBLFXIBSUPODPNUIFDPOGFSFODFTQFBLFSJOWFTUNFOU

Slide 37

Slide 37 text

͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ