yo_waka's blog

78.9 (7hack)というPodcastを聴くためのAndroidアプリを作った

本当に今更ながら、通勤でPodcastを聴くことが習慣づいてきた。
なんかこの1年くらいTech系のPodcast増えてませんか。どれも面白くてすごい。

自分の携帯端末はAndroidなのでFM Playerというアプリで聴いていて、非常に高機能でよくできたアプリなんだけど、自分にはオススメ番組や後で聴く機能とか必要なくて、なんかもうちょっと軽くて操作に集中できるUIのアプリないかなと探してもしっくりくるのがなかった。
そんな折、年末に新しいAndroid端末に変えてAndroid愛が高まったのと、ここ最近Android開発ご無沙汰だったので、最近の開発環境をキャッチアップするのも兼ねてPodcastアプリを作ってみました。

というわけで、先ほど78.9(7hack)というアプリをPlayStoreに公開しました。よかったら。
Podcastを「探す」「見る」「聴く」に特化した機能のみなので、自分のようにサッと登録してチャッと聴きたい人向け。
78.9というのは地元のFMラジオの周波数で、昔Nack5(79.5)をよく聴いていたのを思い出したのでモジってみた。

もちろんソースコードも公開しています
最近のSupportLibraryを結構使ってるのと、RxJava2とSQLBriteを組み合わせて使ってたりします。

個人で久しぶりにアプリ作ったけど、ユーザーが自分なのとUI自由に考えられて楽しい。
マネージメントメインになってから自分でUIを考える機会が減ったのでリハビリとしてもよかった。

開発メモ的な

自分が業務でAndroidの機能実装したの、2年前にMaterialDesign対応して以来なので、最近はSupportLibraryでのMaterialDesign表現が充実していてびっくりした。
特にCollapsingToolbarLayoutやFloatingActionButtonが標準で用意されてるなんて・・、キーラインもほとんど自前で定義する必要なくなってて進化すごい。

DBアクセス層

RSSフィードをSQLiteに保存したいが、生SQLiteHelperもアレだし何か使おうと思って探してみたところ、いい感じに薄そうだったSQLBriteを採用。
SQLBriteのいいところは、いい感じに薄いのでDAO層を作りやすいのと、Transactionが使いやすいところ。
逆にそうでもなかったところは、SQLBriteが標準で用意しているRxJavaサポートで、これはSelect文を発行するObservableをSubscribeしておくと、そのテーブルに行が挿入/削除など変更があったときに自動でSubscriberを実行してくれるというもの。
RecyclerViewでnotifyDataChangedでなくnotifyItemRemovedやnotifyItemChangedを使ってアニメーションさせたり変更を最小限にしようと思っても、Subscriberで再読み込みされてしまうので、実装が複雑になって相性がよくないと感じた。

結局、DAO層はSQLBrite#queryを愚直に使ってObservableを返さずEntityを返すのみにして、ビジネスロジックを扱うLogic層でDAOの結果を返すObservableを返すようにした。
どのみち、SQLBriteが依存しているRxJavaは1系なので、RxJava2を入れた時点でこうするしかない。
UI層から直接DAOを叩くことはしないので、実装的には見通しがよくなってよかった。

EpisodeDao

@Singleton
public class EpisodeDao {
    public List<Episode> findAll(Podcast podcast, int limit, int offset) {
        List<Episode> items = new ArrayList<>();
        Formatter formatter = new Formatter(new StringBuilder(), Locale.JAPANESE);
        String sql = formatter.format(LIST_QUERY, podcast.id, limit, offset).toString();

        Cursor cursor = db.query(sql);
        if (cursor == null) {
            return items;
        }
        try {
            while (cursor.moveToNext()) {
                // entityに詰める
            }
        } finally {
            cursor.close();
        }
        return items;
    }
}

EpisodeLogic

@Singleton
public class EpisodeLogic {
    public Observable<List<Episode>> list(Podcast podcast, int limit, int offset) {
        return Observable.just(episodeDao.findAll(podcast, limit, offset))
                .flatMap(this::withEnclosureCache)
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread());
    }

    private Observable<List<Episode>> withEnclosureCache(List<Episode> episodes) {
        return Observable.just(enclosureCacheDao.findAll(episodes))
                .map(enclosureCaches -> {
                    for (Episode episode : episodes) {
                        // set cache to episode
                    }
                    return episodes;
                });
    }
}

この辺をやってるところで、Ormaの存在に気づいてあーっとなった。
Ormaもいい感じに薄い上に、relationの定義も簡単だし、migrationも使いやすそう。多分次何か作る場合はOrmaを採用すると思います。

APIアクセス層

Retrofit2を採用してみた。
Retrofit1時代にSimpleXMLConverterを使ってみたことがあって、そのときは結構バギーな印象だったけど、今の所特に問題は起きてない。
宣言的なAPIクライアントとPOJOだけでよくなるのでXMLのパースをサクッと書けて最高でした。
話はずれるが、PodcastのATOMフィードは各番組でXMLの仕様が微妙に違っていて辛い。

StethoとLeakCanary

今回使ってみて感動した開発向けライブラリを2つ。

Stethoを使うとSQLiteの中身や通信の中身をChromeのdeveloper consoleで見ることができる。
(通信の中身を見る場合はOKHttp用のヘルパーを別途dependencyで指定する必要はある)
以前PonyDebuggerというのがあって、同様にChromeから通信内容見れたけど、それの高機能版という感じか。
SQLiteの中身をLocalStorage見る感覚で確認できるの最高。
使い方も簡単で、Applicationクラスで初期化するだけでOK。

public class MainApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Stetho.initializeWithDefaults(this);
    }
}

LeakCanaryはメモリリークを検出するライブラリ。
アプリを操作してメモリーリークが発生すると、通知されてどこでリークが発生したかが分かる。
ログを見てると、GCを走らせてactivityの参照が残ってないかをチェックしている?
リークが一定時間発生しなかったら「No leak!」と通知されて気持ちいい。
こちらもApplicationクラスで初期化するだけでOK。

public class MainApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        LeakCanary.install(this);
    }
}
@yo_waka

Software Engineer. Joined at freee K.K. since 2013. I like Ruby and JavaScript, but I really like Java.