Thursday, 29 November 2018

How to create a Lint check to ensure an Activity/Fragment is attached?

I am trying to create a custom Lint check so that particular annotated methods have to first ensure that the fragment/activity is attached/visible before doing any work.

E.g.

class MyFragment extends Fragment {

    @CheckIsActive
    void lint_fail() {
        // This would throw a lint warning
    }

    @CheckIsActive
    void lint_succeed() {
        if(isAdded()) {
            // This wouldn't throw a lint warning
        }
    }

}

To do this, I have created an IssueRegistry, the @CheckIsActive annotation and the following custom Detector class.

public class CheckActiveDetector extends Detector implements Detector.UastScanner {

    private static final String CHECK_ACTIVE_ANNOTATION = Constants.ANNOTATIONS_PREFIX + "CheckIsActive";

    private static final Implementation IMPLEMENTATION = new Implementation(
            CheckActiveDetector.class,
            Scope.JAVA_FILE_SCOPE);

    public static final Issue ISSUE = Issue.create(
            "CheckActive",
            "Method should check if the activity/fragment is active",
            "This method should ensure the Activity/Fragment is active before continuing",
            Category.CORRECTNESS,
            9,
            Severity.WARNING,
            IMPLEMENTATION
    );


    public CheckActiveDetector() {}

    @Nullable
    @Override
    public List<Class<? extends UElement>> getApplicableUastTypes() {
        return Collections.<Class<? extends UElement>>singletonList(UMethod.class);
    }

    @Nullable
    @Override
    public UElementHandler createUastHandler(@NotNull final JavaContext context) {
        return new UElementHandler() {
            @Override
            public void visitMethod(@NotNull UMethod node) {


                UExpression body = node.getUastBody();
                if(body != null && node.findAnnotation(CHECK_ACTIVE_ANNOTATION) != null) {

                    String methodName = node.getName();
                    String message = "Overriding method should call `isAdded()"
                            + methodName + "`";
                    Location location = context.getLocation(node);
                    context.report(ISSUE, node, location, message);

                }
            }
        };
    }
}

I have struggled to work out how to progress from the state I am in now, which is (I believe) entering a method and correctly checking if it was annotated. I am not sure with the detector methods, how to

  1. Check if the containing class extends Activity / Fragment
  2. Check if isAdded() for Fragment or isDestroyed() for Activity was called

Does anyone have any idea how to carry on from this or know where any of this is documented? It seems Google is not using the latest lint version within their source (e.g. CallSuper source) so that has not been too much help.

Thanks



from How to create a Lint check to ensure an Activity/Fragment is attached?

No comments:

Post a Comment