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

Android Support Annotations

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

Android Support Annotations

Avatar for takahirom

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