androidlint #droidkaigi
TRANSCRIPT
![Page 1: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/1.jpg)
Android Lintで正しさを学ぼう2015.02.18 DroidKaigi 2016 Yukiya Nakagawa / @Nkzn
ROOM:B 13:00-13:50 #DroidKaigiB
![Page 2: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/2.jpg)
Who are you?
• Yukiya Nakagawa / @Nkzn
• ウォーターセル株式会社@新潟
• 農業向けサービス「アグリノート」
• エンジニア募集中です
• Androidは2009年からチマチマと
![Page 3: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/3.jpg)
Recent Activity
• C89コミケ
• とびだそう!Androidプログラミングレシピ
• https://techbooster.booth.pm/
• Android Lintネタ書いてました
![Page 4: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/4.jpg)
Target
• まだAndroidプログラミングのベストプラクティスが分からない初学者
• Androidアプリの品質を表す指標がほしい品質担当者
![Page 5: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/5.jpg)
Agenda
• Android Lintとは
• 初学者がやりがちなミス
• もっと上を目指す人のために
• 品質指標としてのAndroid Lintを考える
![Page 6: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/6.jpg)
Android Lintとは
![Page 7: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/7.jpg)
Lintとは
Another HTML-lint HTMLの構文チェックツール
JSLint, JSHint, ESLint JavaScriptの構文チェックツール
textlint 日本語の構文チェックツール
![Page 8: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/8.jpg)
lintとは、主にC言語のソースコードに対し、コンパイラより詳細かつ厳密なチェックを行うプログラムである。
• 型の一致しない関数呼び出し
• 初期化されていない変数の参照
• 宣言されているが使われていない変数
• 同じ関数を参照しているが、戻り値を使う場合と使わない場合がある
• 関数が戻り値を返す場合と返さない場合がある
など、コンパイラではチェックされないが、バグの原因になるような曖昧な記述についても警告される。
https://ja.wikipedia.org/wiki/Lint
![Page 9: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/9.jpg)
Javaの場合?
![Page 10: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/10.jpg)
Android Lintとは
• Google公式
• 222個のルール(ver25.0.4現在)
• Category, Severity, Priorityで分類される
• 結構手広い
• 不具合予備軍の検出
• ユーザビリティチェック
![Page 11: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/11.jpg)
Categoryカテゴリ名 チェックする内容Correctness SDKの使い方の正しさ
Correctness:Messages Correctnessのうち、特にメッセージ
Security セキュリティ
Performance パフォーマンス(動作速度)
Usability ユーザビリティ(使い勝手)
Usability:Icons Usabilityのうち、特にアイコン
Usability:Typography Usabilityのうち、特に文字
Accessibility アクセシビリティ
Internationalization 国際化・多言語化
Bi-directional Text Right-to-Leftモードでの見た目
![Page 12: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/12.jpg)
Severity
レベル 意味 アプリへの影響
Fatal 致命的 ビルドや実行に必ず失敗する
Error エラー ビルドはできるが実行時エラーを引き起こす可能性が高い
Warning 警告 動作はするが修正したほうが より良いアプリになる
Information 情報 ほぼ問題ないが頭の片隅に 置いておいたほうがよい
Ignore 無視 問題があったとしても 検出しない
![Page 13: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/13.jpg)
チェックできるファイルの種類
• https://android.googlesource.com/platform/tools/base
• com.android.tools.lint.detector.api.Detector https://android.googlesource.com/platform/tools/base/+/master/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Detector.java
![Page 14: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/14.jpg)
1. Manifest file
2. Resource files, in alphabetical order by resource type
3. Java sources
4. Java classes
5. Gradle files
6. Generic files
7. Proguard files
8. Property files
![Page 15: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/15.jpg)
Priority
• 1から10まで
• あまりあてにならない
![Page 16: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/16.jpg)
どんな問題を解決するのか
![Page 17: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/17.jpg)
お作法分かりづらい問題
https://twitter.com/konifar/status/698760224409169920
![Page 18: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/18.jpg)
Android Way is どこ
https://twitter.com/konifar/status/698760496837603328
![Page 19: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/19.jpg)
Googleによる正解集 (あるいはアンチパターン集)
Android Lint
![Page 20: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/20.jpg)
事例紹介 Part 1. 初学者がやりがちなミス
![Page 21: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/21.jpg)
Case 1. LinearLayoutに並べたビューが 表示されないのは何故?
(Orientation)
![Page 22: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/22.jpg)
こんなコード書いてませんか
<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="one" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="two" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="three" /> </LinearLayout>
何故かアイテムがひとつしか出ない・・・?
![Page 23: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/23.jpg)
Orientation
• Summary: Missing explicit orientation
• Priority: 2 / 10
• Severity: Error
• Category: Correctness
![Page 24: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/24.jpg)
何故いけないのか?デフォルトのorientationはhorizontal
![Page 25: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/25.jpg)
こう書きましょう
<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="one" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="two" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="three" /> </LinearLayout>
![Page 26: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/26.jpg)
Case 2: Fragmentが表示されない
![Page 27: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/27.jpg)
こんなコード書いてませんか
@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_commit_transaction); getSupportFragmentManager().beginTransaction() .add(R.id.container, new MyFragment());}
あれー? Fragmentが表示されないなー?
![Page 28: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/28.jpg)
CommitTransaction
• Summary: Missing commit() calls
• Priority: 7 / 10
• Severity: Warning
• Category: Correctness
![Page 29: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/29.jpg)
何故いけないのか?
• beginTransaction()から始まるメソッドチェーンは、commit()が呼び出されてから描画処理に入ります
• そりゃcommit()を呼んでなきゃ何も表示されません
• DBのトランザクションみたいなイメージ
![Page 30: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/30.jpg)
こう書きましょう
@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_commit_transaction); getSupportFragmentManager().beginTransaction() .add(R.id.container, new MyFragment()) .commit(); }
![Page 31: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/31.jpg)
Case 3. SharedPreferenceが 保存されない
![Page 32: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/32.jpg)
こんなコード書いてませんか
SharedPreferences pref = PreferenceManager .getDefaultSharedPreferences(this);
SharedPreferences.Editor editor = pref.edit();editor.putLong("now", new Date().getTime());
pref.getLong("now", -1); // -1
SharedPreferencesに保存したはずの データが取れないなあ
![Page 33: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/33.jpg)
CommitPrefEdits
• Summary: Missing commit() on SharedPreference editor
• Priority: 6 / 10
• Severity: Warning
• Category: Correctness
![Page 34: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/34.jpg)
何故いけないのか?
• SharedPreferencesにデータを保存する場合、実際に保存されるタイミングはcommit()のとき
• そりゃcommit()を呼んでなきゃ何も表示されません
![Page 35: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/35.jpg)
こう書きましょう
SharedPreferences pref = PreferenceManager .getDefaultSharedPreferences(this);
SharedPreferences.Editor editor = pref.edit();editor.putLong("now", new Date().getTime()); editor.commit(); // consider using apply() instead
pref.getLong("now", -1); // 1455620005841
![Page 36: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/36.jpg)
おまけ: commit()とapply()
• SharedPreferencesはアプリ内領域のXMLを読み書きすることでデータを永続化している/data/data/[applicationId]/shared_prefs/hoge.xml
Editor XMLMemory Cache
commit()は待つ
apply()は待たない
![Page 37: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/37.jpg)
こう書くとなおよい
SharedPreferences pref = PreferenceManager .getDefaultSharedPreferences(this);
SharedPreferences.Editor editor = pref.edit();editor.putLong("now", new Date().getTime()); editor.apply();
// OR
// if(editor.commit()) { // pref.getLong(“now”, -1); // 1455620005841 // }
![Page 38: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/38.jpg)
Case 4: Toastが出ない
![Page 39: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/39.jpg)
こんなコード書いてませんか
Toast.makeText( this, “this line is passed”, Toast.LENGTH_LONG)
なんでだ、絶対にこの行を通ってるはずなのに! なんでトーストが出ないんだ!!!!
![Page 40: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/40.jpg)
ShowToast
• Summary: Toast created but not shown
• Priority: 6 / 10
• Severity: Warning
• Category: Correctness
![Page 41: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/41.jpg)
こう書きましょう
Toast.makeText( this, “this line is passed”, Toast.LENGTH_LONG).show()
![Page 42: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/42.jpg)
Case 5: なんかこのOKボタン 押しづらくない?
![Page 43: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/43.jpg)
こんなコード書いてませんか
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@android:string/ok" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@android:string/cancel" /> </LinearLayout>
![Page 44: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/44.jpg)
ButtonOrder
• Summary: Button order
• Priority: 8 / 10
• Severity: Warning
• Category: Usability
![Page 45: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/45.jpg)
何故いけないのか?
• デザインガイドラインに書いてある https://www.google.com/design/spec/components/dialogs.html#dialogs-specs
• 単にダイアログを閉じるだけの「キャンセル」のようなものは左、本来やりたかった操作を続ける「OK」のようなものは右に置く
• 「削除」はネガティブなイメージなので左に置いてしまいがちだけど右
![Page 46: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/46.jpg)
こう書きましょう
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@android:string/cancel" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@android:string/ok" /> </LinearLayout>
![Page 47: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/47.jpg)
おまけ: 検出条件
1. targetSdkがAPI Level 14以上
• API Level 13まではOKが左で正しかった
2. ラベルが”OK”, “Cancel”, android.R.string.ok, android.R.string.cancelのいずれか
3. 親レイアウトの設定上、並びが明らか
• LinearLayoutかTableRowで、orientationがhorizontal
• RelativeLayoutで、toRightOf, toLeftOfの関係で順序が分かる
• RelativeLayoutで、alignParentLeft, alignParentRightの関係で順序が分かる
![Page 48: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/48.jpg)
Case 6: このアプリ英語化して
![Page 49: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/49.jpg)
こんなコード書いてませんか
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="ユーザーを選択してください" /><ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:contentDescription="犬のアイコン" android:src=“@drawable/ic_dog” /><EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="入力してください" />
日本語が表示されるところを全部探して 英語に書き直す・・・? 1日じゃ無理だよ!
![Page 50: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/50.jpg)
HardcodedText
• Summary: Hardcoded text
• Priority: 5 / 10
• Severity: Warning
• Category: Internationalization
![Page 51: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/51.jpg)
検出対象
• レイアウトXML(res/layout/)
• android:text
• android:contentDescription
• android:hint
• android:prompt
• AndroidManifest.xml
• android:label
• メニューXML(res/menu/)
• android:title
![Page 52: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/52.jpg)
何故いけないのか?
• 文字列リソースに抜き出したほうが総合的にメリットが多い
• Java側で言語情報を切り替えることもできなくもないがかなり煩雑
• リソースファイルの自動切り替えに頼ったほうが遥かに楽
![Page 53: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/53.jpg)
こう書きましょう
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=“@string/select_a_user“ /><ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:contentDescription=“@string/dog_icon“ android:src=“@drawable/ic_dog” /> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:hint=“@string/input_please“ />
![Page 54: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/54.jpg)
おまけ
• デモ
• クイックフィックス
• 言語コードごとにstrings.xmlを用意した場合のAndroid Studioの挙動
![Page 55: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/55.jpg)
事例紹介 Interlude: konifar/droidkaigi2016
![Page 56: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/56.jpg)
./gradlew lint
app/build/outputs/lint-results.html
![Page 57: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/57.jpg)
👍
![Page 58: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/58.jpg)
事例紹介 part2. もっと上を目指す人のために
![Page 59: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/59.jpg)
Case 7: アプリが管理していた個人情報が 別のアプリから奪われた!
![Page 60: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/60.jpg)
こんなコード書いてませんか
<!—- AndroidManifest.xml —-> <provider android:name=".provider.MyContentProvider" android:authorities="info.nkzn.mycontentprovider" />
ContentProviderを使うときはマニフェストに 書くんだろ? そんなこと知ってるよ!
![Page 61: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/61.jpg)
ExportedContentProvider
• Summary: Content provider does not require permission
• Priority: 5 / 10
• Severity: Warning
• Category: Security
![Page 62: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/62.jpg)
何故いけないのか?
• 実は<provider>にはexportedというパラメータがあり、デフォルトでtrue
• 外部のアプリからでも content://hogehoge のようなURIでデータにアクセスできてしまう
• Dropboxが2011年にやらかしてたhttp://codezine.jp/article/detail/6286
![Page 63: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/63.jpg)
こう書きましょう
<!—- AndroidManifest.xml —-> <provider android:name=".provider.MyContentProvider" android:authorities=“info.nkzn.mycontentprovider" android:exported=“false" />
※ minSdkVersion, targetSdkVersionが 両方とも17以上なら、デフォルトでfalseになります http://developer.android.com/intl/ja/guide/topics/manifest/provider-element.html
![Page 64: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/64.jpg)
Case 8: これ、何を入力する欄?
![Page 65: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/65.jpg)
こんなコード書いてませんか
<EditText android:layout_width="match_parent" android:layout_height="wrap_content" /> <EditText android:id=“@+id/phone_number" android:hint=“電話番号を入力してください" android:layout_width="match_parent" android:layout_height="wrap_content" /> <EditText android:id="@+id/pin" android:hint=“PINコードを入力してください" android:layout_width="match_parent" android:layout_height="wrap_content" /> <EditText android:id="@+id/site_url" android:hint=“サイトのURLを入力してください" android:layout_width="match_parent" android:layout_height="wrap_content" />
電話番号を入れたいのに日本語キーボードが 出てきて切り替えるの面倒くさい・・・
何入れたらいいのか わからない・・・
![Page 66: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/66.jpg)
TextFields
• Summary: Missing inputType or hint
• Priority: 5 / 10
• Severity: Warning
• Category: Usability
![Page 67: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/67.jpg)
何故いけないのか?
• ユーザーはhintを見て何を入力する欄なのかを判断するので、無いと不便
• inputTypeを指定すれば適切なキーボードが現れることが多いので、キーボードを切り替える手間が省けて便利
![Page 68: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/68.jpg)
こう書きましょう
<EditText android:hint="メモを入力してください" android:layout_width="match_parent" android:layout_height="wrap_content" /> <EditText android:id=“@+id/phone_number" android:hint=“電話番号を入力してください” android:inputType=“phone” android:layout_width="match_parent" android:layout_height="wrap_content" /> <EditText android:id="@+id/pin" android:hint=“PINコードを入力してください” android:inputType="numberPassword" android:layout_width="match_parent" android:layout_height="wrap_content" /> <EditText android:id="@+id/site_url" android:hint=“サイトのURLを入力してください” android:inputType="textUri" android:layout_width="match_parent" android:layout_height="wrap_content" />
![Page 69: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/69.jpg)
Case 9: やっぱりアイコンは カラフルじゃなきゃ
![Page 70: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/70.jpg)
こんなアイコン作ったことありませんか
色がついててみづらい
色がついててみづらい
![Page 71: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/71.jpg)
IconColors
• Summary: Icon colors do not follow the recommended visual style
• Priority: 6 / 10
• Severity: Warning
• Category: Usability:Icons
![Page 72: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/72.jpg)
何故いけないのか• マテリアルデザインの登場などもあり、ステータスバーや
ActionBarの色はアプリごとに鮮やかに変わるようになった
• アイコンと同系色の背景になったときに見づらくなってしまう
• 通知アイコンは白、アクションアイコンはグレーにしておけば、なんとなく大体の色に合う
• というような話が developer.android.com/design に載っていたような気がするのだけれど、www.google.com/design に移ったときにその辺の話がなくなってしまった気がする
![Page 73: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/73.jpg)
こうしましょう
![Page 74: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/74.jpg)
おまけ
// com.android.tools.lint.checks.IconDetector.java for (int y = 0, height = image.getHeight(); y < height; y++) { for (int x = 0, width = image.getWidth(); x < width; x++) { int rgb = image.getRGB(x, y); if ((rgb & 0xFF000000) != 0) { int r = (rgb & 0xFF0000) >>> 16; int g = (rgb & 0x00FF00) >>> 8; int b = (rgb & 0x0000FF); if (r != g || r != b) {
1pxずつ色を評価していて執念を感じた
![Page 75: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/75.jpg)
Case 10: 文字は正しく使いましょう
![Page 76: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/76.jpg)
こんなコード書いてませんか
<string name="continue_to_read">続きを読む...</string>
3点リーダ(…)の入れ方わかんないから ピリオド3つでいいや
![Page 77: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/77.jpg)
TypographyEllipsis
• Summary: Ellipsis string can be replaced with ellipsis character
• Priority: 5 / 10
• Severity: Warning
• Category: Usability:Typography
![Page 78: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/78.jpg)
何故いけないのか
• フォントによってはピリオドが都合よく…と見えてくれるかどうかはわからない
• ISO-8859-1で…は”…”として定義されており、各フォントも…が3点リーダとして見やすくなるようにしてくれているはず
• 用意されている文字はちゃんと使おうというスタンス
![Page 79: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/79.jpg)
こう書きましょう
<string name=“continue_to_read”>続きを読む…</string> <!—- または —-> <string name=“continue_to_read”>続きを読む…</string>
![Page 80: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/80.jpg)
品質管理担当者の方へ: 品質指標として使ってみませんか
![Page 81: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/81.jpg)
エンジニアの勝手な言い分
• Googleが「これやると良くないアプリになるよ」と言っているものを回避しているので、相対的にアプリの品質は上がっていると言っていいのでは
• こんな重箱の隅を突くようなチェックを200項目以上も回避するのはそれなりの経験値がないと難しい
• 正しくAndroid SDKを使えるように気を使いながら開発できているという点は、品質上の成果として計上して、取引先や経営層が評価してもらえると嬉しい
![Page 82: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/82.jpg)
品質指標に使う場合
• 優先度が低いルールは事前に設定でignoreしておく(Bi-
directional Textカテゴリなど)
• 予算やスケジュールに応じて事前に「Fatal, Errorはすべて直す」「Warningは15件以内の検出」などの品質目標を定めておく
• カテゴリ単位での採用・不採用を各プロジェクトごとに定めるのもよいと思います
![Page 83: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/83.jpg)
エンジニアの方へ: abortOnErrorを切らないで
![Page 84: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/84.jpg)
abortOnError, checkReleaseBuilds// build.gradleandroid { lintOptions { abortOnError false checkReleaseBuilds false }}
abortOnError: 危険度がErrorまたはFatalのルールに抵触したらビルドを中断する checkReleaseBuilds: リリースビルド時にFatalのルールに抵触したらビルドを中断する
![Page 85: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/85.jpg)
趣味でやってたり プロトタイプならともかく
お仕事でやるときに falseにするのよくないと思います
本当に対応しないルールだけ ignoreしましょう
![Page 86: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/86.jpg)
droidkaigi2016/app/build.gradle
lintOptions { abortOnError false disable 'InvalidPackage'}
😡
![Page 87: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/87.jpg)
さいごに
• Android Studioがソースコードに黄色いのとか赤いのとか付けてたら、マウスオーバーして何が起きてるか見てね!
• すべてのルールを倒したら胸を張ろう
• Android Lintと俺達の戦いはこれからだ!
![Page 88: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/88.jpg)
ご清聴ありがとうございました
![Page 89: AndroidLint #DroidKaigi](https://reader033.vdocuments.site/reader033/viewer/2022042723/587286381a28abc7068b7315/html5/thumbnails/89.jpg)
参考文献• lint | Android Developers
http://developer.android.com/intl/ja/tools/help/lint.html
• Improving Your Code with lint | Android Developershttp://developer.android.com/intl/ja/tools/debugging/improving-w-lint.html
• Android Lint - Android Tools Project Site http://tools.android.com/tips/lint
• Writing a Lint Check - Android Tools Project Sitehttp://tools.android.com/tips/lint/writing-a-lint-check
• platform_tools_basehttps://android.googlesource.com/platform/tools/base
• LintOptions - Android Plugin 1.5.0 DSL Referencehttp://google.github.io/android-gradle-dsl/current/com.android.build.gradle.internal.dsl.LintOptions.html