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

Android Support Annotations

Android Support Annotations

takahirom

June 16, 2015
Tweet

More Decks by takahirom

Other Decks in Programming

Transcript

  1. 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
  2. ϝιου • @CallSuper superϝιουΛݺͼग़͍ͯ͠ͳ͍ͱܯࠂ • @CheckResult ϝιουͷฦΓ஋Λ࢖͍ͬͯͳ͍ͱܯࠂ • @RequiresPermission ඞཁͳύʔϛογϣϯ͕ͳ͍ͱܯࠂ

    • @Keep ProGuard͕͚͍ͭͯΔϝιουͰແޮʹͳΔ(ࠓ ͸ಈ͔ͳ͍ʁ) • @VisibleForTesting TestͰ࢖͏ϝιου͔ͩΒpublicͳͲ ʹ͍ͯ͠Δͱ͍͏දࣔ
  3. ඪ४ϝιουͷΞϊςʔγϣϯ • Ͳͷϝιου΍ΫϥεʹΞϊςʔγϣϯ͕෇͍͍ͯΔ͔Θ͔ Δ • 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>
  4. 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จͷத͕࣮ߦ͞ΕΔͱܯࠂ͕දࣔ͞Ε·͢
  5. 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จͷத͕࣮ߦ͞ΕΔͱܯࠂ͕දࣔ͞Ε·͢
  6. 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 ͳͷͰ৚݅จΛݟͯߦ͖·͠ΐ͏
  7. 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 ͳͷͰ৚݅จΛݟͯߦ͖·͠ΐ͏
  8. 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ͱ͍͏ϝιουͰνΣοΫ͍ͯ͠ΔΑ͏Ͱ͢
  9. 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ͱ͍͏ϝιουͰνΣοΫ͍ͯ͠ΔΑ͏Ͱ͢
  10. 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ͳͲͷจࣈྻ͕ೖ͍ͬͯ·͢
  11. 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Λฦ͠·͢
  12. 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Λฦ͠·͢
  13. 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Ͱ࣮ߦ͍ͯ͠Δ͔ͷ൑ఆΛݟͯߦ͖·͠ΐ͏
  14. 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Ͱ࣮ߦ͍ͯ͠Δ͔ͷ൑ఆΛݟͯߦ͖·͠ΐ͏
  15. @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;
 }
 } …….
 } ϝιουʹ͍͍ͭͯΔΞϊςʔγϣϯΛऔಘͯͦ͠ΕΛฦ͠·͢
  16. @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 
 } ……. } ϝιουʹ͍͍ͭͯΔΞϊςʔγϣϯΛऔಘͯͦ͠ΕΛฦ͠·͢
  17. @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;
 }
 }
 }
 } ͦΕ͚ͩͰͳ͘ɺΫϥεʹ͍͍ͭͯΔΞϊςʔγϣϯΛऔಘ͠·͢
  18. @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 
 }
 }
 } ͦΕ͚ͩͰͳ͘ɺΫϥεʹ͍͍ͭͯΔΞϊςʔγϣϯΛऔಘ͠·͢
  19. 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);
 } } ͭ·Γݺͼग़͠ݩͷϝιουͱΫϥεͷΞϊςʔγϣϯΛݟ͍ͯ·͢
  20. 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.
  21. ·ͱΊ • ґଘؔ܎Ͱೖͬͯ͘ΔͷͰҙࣝͤͣ؆୯ʹ࢖͑Δ • Android StudioͷதΛ೷͚͹ͳΜͰܯࠂ͕ग़ͳ͍͔෼͔Δ(ͦ͜· Ͱݡ͘ͳ͍) • AnnotationͰઃఆͨ͠ϧʔϧΛڧ੍Ͱ͖Δ •

    ݁ߏ࠷ۙίʔυ͕มΘ͍͍ͬͯͬͯΔͷͰɺ͜Ε͔Βݡ͘ͳΔ͔ ΋ʁ • (ࣗ෼Ͱ΋ਅࣅͯ͠ϓϥάΠϯͱͯ͠ಉ͡Α͏ͳ΋ͷ͕࡞ΕΔ)
  22. Thanks w "OESPJE5PPMT1SPKFDU4JUF w IUUQUPPMTBOESPJEDPNUFDIEPDTTVQQPSUBOOPUBUJPOT ! w "OESPJE0QFO4PVSDF1SPKFDU w IUUQTBOESPJEHPPHMFTPVSDFDPNQMBUGPSNUPPMTBEUJEFB

    w IUUQTBOESPJEHPPHMFTPVSDFDPNQMBUGPSNUPPMTCBTF ! w 5IF$POGFSFODF4QFBLFS*OWFTUNFOU w IUUQKBLFXIBSUPODPNUIFDPOGFSFODFTQFBLFSJOWFTUNFOU