為さねば成らぬ

retia.verno@gmail.com

Android LintのDetectorを読む Day2: AlarmDetector

ソースコード

https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AlarmDetector.kt

背景

高頻度のアラームはバッテリーに悪いため、API 22からは最低でも5秒かかるし、インターバルも最低60秒になっている。 5秒以内に実行したい場合はメッセージを遅延させて送るか runnableを使うこと。

適用例

AlarmManager.setRepeatingにおいて、第2引数が5000、第3引数が60000未満であれば適用

気づき

特定のメソッドについて着目する場合、メソッド名だけで判定しない

getApplicableMethodNames でメソッド名を指定しつつ、更に引数の型・引数の数で判定している。 現在 setRepeatingオーバーロードが存在せず名前だけで一意に指定できるので、現状意味をなしていない判定である。 おそらく今後他の引数を持つ同名メソッドが増えた時にそちらに適用されないようにしているのだろう

override fun getApplicableMethodNames(): List<String> = listOf("setRepeating")

override fun visitMethodCall(
        context: JavaContext,
        node: UCallExpression,
        method: PsiMethod
    ) {
        val evaluator = context.evaluator
        if (evaluator.isMemberInClass(method, "android.app.AlarmManager") &&
            evaluator.getParameterCount(method) == 4
        ) {
            ensureAtLeast(context, node, 1, 5000L)
            ensureAtLeast(context, node, 2, 60000L)
        }
    }

変数・定数を考慮した値の取得の仕方

このLintでは引数の値が一定以下のときに引っかかるようになっている。 引数に渡される値も、数値直の場合もあるが、変数・定数でその箇所だけではわからない場合がある。

ConstantEvaluatorを用いて、visitMethodCallからは以下のように取得できる。

override fun visitMethodCall(
        context: JavaContext,
        node: UCallExpression,
        method: PsiMethod
    ) {
        val evaluator = context.evaluator
        val numberOfTargetParam // 取得したい引数が第何引数か
        val argument = node.valueArguments[numberOfTargetParam]
        val value = ConstantEvaluator.evaluate(context, argument)
        
        // Long
        value.toLong() 
        // String
        value.toString()
    }