Android開発のための Effective Java

ask_hn__why_is_advice_in_effective_java_considered_inappropriate_for_android____hacker_news

「Effective Java」は素晴らしい本であるが, 「Android開発では少し当てはまらないとこもあるよ」という話.

Effective Java for Android (cheatsheet). – RockNNull – Medium

インスタンス化させない

new を使ってオブジェクトを生成させたくない場合, private なコンストラクタを使いましょう. 特に, 静的なメソッドのみのユーティリティクラスで使うといいでしょう.


class MovieUtils {
    private MovieUtils() {}
    static String titleAndYear(Movie movie) {
        [...]
    }
}

静的なFactory

new を使う代わりに, private なコンストラクタを静的な Factory メソッドを使いましょう. これらの Factory メソッドは名前をつけることができ、毎回オブジェクトの新しいインスタンスを返す必要はなく、必要なものに応じて異なるサブタイプを返すことができます.


class Movie {
    [...]
    public static Movie create(String title) {
        return new Movie(title);
    }
}

Builder

3つを超えるコンストラクタパラメータを必要とするオブジェクトは, Builder を使ってオブジェクトを生成しましょう. すこし冗長かもしれませんが, 拡張しやすく読みやすいです. 値にクラスを使う場合は AutoValue を考慮するといいです.


class Movie {
    static Builder newBuilder() {
        return new Builder();
    }
    static class Builder {
        String title;
        Builder withTitle(String title) {
            this.title = title;
            return this;
        }
        Movie build() {
            return new Movie(title);
        }
    }

    private Movie(String title) {
    [...]    
    }
}
// Use like this:
Movie matrix = Movie.newBuilder().withTitle("The Matrix").build();

イミュータブル化

「イミュータブル」とは, 生存している間は変化しないことです. オブジェクト内のすべての必要なデータは作成時に提供されます. これは, 簡単, スレッドセーフ, 共有性などさまざまな利点があります.


class Movie {
    [...]
    Movie sequel() {
        return Movie.create(this.title + " 2");
    }
}
// Use like this:
Movie toyStory = Movie.create("Toy Story");
Movie toyStory2 = toyStory.sequel();

すべてのクラスをイミュータブルにすることは難しいかもしれませんが可能な限りイミュータブルにします. (例えば, フィールドを private final に, クラスを final に). オブジェクトの生成はモバイル上では, コストが高くなる可能性があります.

静的なメンバークラス

外部に依存しないインナークラスを定義する場合, それを static にすることを忘れてはなりません. そうしないと, それぞれのインナークラスのインスタンスが外部のクラスの参照をもつことになります.


class Movie {
    [...]
    static class MovieAward {
        [...]
    }
}

すべてにジェネリクスを

Java は, タイプセーフティ(型安全) を提供しています. 可能であれば, プリミティブ型やObject型は避けましょう. ジェネリックスは、コンパイル時にコード・タイプを安全にしてくれるメカニズムです.


// DON'T
List movies = Lists.newArrayList();
movies.add("Hello!");
[...]
String movie = (String) movies.get(0);

// DO
List<String> movies = Lists.newArrayList();
movies.add("Hello!");
[...]
String movie = movies.get(0);

メソッドのパラメータや戻り値についても使うことができることを忘れないでください.


// DON'T
List sort(List input) {
    [...]
}

// DO
<T> List<T> sort(List<T> input) {
    [...]
}

すこし柔軟にするには, バインドされたワイルドカードを利用して, 受け入れる範囲を広げることができます.


// Read stuff from collection - use "extends"
void readList(List<? extends Movie> movieList) {
    for (Movie movie : movieList) {
        System.out.print(movie.getTitle());
        [...]
    }
}

// Write stuff to collection - use "super"
void writeList(List<? super Movie> movieList) {
    movieList.add(Movie.create("Se7en"));
    [...]
}

空の返却

