Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Android Support Annotations

Android Support Annotations

7166bc2cbc462ab5fd1987a76d0fe709?s=128

takahirom

June 16, 2015
Tweet

More Decks by takahirom

Other Decks in Programming

Transcript

  1. support library 22.2.0 Annotations takahirom

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

  3. Support Annotations Google I/O What’s New in Android Development Tools

    - 13ݸͷ৽͍͠Ξϊςʔγϣϯͷ௥Ճ
  4. Support Annotations compile 'com.android.support:support-annotations:22.2.0' !

  5. 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
  6. ΞϊςʔγϣϯҰཡ @WorkerThread
 @UiThread @MainThread @BinderThread
 Thread @CallSuper @CheckResult @RequiresPermission @Keep

    @VisibleForTesting Ҿ਺νΣοΫ @Size
 @IntRange @FloatRange @ColorInt ϝιου
  7. Thread Annotations • @WorkerThread • @UiThread • @MainThread • @BinderThread

  8. ྫ:@WorkerThread

  9. Ҿ਺νΣοΫ • @Size จࣈྻ΍഑ྻͷ௕͞ͷ࠷େ஋ ࠷খ஋ͳͲΛઃఆ ߹க ͠ͳ͍ͳΒܯࠂ • @IntRange ਺ࣈͷൣғ

    ൣғ֎ͳΒܯࠂ • @FloatRange float൛ • @ColorInt ৭ͷintͰͳ͚Ε͹ܯࠂ
  10. ྫ:@Size

  11. ϝιου • @CallSuper superϝιουΛݺͼग़͍ͯ͠ͳ͍ͱܯࠂ • @CheckResult ϝιουͷฦΓ஋Λ࢖͍ͬͯͳ͍ͱܯࠂ • @RequiresPermission ඞཁͳύʔϛογϣϯ͕ͳ͍ͱܯࠂ

    • @Keep ProGuard͕͚͍ͭͯΔϝιουͰແޮʹͳΔ(ࠓ ͸ಈ͔ͳ͍ʁ) • @VisibleForTesting TestͰ࢖͏ϝιου͔ͩΒpublicͳͲ ʹ͍ͯ͠Δͱ͍͏දࣔ
  12. ඪ४ϝιουͷΞϊςʔγϣϯ • Ͳͷϝιου΍ΫϥεʹΞϊςʔγϣϯ͕෇͍͍ͯΔ͔Θ͔ Δ • https://android.googlesource.com/platform/tools/adt/ idea/+/master/android/annotations/android/ ! <item name="android.app.Activity

    void onCreate(android.os.Bundle)">
 <annotation name="android.support.annotation.MainThread" />
 <annotation name="android.support.annotation.CallSuper" />
 </item>
  13. @MainThread͕Α͘෼͔Βͳ͍

  14. Android Studio಺ͷThreadνΣοΫ

  15. 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จͷத͕࣮ߦ͞ΕΔͱܯࠂ͕දࣔ͞Ε·͢
  16. 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จͷத͕࣮ߦ͞ΕΔͱܯࠂ͕දࣔ͞Ε·͢
  17. 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 ͳͷͰ৚݅จΛݟͯߦ͖·͠ΐ͏
  18. 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 ͳͷͰ৚݅จΛݟͯߦ͖·͠ΐ͏
  19. 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ͱ͍͏ϝιουͰνΣοΫ͍ͯ͠ΔΑ͏Ͱ͢
  20. 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ͱ͍͏ϝιουͰνΣοΫ͍ͯ͠ΔΑ͏Ͱ͢
  21. 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ͳͲͷจࣈྻ͕ೖ͍ͬͯ·͢
  22. 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Λฦ͠·͢
  23. 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Λฦ͠·͢
  24. 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Ͱ࣮ߦ͍ͯ͠Δ͔ͷ൑ఆΛݟͯߦ͖·͠ΐ͏
  25. 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Ͱ࣮ߦ͍ͯ͠Δ͔ͷ൑ఆΛݟͯߦ͖·͠ΐ͏
  26. @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;
 }
 } …….
 } ϝιουʹ͍͍ͭͯΔΞϊςʔγϣϯΛऔಘͯͦ͠ΕΛฦ͠·͢
  27. @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 
 } ……. } ϝιουʹ͍͍ͭͯΔΞϊςʔγϣϯΛऔಘͯͦ͠ΕΛฦ͠·͢
  28. @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;
 }
 }
 }
 } ͦΕ͚ͩͰͳ͘ɺΫϥεʹ͍͍ͭͯΔΞϊςʔγϣϯΛऔಘ͠·͢
  29. @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 
 }
 }
 } ͦΕ͚ͩͰͳ͘ɺΫϥεʹ͍͍ͭͯΔΞϊςʔγϣϯΛऔಘ͠·͢
  30. 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);
 } } ͭ·Γݺͼग़͠ݩͷϝιουͱΫϥεͷΞϊςʔγϣϯΛݟ͍ͯ·͢
  31. @WorkerThreadΛ͚ͭΕ͹OK

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

  33. 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.
  34. ·ͱΊ • ґଘؔ܎Ͱೖͬͯ͘ΔͷͰҙࣝͤͣ؆୯ʹ࢖͑Δ • Android StudioͷதΛ೷͚͹ͳΜͰܯࠂ͕ग़ͳ͍͔෼͔Δ(ͦ͜· Ͱݡ͘ͳ͍) • AnnotationͰઃఆͨ͠ϧʔϧΛڧ੍Ͱ͖Δ •

    ݁ߏ࠷ۙίʔυ͕มΘ͍͍ͬͯͬͯΔͷͰɺ͜Ε͔Βݡ͘ͳΔ͔ ΋ʁ • (ࣗ෼Ͱ΋ਅࣅͯ͠ϓϥάΠϯͱͯ͠ಉ͡Α͏ͳ΋ͷ͕࡞ΕΔ)
  35. Hungarian Inspection Plugin

  36. Thanks w "OESPJE5PPMT1SPKFDU4JUF w IUUQUPPMTBOESPJEDPNUFDIEPDTTVQQPSUBOOPUBUJPOT ! w "OESPJE0QFO4PVSDF1SPKFDU w IUUQTBOESPJEHPPHMFTPVSDFDPNQMBUGPSNUPPMTBEUJEFB

    w IUUQTBOESPJEHPPHMFTPVSDFDPNQMBUGPSNUPPMTCBTF ! w 5IF$POGFSFODF4QFBLFS*OWFTUNFOU w IUUQKBLFXIBSUPODPNUIFDPOGFSFODFTQFBLFSJOWFTUNFOU
  37. ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