// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintDrawAllocationInspection // INSPECTION_CLASS2: com.android.tools.idea.lint.AndroidLintUseSparseArraysInspection // INSPECTION_CLASS3: com.android.tools.idea.lint.AndroidLintUseValueOfInspection import android.annotation.SuppressLint import java.util.HashMap import android.content.Context import android.graphics.* import android.util.AttributeSet import android.util.SparseArray import android.widget.Button @SuppressWarnings("unused") @Suppress("UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION", "UNNECESSARY_NOT_NULL_ASSERTION", "NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS") class JavaPerformanceTest(context: Context, attrs: AttributeSet, defStyle: Int) : Button(context, attrs, defStyle) { private var cachedRect: Rect? = null private var shader: LinearGradient? = null private var lastHeight: Int = 0 private var lastWidth: Int = 0 override fun onDraw(canvas: android.graphics.Canvas) { super.onDraw(canvas) // Various allocations: java.lang.String("foo") val s = java.lang.String("bar") // This one should not be reported: @SuppressLint("DrawAllocation") val i = 5 // Cached object initialized lazily: should not complain about these if (cachedRect == null) { cachedRect = Rect(0, 0, 100, 100) } if (cachedRect == null || cachedRect!!.width() != 50) { cachedRect = Rect(0, 0, 50, 100) } val b = java.lang.Boolean.valueOf(true)!! // auto-boxing dummy(1, 2) // Non-allocations super.animate() dummy2(1, 2) // This will involve allocations, but we don't track // inter-procedural stuff here someOtherMethod() } internal fun dummy(foo: Int?, bar: Int) { dummy2(foo!!, bar) } internal fun dummy2(foo: Int, bar: Int) { } internal fun someOtherMethod() { // Allocations are okay here java.lang.String("foo") val s = java.lang.String("bar") val b = java.lang.Boolean.valueOf(true)!! // auto-boxing // Sparse array candidates val myMap = HashMap() // Should use SparseBooleanArray val myBoolMap = HashMap() // Should use SparseIntArray val myIntMap = java.util.HashMap() // This one should not be reported: @SuppressLint("UseSparseArrays") val myOtherMap = HashMap() } protected fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int, x: Boolean) { // wrong signature java.lang.String("not an error") } protected fun onMeasure(widthMeasureSpec: Int) { // wrong signature java.lang.String("not an error") } protected fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int, wrong: Int) { // wrong signature java.lang.String("not an error") } protected fun onLayout(changed: Boolean, left: Int, top: Int, right: Int) { // wrong signature java.lang.String("not an error") } override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { java.lang.String("flag me") } @SuppressWarnings("null") // not real code override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { java.lang.String("flag me") // Forbidden factory methods: Bitmap.createBitmap(100, 100, null) android.graphics.Bitmap.createScaledBitmap(null, 100, 100, false) BitmapFactory.decodeFile(null) val canvas: Canvas? = null canvas!!.getClipBounds() // allocates on your behalf canvas.clipBounds // allocates on your behalf canvas.getClipBounds(null) // NOT an error val layoutWidth = width val layoutHeight = height if (mAllowCrop && (mOverlay == null || mOverlay!!.width != layoutWidth || mOverlay!!.height != layoutHeight)) { mOverlay = Bitmap.createBitmap(layoutWidth, layoutHeight, Bitmap.Config.ARGB_8888) mOverlayCanvas = Canvas(mOverlay!!) } if (widthMeasureSpec == 42) { throw IllegalStateException("Test") // NOT an allocation } // More lazy init tests var initialized = false if (!initialized) { java.lang.String("foo") initialized = true } // NOT lazy initialization if (!initialized || mOverlay == null) { java.lang.String("foo") } } internal fun factories() { val i1 = 42 val l1 = 42L val b1 = true val c1 = 'c' val f1 = 1.0f val d1 = 1.0 // The following should not generate errors: val i3 = Integer.valueOf(42) } private val mAllowCrop: Boolean = false private var mOverlayCanvas: Canvas? = null private var mOverlay: Bitmap? = null override fun layout(l: Int, t: Int, r: Int, b: Int) { // Using "this." to reference fields if (this.shader == null) this.shader = LinearGradient(0f, 0f, width.toFloat(), 0f, intArrayOf(0), null, Shader.TileMode.REPEAT) val width = width val height = height if (shader == null || lastWidth != width || lastHeight != height) { lastWidth = width lastHeight = height shader = LinearGradient(0f, 0f, width.toFloat(), 0f, intArrayOf(0), null, Shader.TileMode.REPEAT) } } fun inefficientSparseArray() { SparseArray() // Use SparseIntArray instead SparseArray() // Use SparseLongArray instead SparseArray() // Use SparseBooleanArray instead SparseArray() // OK } fun longSparseArray() { // but only minSdkVersion >= 17 or if has v4 support lib val myStringMap = HashMap() } fun byteSparseArray() { // bytes easily apply to ints val myByteMap = HashMap() } }