リストやコレクションを結果なしで返却するときの null を避けます.
空のコレクションを返すことで, 簡単なインターフェースとなり, 偶発的な NPE を避けることができます. 新しいコレクションを作成するのではなく、同じコレクションを返すことをお勧めします。


List<Movie> latestMovies() {
    if (db.query().isEmpty()) {
        return Collections.emptyList();
    }
    [...]
}

String の連結

文字列を連結しなければならない場合、+演算子を使う場合があります.
多くの文字列の連結には使用しないでくだい. パフォーマンスは本当に悪いです.
代わりに StringBuilder を推奨します.


String latestMovieOneLiner(List<Movie> movies) {
    StringBuilder sb = new StringBuilder();
    for (Movie movie : movies) {
        sb.append(movie);
    }
    return sb.toString();
}

例外を使った修正

私はエラーを示すための例外を投げることに賛成ではありませんが, 例外をチェックすることで、エラーを修正することができます.


List<Movie> latestMovies() throws MoviesNotFoundException {
    if (db.query().isEmpty()) {
        throw new MoviesNotFoundException();
    }
    [...]
}

再度, 見直してみるといいのかもしれません.



Ask HN: Why is “advice in Effective Java considered inappropriate for Android”? | Hacker News

Radio Hacker News -Translation - Android Apps on Google Play


日本語設定では見ることができない本当のAndroidバージョン別のシェア率

Android のバージョン別の利用割合をみるとき,

みますよね, ここ.

ダッシュボード | Android Developers

%e3%82%bf%e3%82%99%e3%83%83%e3%82%b7%e3%83%a5%e3%83%9b%e3%82%99%e3%83%bc%e3%83%88%e3%82%99___android_developers

ところが, ここにアクセスすると,

Dashboards | Android Developers

dashboards___android_developers

当然数字も違いますよね.

URLを見てみると, 微妙に違ったりします.

../dashboards/index.html?hl=ja

../dashboards/index.html?hl=en

パラメータだけでなく, 他の何かも影響してる雰囲気.

../dashboards/index.html

画面の下にあるプルダウンから言語変えてみるのもいいみたいです.

%e3%82%bf%e3%82%99%e3%83%83%e3%82%b7%e3%83%a5%e3%83%9b%e3%82%99%e3%83%bc%e3%83%88%e3%82%99___android_developers

なんだか気持ち悪いので, スクリプトにしておきますね.

version_android

Android バージョンやコードネームなどからのシェアの一覧取得


Android バージョンやコードネームなどからのシェアの一覧取得

Android のバージョンを表す場合,

相手によって言い方を変えなければなりません.

「Android 4.4 はー」

「ロリポップってー」

「minSdk は 19 でー」

「Android M はー」

特に,

会議などでバージョン別のユーザの割合を話しているときには,

営業からプログラマまで言い方がバラバラでいきなり

「ジンジャーブレッドまではもう切り捨ててもいいんじゃね?」

といわれてもすぐにピンとこなかったりします.

いくつかオフィシャルに公開されている一般的な名称の一覧があります.

codenames__tags__and_build_numbers___android_open_source_project

Codenames, Tags, and Build Numbers | Android Open Source Project

%e3%82%bf%e3%82%99%e3%83%83%e3%82%b7%e3%83%a5%e3%83%9b%e3%82%99%e3%83%bc%e3%83%88%e3%82%99___android_developers

ダッシュボード | Android Developers

また, システムの内部的には, Build.VERSION_CODES とよばれる名称が, APIレベルという整数を表します.

build_version_codes___android_developers

Build.VERSION_CODES | Android Developers

android_os_%e3%83%8f%e3%82%99%e3%83%bc%e3%82%b7%e3%82%99%e3%83%a7%e3%83%b3%e3%81%ae%e3%82%b3%e3%83%bc%e3%83%88%e3%82%99%e3%83%8d%e3%83%bc%e3%83%a0%e3%82%92%e5%8f%96%e5%be%97%e3%81%99%e3%82%8b

