四畳半の秘密基地

さあ、今日はどんな実験をしよう

MENU

CalendarクラスのcompareToが分かりづらい

今まで日付の比較を行うときはCalendarクラスのcompareToを使ってやってました。
でもcompareToって直感的じゃなくて毎回リファレンスみて「なるほど」となってたんですよ。

なんか良い実装ないかなぁと思ってましたが、よくよくソースを見てみるとやってることは単純でした。

public int compareTo(Calendar anotherCalendar) {
        if (anotherCalendar == null) {
            throw new NullPointerException("anotherCalendar == null");
        }
        long timeInMillis = getTimeInMillis();
        long anotherTimeInMillis = anotherCalendar.getTimeInMillis();
        if (timeInMillis > anotherTimeInMillis) {
            return 1;
        }
        if (timeInMillis == anotherTimeInMillis) {
            return 0;
        }
        return -1;
    }


要はUnix時間を比較してるだけなんですね。
これなら2つのCalendarで

if (calendar1.getTime() < calendar2.getTime()){
    //処理
}


としたほうが直感的だと思ったので最近はこちらを使うようにしています。

アニメーション設定を見てアプリの挙動を変えたいとき

アプリに凝った実装を入れるのは大事だなーと思ってアニメーションを加えてみました。
しかし、アプリリリース後に早速問い合わせが、、、
加えたアニメーションはゆっくり縦揺れするアニメーションでしたがユーザの問い合わせによるとアニメーション対象物がブルブル震えてるとのこと(((( ;゚Д゚)))ガクガクブルブル
調査したところ開発者オプションのアニメーション間隔設定がオフされていると再現しました。

そんなときは

canUseDuration = Settings.Global.getFloat(contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 0);

APIレベル17未満の場合は

canUseDuration = Settings.System.getFloat(contentResolver, Settings.System.ANIMATOR_DURATION_SCALE, 0);

のように書くと設定値が取得できます。オフの場合は0が返ってくるのでその場合はアニメーションしないようにしました。

完全無欠ダイエットやってみた。

社会人になってからだんだんとメタボ体質になっており何とかしなくてはと思いつつも食事は改善せずにマラソンや水泳をやってる日々でしたが、年末年始あたりに読んだ下記の本が面白くて試したくなるレベルでした。

シリコンバレー式 自分を変える最強の食事

シリコンバレー式 自分を変える最強の食事

内容はコーヒーにグラスフェッドバターという牧草だけ食べて育った牛のバターを大さじ1〜2杯とMCTオイルという中鎖脂肪酸という体に良い油を大さじ1〜2杯入れて飲むというもの。
当時はコーヒーのハンドドリップにはまってて自動コーヒーミルを買おうと思ってた矢先だったのでその辺のことも完全無欠ダイエットを始める後押しになった。
スペシャリティコーヒーが良いということだったので、近所のコーヒー専門店でいい豆を買いました。バターはamazonで海外から取り寄せました。

初めて〜1ヶ月

いきなりバターや油を通常量入れると体調をくずす人もいるらしかったので、初日はスペシャリティコーヒーを挽いて抽出し、そこにバターを最初は大さじ1/2くらいいれてオイルは数滴とかかなり慎重に始めました。
そのおかげが体調をくずすこともなく良い感じのスタートがきれました。ただ、バターとオイルの量が少なすぎると昼ご飯までに腹が減るので自分で調整して昼ご飯まで空腹にならない量を模索する必要があります。

1ヶ月〜2ヶ月

最初2週間ぐらいは変化がなかったんですが、3〜4週間頃から背中まわりに着いた脂肪が落ちてる感じがしてきました。
また、1ヶ月経つぐらいからは腹まわりの脂肪が減ってきてベルトの穴1つ分ウェストが痩せました。
ほんとに効果あるのかすごいなーと思いました。朝食に炭水化物のかわりに油を摂るようにするだけでこんなに違うのかと。

ちなみに変えたのは朝食のみで昼ご飯と晩ご飯は今まで通りふつうにとってます。
なので調子にのって晩ご飯の後にデザートとか食べてると痩せないところか太ります。
「朝食でダイエット頑張ってるから晩ご飯にデザート食べるかー」みたいな残念な考え方をなくしていきたい。

2ヶ月〜

2ヶ月ぐらい続ける間にいろいろ試した結果、以下のことがわかりました。
・コーヒーはスペシャリティコーヒーじゃなくてプレミアムコーヒーでもいける(飲んだ感じ差が感じられない)
グラスフェッドバターは必須(同じくこのダイエットを試していた知り合い曰く、普通のバターで試したら胃もたれしたとのこと・・・)
MCTオイルは味がほとんどしないので何にでも合う

長期的にダイエットをするときに一番ネックになるのがコストですよね。
ダイエットに使う食品でダウングレードできるものがないか模索してみたんですが、バターとオイルをケチるといけないことがわかりました。また、コーヒーはプレミアムでもそこまで変わらないのでは?ということでカルディで買った豆を使用しています。

コーヒーを安くできましたが、他を何とかしたいなーと思い色々探しているとグラスフェッドバター5kgで買えることがわかりました。
かなりの量だと思いますが、200gで買うものよりも3倍安かったので迷わず買いました。

f:id:k-seito:20160424205146j:plain

でかいです。冷凍庫の1/3が占領されました。
ですがこれで完全無欠ダイエットを長期継続できそうです。

1年ぐらい続けたらどれくらいかわるのかなーとワクワクしてますが、調子にのって食べ過ぎる癖をやめないとあんまり変わらないかも・・・
次回は体重測っておいてグラフにできたらいいな。

CentoOSにJDKが入れらないとき

wget http://download.oracle.com/otn-pub/java/jdk/8u73-b02/jdk-8u73-linux-x64.rpm


参考サイトによくあるように上記のようなコマンドを打つとダウンロードに失敗します。

--2016-03-18 08:57:30--  http://download.oracle.com/otn-pub/java/jdk/8u73-b02/jdk-8u73-linux-x64.rpm
Resolving download.oracle.com... 165.254.42.40, 165.254.42.51
Connecting to download.oracle.com|165.254.42.40|:80... connected.
HTTP request sent, awaiting response... 302 Moved Temporarily
Location: https://edelivery.oracle.com/otn-pub/java/jdk/8u73-b02/jdk-8u73-linux-x64.rpm [following]
--2016-03-18 08:57:30--  https://edelivery.oracle.com/otn-pub/java/jdk/8u73-b02/jdk-8u73-linux-x64.rpm
Resolving edelivery.oracle.com... 23.218.8.217
Connecting to edelivery.oracle.com|23.218.8.217|:443... connected.
HTTP request sent, awaiting response... 302 Moved Temporarily
Location: http://download.oracle.com/errors/download-fail-1505220.html [following]
--2016-03-18 08:57:31--  http://download.oracle.com/errors/download-fail-1505220.html
Connecting to download.oracle.com|165.254.42.40|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 5307 (5.2K) [text/html]

こんな感じです。
いろいろ調べてたら答えが書いてあるサイトがありました。

blog.tottokug.com

正解は

wget --no-cookies --no-check-certificate --header "Cookie: oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/8u73-b02/jdk-8u73-linux-x64.rpm

たしかにブラウザではAcceptのラジとボタン選択するけどCUIでダウンロードするときは必要ないのかなとか思ってたけど、そんなことありませんでしたorz
おそらくまたハマると思われるのでメモ。

TDDBCに行ってきた

久しぶりの更新になります。
本日、TDDBC in Tokyo 2016-02 - TDDBC | Doorkeeperに参加させて頂きました。
TDD自体は興味があって趣味の範囲でちょこちょこJUnit触ったりとか最近だとEspresso触ったりしていたんですが、
業務ではまったくやっていないため入門書をかじった程度で止まっていました。
そこから先に進んでみたいなと思い今回参加に踏み切りました。

午前の部(導入)

午前中は@setoazusaさんによるTDDとはなんぞや?という解説(基調講演)を聞きました。
TDDの知識はネットと本でしか得ておらず、実践している方からお話が聞けたのはとてもためになりました。
特に自分に刺さったのは

  • 不安をテストで表現する
  • TDDをやれば偉いわけじゃない
  • なぜTDDするのか

です。特になぜTDDするのか?は考えさせられました。一番最初に興味を持ったのは当時担当していたAndroidアプリのリリース頻度が短くて毎回人力テストをしていたときに辛みが増してきて、リリース前のテストを自動化できたらいいのにとか思ってたときですね。最近では、Espressoかっけー!テストやってる感じがする!テスト書いてる自分偉い!ってなってました。開発効率全然考えてない・・・。プログラマの三大美徳がおざなりになってました。

その後は、運営チームの方がペアプロのデモをしてくれました。
デモを見ながら、あーこんなペースで話しながら開発するなんて無理だわって思ってました。
手慣れてる感がすごい。

午後の部(ペアプロでTDD)

午後は実際にペアプロしてみましょうということで早速ペア決めを。自分はJavaを選択しました。ここでKotlinとか言えたらかっこよかったのかな。
その後、お題が出されそのお題をTDDしながら2人で開発していくことに。ペアプロ自体もあまりやったことがなかったので新鮮でした。というかペアでキーボードを叩いていないときは何をすればいいかわからなかったのでとりあえず、スペルミスがないかチェックと実装に悩んでたら一緒に悩む的なことをしてました。最終的には課題4?まで終わってフィニッシュ。後半は頭の回転が鈍くなりペアの方の実装を目で追うので精一杯に・・・

ここで疑問が。ペアプロの場合、組む相手が自分の知らないライブラリやクラスを使い始めたらどうすればいいんだろう。
ペアでする以上片方がついていけなくなるとペアプロの意味がなくなるような気もする。かといって一々解説してもらってたら開発効率が下がるだろうなーと思ったり。

ためになったことは仮実装という概念です「。座標(1, 2)を与えたらgetXは1を返す」というテストをするとき、一番最初に実装するgetXの戻り値は1というハードコーディングでも良いというのは驚きでした。あるテストがあったときにそのテストを最低限の労力でグリーンにするということを念頭に置いて実装していくといいのかなと思いました。

後はレビューという時間があるのも新鮮でした。コードについて議論の経験に乏しい自分では皆の言ってることに納得しかできませんでした。もっと議論できるようになりたい。

改善点とか

終了間際に運営にフィードバックあったらお願いしますって言われましたが、TDDにより頭がパンクしていてその場ではまったく出てきませんでした。帰宅して、ブログに起こすにあたり思い当たる節があったので箇条書きでまとめます。

  • デモのときにペアプロの解説が欲しかった(ドライバー・オブザーバーの役割等)
  • (個人のレベルもありますが)課題が途中から急に難易度が高くなり実装を考える時間が大幅に増えたので、問題をもう少し簡単にしてもらえると実装を考える時間を減らしTDDに費やせる時間が増やせるのかなと思いました。

次のステップ

勉強会に行って満足するときが多々あるのでに次のアクションにつなげるようにしています。今回のTDDBCで得た情報を元に何をしようかと考えましたがこんな感じかな。

  • JUnit実践入門読み返す
  • プライベートで開発してるプロジェクトにJUnit導入
  • 書籍:リファクタリング―既存のコードを安全に改善する― を買う

最後に、とても有意義な場を提供していただいてありがとうございました。
時間がなくて懇親会には参加できませんでしたが、次回があれば参加したいです。

Retrofitで実装したAPIにRxJavaを混ぜてみる

前回はRetrofitでグルメAPIを叩く処理を実装しました。
今回はそれにRxJavaを加えてみたいと思います。正直API1つだけで単純にデータを取得してるだけの処理だとあまり恩恵を感じないかもしれません。
あるAPIを叩いて戻り値を受け取ってその戻り値を元にまた別のAPIを叩くとかPromise的な処理をしたいときには便利そう。


app/build.gradle

compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta3'
compile 'io.reactivex:rxandroid:1.0.1'



まずは定義。次に、MainActivity部分。

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://webservice.recruit.co.jp")
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build();


RxJavaを使用するときは上のように定義しないとだめらしいです。

Observable<GourmetData> observable = service.getGourmetData(Environment.API_KEY, "Z012", "json");
        observable
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .map(new Func1<GourmetData, List<Shop>>() {
                    @Override
                    public List<Shop> call(GourmetData gourmetData) {
                        return gourmetData.results.getShop();
                    }
                })
                .subscribe(new Subscriber<List<Shop>>() {
                    @Override
                    public void onCompleted() {
                        System.out.println("Completed!");
                    }

                    @Override
                    public void onError(Throwable e) {
                        System.out.println("Error:" + e.getMessage());
                    }

                    @Override
                    public void onNext(List<Shop> shops) {
                        StringBuilder builder = new StringBuilder();

                        for (Shop shop : shops) {
                            builder.append(shop.getName() + "\n");
                        }
                        ((TextView) findViewById(R.id.textview)).setText(new String(builder));
                    }
                });


後はAPIのコール部分です。CallをObservableに変更して、通信は非同期・TextViewへの反映はUIスレッドで行おうとするとこんな感じの記述になりました。RxJavaは触り始めたばっかでまだまだ理解できていません・・・。精進せねば

github.com

Retrofit2を試してみる

最近RxJavaについて勉強中なんですが、RxJavaで非同期通信を行うときに相性の良いライブラリとしてRetrofitというものがあるという話を聞いて試してみようと思いました。
今回はリクルートさんのグルメサーチAPIを使ってみたいと思います。

まずはbuild.gradleに記述します。

compile 'com.squareup.retrofit2:retrofit:2.0.0-beta3'
compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta3'
compile 'com.google.code.gson:gson:2.4'


パーミッションも忘れずに

<uses-permission android:name="android.permission.INTERNET"/>




次にAPIのリファレンスやら参考サイトを読みながらPOJOを作っていきます。
リファレンスに書いてあるhttp://webservice.recruit.co.jp/hotpepper/gourmet/v1/?format=json
APIキーを指定して読み込みJSONデータを取得します。
その後、jsonschema2pojoを使ってPOJOを作成します。

Retrofitを使って天気情報を取得してみる【Android】

に詳しく記載があります。

作成したクラスはmodelsパッケージ内に格納しました。
次にRetrofitで通信部分を作成します。

public interface HotPepperService {
    @GET("/hotpepper/gourmet/v1/")
    Call<GourmetData> getGourmetData(@Query("key") String key, @Query("large_area") String area, @Query("format") String format);
}


通信結果を入れるオブジェクトはGourmetDataです。
最初、レスポンスのJSONを見てみるとルートがresultsとなってるのでレスポンスもResultsにしたら動きませんでした。
レスポンスを受け取るオブジェクトはJSONのルートのオブジェクトを持つクラスにしてあげると良いみたいです。


ここからはMainActivityへの記述になります。
まずは、APIインスタンスを作ります。

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://webservice.recruit.co.jp")
                .addConverterFactory(GsonConverterFactory.create())
                .build();

HotPepperService service = retrofit.create(HotPepperService.class);

後は、作成したインスタンスAPIを呼び出すだけです。

Call<GourmetData> call = service.getGourmetData(Environment.API_KEY, "Z012", "json");
call.enqueue(new Callback<GourmetData>() {
    @Override
    public void onResponse(Response<GourmetData> response) {
        List<Shop> shopList = response.body().results.getShop();
        StringBuilder builder = new StringBuilder();

        for (Shop shop : shopList) {
            builder.append(shop.getName() + "\n");
        }
                ((TextView) findViewById(R.id.textview)).setText(new String(builder));
    }

    @Override
    public void onFailure(Throwable t) {
        Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_LONG).show();
    }
});

f:id:k-seito:20160130220915p:plain


結果はこんな感じです。とても簡単にできますね。
クエリパラメータをStringBuilderを使ってごにょごにょしてたのがアホらしくなってきました。

https://github.com/kseito/RetrofitPractice/tree/first
ソースはGitHubに置いておきます。