aboutsummaryrefslogtreecommitdiffstats
path: root/lint
diff options
context:
space:
mode:
authorTor Norbye <tnorbye@google.com>2012-02-13 14:45:29 -0800
committerTor Norbye <tnorbye@google.com>2012-02-13 22:02:22 -0800
commitff1646120b0fde2313d2686d025c28082993933d (patch)
treed97a6300e71e863a5622b5be32523d243cdc628b /lint
parentb13f9f440e83ad9a17abd13b3df25f49efd436ac (diff)
downloadsdk-ff1646120b0fde2313d2686d025c28082993933d.tar.gz
sdk-ff1646120b0fde2313d2686d025c28082993933d.tar.bz2
sdk-ff1646120b0fde2313d2686d025c28082993933d.zip
Add Java performance lint checks
This changeset adds two new checks for Java performance suggestions: (1) It identifies object allocation within draw methods (onDraw(Canvas)) and warns that it's a bad idea. It also looks for certain factory methods, such as Bitmap.createBitmap(), which have the same issue. The lint detector also checks to make sure that the allocation isn't part of lazy initialization: if the return value is stored into a field, it checks that the allocation is surrounded by a null check on that field. (2) It identifies usages of HashMap<Integer, X> and suggests replacing it with SparseArray (or SparseIntArray or SparseBooleanArray, depending on the second type variable of the map initialization). Change-Id: I3bcbb182404b1eeaaa8c9e5209d889694d88f216
Diffstat (limited to 'lint')
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java4
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/FieldGetterDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/GridLayoutDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/IconDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/JavaPerformanceDetector.java445
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/ManifestOrderDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/MathDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/SdCardDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/SecurityDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/StringFormatDetector.java5
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/ViewConstructorDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/WrongImportDetector.java2
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java45
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/src/test/pkg/JavaPerformanceTest.java.txt147
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/wrongid/ignorelayout1.xml2
15 files changed, 653 insertions, 13 deletions
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java
index 67aabdb18..5a8bf9f97 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java
@@ -53,7 +53,7 @@ public class BuiltinIssueRegistry extends IssueRegistry {
private static final List<Issue> sIssues;
static {
- List<Issue> issues = new ArrayList<Issue>(60);
+ List<Issue> issues = new ArrayList<Issue>(75);
issues.add(AccessibilityDetector.ISSUE);
issues.add(MathDetector.ISSUE);
@@ -127,6 +127,8 @@ public class BuiltinIssueRegistry extends IssueRegistry {
issues.add(ViewConstructorDetector.ISSUE);
issues.add(TypoDetector.ISSUE);
issues.add(AlwaysShowActionDetector.ISSUE);
+ issues.add(JavaPerformanceDetector.PAINT_ALLOC);
+ issues.add(JavaPerformanceDetector.USE_SPARSEARRAY);
addCustomIssues(issues);
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/FieldGetterDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/FieldGetterDetector.java
index 8d401e1af..8bbc749c6 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/FieldGetterDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/FieldGetterDetector.java
@@ -68,7 +68,7 @@ public class FieldGetterDetector extends Detector implements Detector.ClassScann
setEnabledByDefault(false).setMoreInfo(
"http://developer.android.com/guide/practices/design/performance.html#internal_get_set"); //$NON-NLS-1$
- /** Constructs a new accessibility check */
+ /** Constructs a new {@link FieldGetterDetector} check */
public FieldGetterDetector() {
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/GridLayoutDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/GridLayoutDetector.java
index 524b8c640..762a4b59d 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/GridLayoutDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/GridLayoutDetector.java
@@ -55,7 +55,7 @@ public class GridLayoutDetector extends LayoutDetector {
GridLayoutDetector.class,
Scope.RESOURCE_FILE_SCOPE);
- /** Constructs a new accessibility check */
+ /** Constructs a new {@link GridLayoutDetector} check */
public GridLayoutDetector() {
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/IconDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/IconDetector.java
index 00df53c7f..42ed1b8e8 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/IconDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/IconDetector.java
@@ -253,7 +253,7 @@ public class IconDetector extends Detector implements Detector.XmlScanner {
private String mApplicationIcon;
- /** Constructs a new accessibility check */
+ /** Constructs a new {@link IconDetector} check */
public IconDetector() {
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/JavaPerformanceDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/JavaPerformanceDetector.java
new file mode 100644
index 000000000..7e017e170
--- /dev/null
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/JavaPerformanceDetector.java
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.checks;
+
+import com.android.tools.lint.detector.api.Category;
+import com.android.tools.lint.detector.api.Context;
+import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.JavaContext;
+import com.android.tools.lint.detector.api.Scope;
+import com.android.tools.lint.detector.api.Severity;
+import com.android.tools.lint.detector.api.Speed;
+import com.google.common.collect.Sets;
+import com.google.common.collect.Sets.SetView;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import lombok.ast.AstVisitor;
+import lombok.ast.BinaryExpression;
+import lombok.ast.BinaryOperator;
+import lombok.ast.ConstructorInvocation;
+import lombok.ast.Expression;
+import lombok.ast.ForwardingAstVisitor;
+import lombok.ast.If;
+import lombok.ast.MethodDeclaration;
+import lombok.ast.MethodInvocation;
+import lombok.ast.Node;
+import lombok.ast.StrictListAccessor;
+import lombok.ast.Throw;
+import lombok.ast.TypeReference;
+import lombok.ast.TypeReferencePart;
+import lombok.ast.UnaryExpression;
+import lombok.ast.VariableDefinition;
+import lombok.ast.VariableReference;
+
+/**
+ * Looks for performance issues in Java files, such as memory allocations during
+ * drawing operations and using HashMap instead of SparseArray.
+ */
+public class JavaPerformanceDetector extends Detector implements Detector.JavaScanner {
+ /** Allocating objects during a paint method */
+ public static final Issue PAINT_ALLOC = Issue.create(
+ "DrawAllocation", //$NON-NLS-1$
+ "Looks for memory allocations within drawing code",
+
+ "You should avoid allocating objects during a drawing or layout operation. These " +
+ "are called frequently, so a smooth UI can be interrupted by garbage collection " +
+ "pauses caused by the object allocations.\n" +
+ "\n" +
+ "The way this is generally handled is to allocate the needed objects up front " +
+ "and to reuse them for each drawing operation.\n" +
+ "\n" +
+ "Some methods allocate memory on your behalf (such as Bitmap.create), and these " +
+ "should be handled in the same way.",
+
+ Category.PERFORMANCE,
+ 9,
+ Severity.WARNING,
+ JavaPerformanceDetector.class,
+ Scope.JAVA_FILE_SCOPE);
+
+ /** Using HashMaps where SparseArray would be better */
+ public static final Issue USE_SPARSEARRAY = Issue.create(
+ "UseSparseArrays", //$NON-NLS-1$
+ "Looks for opportunities to replace HashMaps with the more efficient SparseArray",
+
+ "For maps where the keys are of type integer, it's typically more efficient to " +
+ "use the Android SparseArray API. This check identifies scenarios where you might " +
+ "want to consider using SparseArray instead of HashMap for better performance.\n" +
+ "\n" +
+ "This is *particularly* useful when the value types are primitives like ints, " +
+ "where you can use SparseIntArray and avoid auto-boxing the values from int to " +
+ "Integer.\n" +
+ "\n" +
+ "If you need to construct a HashMap because you need to call an API outside of " +
+ "your control which requires a Map, you can suppress this warning using for " +
+ "example the @SuppressLint annotation.",
+
+ Category.PERFORMANCE,
+ 4,
+ Severity.WARNING,
+ JavaPerformanceDetector.class,
+ Scope.JAVA_FILE_SCOPE);
+
+ private static final String INT = "int"; //$NON-NLS-1$
+ private static final String INTEGER = "Integer"; //$NON-NLS-1$
+ private static final String BOOL = "boolean"; //$NON-NLS-1$
+ private static final String BOOLEAN = "Boolean"; //$NON-NLS-1$
+ private static final String LONG = "Long"; //$NON-NLS-1$
+ private static final String HASH_MAP = "HashMap"; //$NON-NLS-1$
+ private static final String CANVAS = "Canvas"; //$NON-NLS-1$
+ private static final String ON_DRAW = "onDraw"; //$NON-NLS-1$
+ private static final String ON_LAYOUT = "onLayout"; //$NON-NLS-1$
+ private static final String ON_MEASURE = "onMeasure"; //$NON-NLS-1$
+
+ /** Constructs a new {@link JavaPerformanceDetector} check */
+ public JavaPerformanceDetector() {
+ }
+
+ @Override
+ public boolean appliesTo(Context context, File file) {
+ return true;
+ }
+
+ @Override
+ public Speed getSpeed() {
+ return Speed.FAST;
+ }
+
+ // ---- Implements JavaScanner ----
+
+ @Override
+ public List<Class<? extends Node>> getApplicableNodeTypes() {
+ List<Class<? extends Node>> types = new ArrayList<Class<? extends Node>>(3);
+ types.add(ConstructorInvocation.class);
+ types.add(MethodDeclaration.class);
+ types.add(MethodInvocation.class);
+ return types;
+ }
+
+ @Override
+ public AstVisitor createJavaVisitor(JavaContext context) {
+ return new PerformanceVisitor(context);
+ }
+
+ private static class PerformanceVisitor extends ForwardingAstVisitor {
+ private final JavaContext mContext;
+ /** Whether allocations should be "flagged" in the current method */
+ private boolean mFlagAllocations;
+
+ public PerformanceVisitor(JavaContext context) {
+ mContext = context;
+ }
+
+ @Override
+ public boolean visitMethodDeclaration(MethodDeclaration node) {
+ mFlagAllocations = isBlockedAllocationMethod(node);
+
+ return super.visitMethodDeclaration(node);
+ }
+
+ @Override
+ public boolean visitConstructorInvocation(ConstructorInvocation node) {
+ TypeReference reference = node.astTypeReference();
+ String typeName = reference.astParts().last().astIdentifier().astValue();
+ // TODO: Should we handle factory method constructions of HashMaps as well,
+ // e.g. via Guava? This is a bit trickier since we need to infer the type
+ // arguments from the calling context.
+ if (typeName.equals(HASH_MAP)) {
+ checkSparseArray(node, reference);
+ }
+
+ if (mFlagAllocations && !(node.getParent() instanceof Throw)) {
+ // Make sure we're still inside the method declaration that marked
+ // mInDraw as true, in case we've left it and we're in a static
+ // block or something:
+ Node method = node;
+ while (method != null) {
+ if (method instanceof MethodDeclaration) {
+ break;
+ }
+ method = method.getParent();
+ }
+ if (method != null && isBlockedAllocationMethod(((MethodDeclaration) method))
+ && !isLazilyInitialized(node)) {
+ reportAllocation(node);
+ }
+ }
+
+ return super.visitConstructorInvocation(node);
+ }
+
+ private void reportAllocation(Node node) {
+ mContext.report(PAINT_ALLOC, node, mContext.getLocation(node),
+ "Avoid object allocations during draw/layout operations (preallocate and " +
+ "reuse insteaD)", null);
+ }
+
+ @Override
+ public boolean visitMethodInvocation(MethodInvocation node) {
+ if (mFlagAllocations) {
+ // Look for forbidden methods
+ String methodName = node.astName().astValue();
+ if (methodName.equals("createBitmap") //$NON-NLS-1$
+ || methodName.equals("createScaledBitmap")) { //$NON-NLS-1$
+ String operand = node.astOperand().toString();
+ if (operand.equals("Bitmap") //$NON-NLS-1$
+ || operand.equals("android.graphics.Bitmap")) { //$NON-NLS-1$
+ if (!isLazilyInitialized(node)) {
+ reportAllocation(node);
+ }
+ }
+ } else if (methodName.startsWith("decode")) { //$NON-NLS-1$
+ // decodeFile, decodeByteArray, ...
+ String operand = node.astOperand().toString();
+ if (operand.equals("BitmapFactory") //$NON-NLS-1$
+ || operand.equals("android.graphics.BitmapFactory")) { //$NON-NLS-1$
+ if (!isLazilyInitialized(node)) {
+ reportAllocation(node);
+ }
+ }
+ } else if (methodName.equals("getClipBounds")) { //$NON-NLS-1$
+ if (node.astArguments().isEmpty()) {
+ mContext.report(PAINT_ALLOC, node, mContext.getLocation(node),
+ "Avoid object allocations during draw operations: Use " +
+ "Canvas.getClipBounds(Rect) instead of Canvas.getClipBounds() " +
+ "which allocates a temporary Rect", null);
+ }
+ }
+ }
+
+ return super.visitMethodInvocation(node);
+ }
+
+ /**
+ * Check whether the given invocation is done as a lazy initialization,
+ * e.g. {@code if (foo == null) foo = new Foo();}.
+ * <p>
+ * This tries to also handle the scenario where the check is on some
+ * <b>other</b> variable - e.g.
+ * <pre>
+ * if (foo == null) {
+ * foo == init1();
+ * bar = new Bar();
+ * }
+ * </pre>
+ * or
+ * <pre>
+ * if (!initialized) {
+ * initialized = true;
+ * bar = new Bar();
+ * }
+ * </pre>
+ */
+ private boolean isLazilyInitialized(Node node) {
+ Node curr = node.getParent();
+ while (curr != null) {
+ if (curr instanceof MethodDeclaration) {
+ return false;
+ } else if (curr instanceof If) {
+ If ifNode = (If) curr;
+ // See if the if block represents a lazy initialization:
+ // compute all variable names seen in the condition
+ // (e.g. for "if (foo == null || bar != foo)" the result is "foo,bar"),
+ // and then compute all variables assigned to in the if body,
+ // and if there is an overlap, we'll consider the whole if block
+ // guarded (so lazily initialized and an allocation we won't complain
+ // about.)
+ List<String> assignments = new ArrayList<String>();
+ AssignmentTracker visitor = new AssignmentTracker(assignments);
+ ifNode.astStatement().accept(visitor);
+ if (assignments.size() > 0) {
+ List<String> references = new ArrayList<String>();
+ addReferencedVariables(references, ifNode.astCondition());
+ if (references.size() > 0) {
+ SetView<String> intersection = Sets.intersection(
+ new HashSet<String>(assignments),
+ new HashSet<String>(references));
+ return intersection.size() > 0;
+ }
+ }
+ return false;
+
+ }
+ curr = curr.getParent();
+ }
+
+ return false;
+ }
+
+ /** Adds any variables referenced in the given expression into the given list */
+ private static void addReferencedVariables(Collection<String> variables,
+ Expression expression) {
+ if (expression instanceof BinaryExpression) {
+ BinaryExpression binary = (BinaryExpression) expression;
+ addReferencedVariables(variables, binary.astLeft());
+ addReferencedVariables(variables, binary.astRight());
+ } else if (expression instanceof UnaryExpression) {
+ UnaryExpression unary = (UnaryExpression) expression;
+ addReferencedVariables(variables, unary.astOperand());
+ } else if (expression instanceof VariableReference) {
+ VariableReference reference = (VariableReference) expression;
+ variables.add(reference.astIdentifier().astValue());
+ }
+ }
+
+ /**
+ * Returns whether the given method declaration represents a method
+ * where allocating objects is not allowed for performance reasons
+ */
+ private boolean isBlockedAllocationMethod(MethodDeclaration node) {
+ return isOnDrawMethod(node) || isOnMeasureMethod(node) || isOnLayoutMethod(node);
+ }
+
+ /**
+ * Returns true if this method looks like it's overriding android.view.View's
+ * {@code protected void onDraw(Canvas canvas)}
+ */
+ private static boolean isOnDrawMethod(MethodDeclaration node) {
+ if (ON_DRAW.equals(node.astMethodName().astValue())) {
+ StrictListAccessor<VariableDefinition, MethodDeclaration> parameters =
+ node.astParameters();
+ if (parameters != null && parameters.size() == 1) {
+ VariableDefinition arg0 = parameters.first();
+ TypeReferencePart type = arg0.astTypeReference().astParts().last();
+ String typeName = type.getTypeName();
+ if (typeName.equals(CANVAS)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true if this method looks like it's overriding
+ * android.view.View's
+ * {@code protected void onLayout(boolean changed, int left, int top,
+ * int right, int bottom)}
+ */
+ private static boolean isOnLayoutMethod(MethodDeclaration node) {
+ if (ON_LAYOUT.equals(node.astMethodName().astValue())) {
+ StrictListAccessor<VariableDefinition, MethodDeclaration> parameters =
+ node.astParameters();
+ if (parameters != null && parameters.size() == 5) {
+ Iterator<VariableDefinition> iterator = parameters.iterator();
+ if (!iterator.hasNext()) {
+ return false;
+ }
+
+ // Ensure that the argument list matches boolean, int, int, int, int
+ TypeReferencePart type = iterator.next().astTypeReference().astParts().last();
+ if (!type.getTypeName().equals(BOOL) || !iterator.hasNext()) {
+ return false;
+ }
+ for (int i = 0; i < 4; i++) {
+ type = iterator.next().astTypeReference().astParts().last();
+ if (!type.getTypeName().equals(INT)) {
+ return false;
+ }
+ if (!iterator.hasNext()) {
+ return i == 3;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true if this method looks like it's overriding android.view.View's
+ * {@code protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)}
+ */
+ private static boolean isOnMeasureMethod(MethodDeclaration node) {
+ if (ON_MEASURE.equals(node.astMethodName().astValue())) {
+ StrictListAccessor<VariableDefinition, MethodDeclaration> parameters =
+ node.astParameters();
+ if (parameters != null && parameters.size() == 2) {
+ VariableDefinition arg0 = parameters.first();
+ VariableDefinition arg1 = parameters.last();
+ TypeReferencePart type1 = arg0.astTypeReference().astParts().last();
+ TypeReferencePart type2 = arg1.astTypeReference().astParts().last();
+ return INT.equals(type1.getTypeName()) && INT.equals(type2.getTypeName());
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks whether the given constructor call and type reference refers
+ * to a HashMap constructor call that is eligible for replacement by a
+ * SparseArray call instead
+ */
+ private void checkSparseArray(ConstructorInvocation node, TypeReference reference) {
+ // reference.hasTypeArguments returns false where it should not
+ StrictListAccessor<TypeReference, TypeReference> types = reference.getTypeArguments();
+ if (types != null && types.size() == 2) {
+ TypeReference first = types.first();
+ if (first.getTypeName().equals(INTEGER)) {
+ String valueType = types.last().getTypeName();
+ if (valueType.equals(INTEGER)) {
+ mContext.report(USE_SPARSEARRAY, node, mContext.getLocation(node),
+ "Use new SparseIntArray(...) instead for better performance",
+ null);
+ } else if (valueType.equals(BOOLEAN)) {
+ mContext.report(USE_SPARSEARRAY, node, mContext.getLocation(node),
+ "Use new SparseBooleanArray(...) instead for better performance",
+ null);
+ } else if (valueType.equals(LONG) && mContext.getProject().getMinSdk() >= 17) {
+ mContext.report(USE_SPARSEARRAY, node, mContext.getLocation(node),
+ "Use new SparseLongArray(...) instead for better performance",
+ null);
+ } else {
+ mContext.report(USE_SPARSEARRAY, node, mContext.getLocation(node),
+ String.format(
+ "Use new SparseArray<%1$s>(...) instead for better performance",
+ valueType),
+ null);
+ }
+ }
+ }
+ }
+ }
+
+ /** Visitor which records variable names assigned into */
+ private static class AssignmentTracker extends ForwardingAstVisitor {
+ private final Collection<String> mVariables;
+
+ public AssignmentTracker(Collection<String> variables) {
+ mVariables = variables;
+ }
+
+ @Override
+ public boolean visitBinaryExpression(BinaryExpression node) {
+ BinaryOperator operator = node.astOperator();
+ if (operator == BinaryOperator.ASSIGN || operator == BinaryOperator.OR_ASSIGN) {
+ mVariables.add(node.astLeft().toString());
+ }
+
+ return super.visitBinaryExpression(node);
+ }
+ }
+}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ManifestOrderDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ManifestOrderDetector.java
index 144a1916d..8009bf04b 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ManifestOrderDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ManifestOrderDetector.java
@@ -80,7 +80,7 @@ public class ManifestOrderDetector extends Detector implements Detector.XmlScann
EnumSet.of(Scope.MANIFEST)).setMoreInfo(
"http://developer.android.com/guide/topics/manifest/uses-sdk-element.html"); //$NON-NLS-1$
- /** Constructs a new accessibility check */
+ /** Constructs a new {@link ManifestOrderDetector} check */
public ManifestOrderDetector() {
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/MathDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/MathDetector.java
index 5266cc4ce..3c8d5d16d 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/MathDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/MathDetector.java
@@ -63,7 +63,7 @@ public class MathDetector extends Detector implements Detector.ClassScanner {
//"http://developer.android.com/reference/android/util/FloatMath.html"); //$NON-NLS-1$
"http://developer.android.com/guide/practices/design/performance.html#avoidfloat"); //$NON-NLS-1$
- /** Constructs a new accessibility check */
+ /** Constructs a new {@link MathDetector} check */
public MathDetector() {
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/SdCardDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/SdCardDetector.java
index 1a765b83c..b239d5749 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/SdCardDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/SdCardDetector.java
@@ -54,7 +54,7 @@ public class SdCardDetector extends Detector implements Detector.JavaScanner {
Scope.JAVA_FILE_SCOPE).setMoreInfo(
"http://developer.android.com/guide/topics/data/data-storage.html#filesExternal"); //$NON-NLS-1$
- /** Constructs a new accessibility check */
+ /** Constructs a new {@link SdCardDetector} check */
public SdCardDetector() {
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/SecurityDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/SecurityDetector.java
index 149313181..f19f8aa91 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/SecurityDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/SecurityDetector.java
@@ -107,7 +107,7 @@ public class SecurityDetector extends Detector implements Detector.XmlScanner,
SecurityDetector.class,
Scope.JAVA_FILE_SCOPE);
- /** Constructs a new accessibility check */
+ /** Constructs a new {@link SecurityDetector} check */
public SecurityDetector() {
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/StringFormatDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/StringFormatDetector.java
index ac3a7a3ad..665a43b9f 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/StringFormatDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/StringFormatDetector.java
@@ -75,7 +75,8 @@ import lombok.ast.VariableDefinitionEntry;
import lombok.ast.VariableReference;
/**
- * Check which looks for accessibility problems like missing content descriptions
+ * Check which looks for problems with formatting strings such as inconsistencies between
+ * translations or between string declaration and string usage in Java.
*/
public class StringFormatDetector extends ResourceXmlDetector implements Detector.JavaScanner {
/** The name of the String.format method */
@@ -156,7 +157,7 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto
*/
private Map<String, Handle> mNotFormatStrings = new HashMap<String, Handle>();
- /** Constructs a new accessibility check */
+ /** Constructs a new {@link StringFormatDetector} check */
public StringFormatDetector() {
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ViewConstructorDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ViewConstructorDetector.java
index b25a4bac4..e5d1e7e7d 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ViewConstructorDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ViewConstructorDetector.java
@@ -67,7 +67,7 @@ public class ViewConstructorDetector extends Detector implements Detector.ClassS
ViewConstructorDetector.class,
EnumSet.of(Scope.CLASS_FILE));
- /** Constructs a new accessibility check */
+ /** Constructs a new {@link ViewConstructorDetector} check */
public ViewConstructorDetector() {
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/WrongImportDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/WrongImportDetector.java
index 59e3314cd..2aa416cd8 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/WrongImportDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/WrongImportDetector.java
@@ -62,7 +62,7 @@ public class WrongImportDetector extends Detector implements Detector.JavaScanne
WrongImportDetector.class,
Scope.JAVA_FILE_SCOPE);
- /** Constructs a new accessibility check */
+ /** Constructs a new {@link WrongImportDetector} check */
public WrongImportDetector() {
}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java
new file mode 100644
index 000000000..7853ddcd0
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tools.lint.checks;
+
+import com.android.tools.lint.detector.api.Detector;
+
+@SuppressWarnings("javadoc")
+public class JavaPerformanceDetectorTest extends AbstractCheckTest {
+ @Override
+ protected Detector getDetector() {
+ return new JavaPerformanceDetector();
+ }
+
+ public void test() throws Exception {
+ assertEquals(
+ "JavaPerformanceTest.java:103: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse insteaD)\n" +
+ "JavaPerformanceTest.java:109: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse insteaD)\n" +
+ "JavaPerformanceTest.java:112: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse insteaD)\n" +
+ "JavaPerformanceTest.java:113: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse insteaD)\n" +
+ "JavaPerformanceTest.java:114: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse insteaD)\n" +
+ "JavaPerformanceTest.java:116: Warning: Avoid object allocations during draw operations: Use Canvas.getClipBounds(Rect) instead of Canvas.getClipBounds() which allocates a temporary Rect\n" +
+ "JavaPerformanceTest.java:140: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse insteaD)\n" +
+ "JavaPerformanceTest.java:28: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse insteaD)\n" +
+ "JavaPerformanceTest.java:29: Warning: Avoid object allocations during draw/layout operations (preallocate and reuse insteaD)\n" +
+ "JavaPerformanceTest.java:70: Warning: Use new SparseArray<String>(...) instead for better performance\n" +
+ "JavaPerformanceTest.java:72: Warning: Use new SparseBooleanArray(...) instead for better performance\n" +
+ "JavaPerformanceTest.java:74: Warning: Use new SparseIntArray(...) instead for better performance",
+
+ lintProject("src/test/pkg/JavaPerformanceTest.java.txt=>" +
+ "src/test/pkg/JavaPerformanceTest.java"));
+ }
+}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/src/test/pkg/JavaPerformanceTest.java.txt b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/src/test/pkg/JavaPerformanceTest.java.txt
new file mode 100644
index 000000000..5cd99d9d1
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/src/test/pkg/JavaPerformanceTest.java.txt
@@ -0,0 +1,147 @@
+package test.pkg;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.widget.Button;
+/** Some test data for the JavaPerformanceDetector */
+@SuppressWarnings("unused")
+public class JavaPerformanceTest extends Button {
+ public JavaPerformanceTest(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ private Rect cachedRect;
+
+ @Override
+ protected void onDraw(android.graphics.Canvas canvas) {
+ super.onDraw(canvas);
+
+ // Various allocations:
+ new String("foo");
+ String s = new String("bar");
+
+ // This one should not be reported:
+ @SuppressLint("DrawAllocation")
+ Integer i = new Integer(5);
+
+ // Cached object initialized lazily: should not complain about these
+ if (cachedRect == null) {
+ cachedRect = new Rect(0, 0, 100, 100);
+ }
+ if (cachedRect == null || cachedRect.width() != 50) {
+ cachedRect = new Rect(0, 0, 50, 100);
+ }
+
+ boolean b = Boolean.valueOf(true); // auto-boxing
+ dummy(1, 2);
+
+ // Non-allocations
+ super.animate();
+ dummy2(1, 2);
+ int x = 4 + '5';
+
+ // This will involve allocations, but we don't track
+ // inter-procedural stuff here
+ someOtherMethod();
+ }
+
+ void dummy(Integer foo, int bar) {
+ dummy2(foo, bar);
+ }
+
+ void dummy2(int foo, int bar) {
+ }
+
+ void someOtherMethod() {
+ // Allocations are okay here
+ new String("foo");
+ String s = new String("bar");
+ boolean b = Boolean.valueOf(true); // auto-boxing
+
+ // Sparse array candidates
+ Map<Integer, String> myMap = new HashMap<Integer, String>();
+ // Should use SparseBooleanArray
+ Map<Integer, Boolean> myBoolMap = new HashMap<Integer, Boolean>();
+ // Should use SparseIntArray
+ Map<Integer, Integer> myIntMap = new java.util.HashMap<Integer, Integer>();
+
+ // This one should not be reported:
+ @SuppressLint("UseSparseArrays")
+ Map<Integer, Object> myOtherMap = new HashMap<Integer, Object>();
+ }
+
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec,
+ boolean x) { // wrong signature
+ new String("not an error");
+ }
+
+ protected void onMeasure(int widthMeasureSpec) { // wrong signature
+ new String("not an error");
+ }
+
+ protected void onLayout(boolean changed, int left, int top, int right,
+ int bottom, int wrong) { // wrong signature
+ new String("not an error");
+ }
+
+ protected void onLayout(boolean changed, int left, int top, int right) {
+ // wrong signature
+ new String("not an error");
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right,
+ int bottom) {
+ new String("flag me");
+ }
+
+ @SuppressWarnings("null") // not real code
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ new String("flag me");
+
+ // Forbidden factory methods:
+ Bitmap.createBitmap(100, 100, null);
+ android.graphics.Bitmap.createScaledBitmap(null, 100, 100, false);
+ BitmapFactory.decodeFile(null);
+ Canvas canvas = null;
+ canvas.getClipBounds(); // allocates on your behalf
+ canvas.getClipBounds(null); // NOT an error
+
+ final int layoutWidth = getWidth();
+ final int layoutHeight = getHeight();
+ if (mAllowCrop && (mOverlay == null || mOverlay.getWidth() != layoutWidth ||
+ mOverlay.getHeight() != layoutHeight)) {
+ mOverlay = Bitmap.createBitmap(layoutWidth, layoutHeight, Bitmap.Config.ARGB_8888);
+ mOverlayCanvas = new Canvas(mOverlay);
+ }
+
+ if (widthMeasureSpec == 42) {
+ throw new IllegalStateException("Test"); // NOT an allocation
+ }
+
+ // More lazy init tests
+ boolean initialized = false;
+ if (!initialized) {
+ new String("foo");
+ initialized = true;
+ }
+
+ // NOT lazy initialization
+ if (!initialized || mOverlay == null) {
+ new String("foo");
+ }
+}
+
+ private boolean mAllowCrop;
+ private Canvas mOverlayCanvas;
+ private Bitmap mOverlay;
+}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/wrongid/ignorelayout1.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/wrongid/ignorelayout1.xml
index deca8b382..bc6d5fd86 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/wrongid/ignorelayout1.xml
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/wrongid/ignorelayout1.xml
@@ -18,7 +18,7 @@
android:layout_alignRight="@+id/my_id3"
android:layout_alignTop="@+id/my_id1"
android:text="Button"
- tools:ignore="UnknownIdInLayout" />
+ tools:ignore="UnknownIdInLayout,UnknownId" />
<Button
android:id="@+id/button2"