Android OS バージョンのコードネームを取得する

いちいちめんどくさいのでスクリプトにしておきます.


#!/usr/local/bin/php
<?php
$versions = '
LV VERSION_CODE           VERSION       CODE_NAME 
 1 BASE                   1             (no code name)
 2 BASE_1_1               1.1           (no code name)
 3 CUPCAKE                1.5           Cupcake
 4 DONUT                  1.6           Donut
 5 ECLAIR                 2             Eclair
 6 ECLAIR_0_1             2.0.1         Eclair
 7 ECLAIR_MR1             2.1           Eclair
 8 FROYO                  2.2.x         Froyo
 9 GINGERBREAD            2.3 - 2.3.2   Gingerbread
10 GINGERBREAD_MR1        2.3.3 - 2.3.7 Gingerbread
11 HONEYCOMB              3             Honeycomb
12 HONEYCOMB_MR1          3.1           Honeycomb
13 HONEYCOMB_MR2          3.2.x         Honeycomb
14 ICE_CREAM_SANDWICH     4.0.1 - 4.0.2 Ice Cream Sandwich
15 ICE_CREAM_SANDWICH_MR1 4.0.3 - 4.0.4 Ice Cream Sandwich
16 JELLY_BEAN             4.1.x         Jelly Bean
17 JELLY_BEAN_MR1         4.2.x         Jelly Bean
18 JELLY_BEAN_MR2         4.3.x         Jelly Bean
19 KITKAT                 4.4 - 4.4.4   KitKat
20 KITKAT_WATCH           4.4W          KitKat Watch
21 LOLLIPOP               5             Lollipop
22 LOLLIPOP_MR1           5.1           Lollipop
23 M                      6             Marshmallow
24 N                      7             Nougat
25 N_MR1                  7.1           Nougat
';

$url = 'https://developer.android.com/about/dashboards/index.html';
$versions = explode("\n", trim($versions));

$html  = file_get_contents($url);
preg_match_all('/var VERSION_DATA =(.*?)var VERSION_NAMES =/si',
               $html, $matches);
$json = json_decode(trim($matches[1][0], " \t\n\r\0\x0B[];"), true);

$sum = 100;
foreach ($json['data'] as $data) {
    printf("%5s %5s %s\n", 
           $data['perc'], sprintf('%03.1f', $sum), $versions[$data['api']]);
    $sum -= $data['perc'];
}

ユーザのシェアの数字は月一で更新される公式ページから実行時に取得します.

左から,

「ユーザ割合」
「そのバージョンをminSdkにしたときのカバー率」
「API レベル」
「バージョンコード」
「OSバージョン」
「コードネーム」

としています.


  0.1 100.0  8 FROYO                  2.2.x         Froyo
  1.3  99.9 10 GINGERBREAD_MR1        2.3.3 - 2.3.7 Gingerbread
  1.3  98.6 15 ICE_CREAM_SANDWICH_MR1 4.0.3 - 4.0.4 Ice Cream Sandwich
  4.9  97.3 16 JELLY_BEAN             4.1.x         Jelly Bean
  6.8  92.4 17 JELLY_BEAN_MR1         4.2.x         Jelly Bean
  2.0  85.6 18 JELLY_BEAN_MR2         4.3.x         Jelly Bean
 25.2  83.6 19 KITKAT                 4.4 - 4.4.4   KitKat
 11.3  58.4 21 LOLLIPOP               5             Lollipop
 22.8  47.1 22 LOLLIPOP_MR1           5.1           Lollipop
 24.0  24.3 23 M                      6             Marshmallow
  0.3   0.3 24 N                      7             Nougat

おおまかですが, すばやく一覧で確認できます.

Jelly Bean (Android 4.3) までを切り捨てた場合, minSdk=19 となり, 利用できるのは Android 4.4 以降の kitKat ユーザー で, 全体の 83.6% 程度なのかっ!