Picasso、Otto、Retrofitのソースコードを読んだ
最近はAndroidアプリを作っているのですが、うどん職人以降まともにAndroidアプリを作っていなかったので勉強のためにライブラリのソースコードを読みました。この記事は各ライブラリのファイルを読んで知ったことをまとめています。
ソースコードを読んだライブラリはSquare製のPicasso、Otto、Retrofitの3つです。
ライブラリについて
Picasso
画像をダウンロードしたりキャッシングしてくれる強力なライブラリです。
次のような一文だけでネットワークの画像を取得して表示してくれます。
// 画像を読み込んで画像を表示する
Picasso.with(context).load(url).into(imageView);
Otto
アプリケーションの異なる部分を分離しつつ通信を行うためのイベントバスに特化したライブラリです。
次のように書くことでイベントを発行したり、ハンドリングすることができます。
// イベントの発行 BusHolder.get().post("イベントを発行しました");
// イベントのハンドリング @Override protected void onResume() { super.onResume(); BusHolder.register(this); } @Subscribe public void subscribe(String str) { Log.d(TAG, str); }
Retrofit
JavaでHTTP APIのインターフェースを書くことでその実装を生成してくれるライブラリです。
次のようにインターフェースを書くだけで実装をしてくれるのでとても分かりやすいです。
// APIインターフェースの定義 public interface APIService { @GET("/group/{id}/users") Call<List<User>> groupList(@Path("id") int groupId); }
// APIの実装 Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://example.com") .build(); APIService service = retrofit.create(APIService.class); Call<List<User>> list = service.groupList(1);
1系と2系があるのですが、これから主流になってくる2系βのソースコードを読みました。
それではざっくりですが、ここから1つずつライブラリを見ていきます。
3つのライブラリとも基本的なフォルダやファイルの構成は同じなので重複する点は飛ばしています。
ライブラリのソースコードを読む
Picasso
deploy_website.sh
- 最新のバージョンをデプロイしたい時はこのスクリプトを実行するだけで簡単にできるようになっている
- 最新のjavadocの取得や日付け入りのcommit、Githubページの更新までやってくれる
- websiteもJavaScriptでmavenからバージョンをとってくるようになっていてすごく良い
PollexorRequestTransformer.java
- Thumbor(画像を変換してくれるサービス)で使うためのプラグイン
- 次のように書くことで画像をそのサービスに渡して変換してくれる
// Thumborで画像を変換する RequestTransformer transformer = new PollexorRequestTransformer("http://example.com", "secretpassword"); Picasso p = new Picasso.Builder(context) .setRequestTransformer(transformer) .build();
RequestHandler.java
- 画像の読み込みの基本的なインターフェースのクラス
- このクラスを実装することでネットワークやAssetから読み込むためのクラスを作ることができる
- 次はこのクラスを実装した画像を読み込むためのクラスの一覧です
- ResourceRequestHandler.java: リソースファイルを読み込む
- AssetRequestHandler.java: アセットファイルを読み込む
- ContactsPhotoRequestHandler.java: 連絡帳にある写真を読み込む
- ContentStreamRequestHandler.java: スキーマがcontentの画像を読み込む
- FileRequestHandler.java: スキーマがfileの画像を読み込む
- MediaStoreRequestHandler.java: スキーマがcontentかつAuthorityがmediaの画像を読み込む
- NetworkRequestHandler.java: ネットワーク上からファイルを読み込むためのクラス
BitmapHunter.java
- キャッシュなどのポリシーに沿ってBitmapの読み込みを制御するクラス
Action.java
Dispatcher.java
- アクションの状態に応じて次のクラスに渡す仲介を行っているクラス
Cache.java
- キャッシュの基本的なインターフェースを持つクラス
- このクラスを実装したメモリでキャッシュを行う`LruCache.java`がある
- キャッシュの上限を越えた時は過去のモノから削除していく
Downloader.java
- 画像をダウンロードするための基本的なインターフェースのクラス
- 次のようにどの通信クライアントを使うかで実装がそれぞれ用意してある
- どちらの通信クライアントを使うかはOkHttpがインストールされているかで判定している
// 通信クライアントを取得する static Downloader createDefaultDownloader(Context context) { if (SDK_INT >= GINGERBREAD) { try { Class.forName("com.squareup.okhttp.OkHttpClient"); return OkHttpLoaderCreator.create(context); } catch (ClassNotFoundException ignored) { } } return new UrlConnectionDownloader(context); }
Otto
AnnotatedHandlerFinder.java
// クラスに定義されたメソッド一覧を取得する for (Method method : class.getDeclaredMethods) {
//メソッドにアノテーションがついているかの判定をする method.isAnnotationPresent(Subscribe.class)
- またメソッドに定義されているパラメータは次のように取得できる
// メソッドに定義されたパラメータを取得する
method.getParameterTypes();
Bus.java
- イベントを発行したりハンドラーの登録をするためにOttoで主に使うクラス
- イベントのハンドリングがない時はDeadEventクラスでラップしてイベントが再発行される
- デバッグの時にはこのDeadEventをハンドリングしてハンドリング漏れを見つける
- ハンドリングするメソッドを探す時は次のように親のクラスも辿っていく
// 親のクラスを辿っていく方法 Set<Class<?>> classes = new HashSet<Class<?>>(); parents.add(startClass); while (!parents.isEmpty()) { Class<?> clazz = parents.remove(0); classes.add(clazz); Class<?> parent = clazz.getSuperclass(); if (parent != null) { parents.add(parent); } } return classes;
- clazzは予約語のclassを避けるためにsを逆にした変数名
Retrofit
OkHttpCall.java
- 通信クライアントにOkHttpを用いてリクエストを行うクラス
- レスポンスコードが200より小さいか300以上の時はエラーのレスポンスになる
- NoContentResponseBodyクラスを使って先にレスポンスコードだけを判定している
CallAdapter.java
- Callクラスの実行を行うインターフェースのクラス
- プラットフォームによってどのスレッドでリクエストのコールバックを実行するかが変わる
Platform.java
- プラットフォームによって変わる処理を書くためのクラス
- 次のように特定のクラスがあるかどうかでプラットフォームの判定をしている
// プラットフォームの判定をする private static Platform findPlatform() { try { Class.forName("android.os.Build"); if (Build.VERSION.SDK_INT != 0) { return new Android(); } } catch (ClassNotFoundException ignored) { } try { Class.forName("java.util.Optional"); return new Java8(); } catch (ClassNotFoundException ignored) { } return new Platform(); }
Converter.java
番外編
- OkHttpを使うことで指定のレスポンスを返すモックサーバを次のように起動できる
// モックサーバを起動してみよう MockWebServer server = new MockWebServer(); server.start(); server.enqueue(new MockResponse() .setResponseCode(404) .setBody("{\"message\":\"Unable to locate resource\"}"));
ライブラリを読んで
3つのライブラリのソースコードを読みました。なぜこの3つのライブラリかというと既に作っているアプリで採用していて、最近のアプリで採用している事例が多いなと感じたからです。しかし、ソースコードを読むとライブラリは魔法ではなく努力の結晶であることを知ったり、ブラックボックスだった部分を知ることができライブラリの偉大さを知ることができました。また、今まで知らなかったAPIがあったり、拡張性が高いコードを書くことで複雑な処理を簡潔にまとめていたりして凄さを身に感じました。
こういう立派なライブラリを目指しつつ、地道にAndroidについてハマっていきたいと思います。
今年中に1つでもAndroidに関するライブラリを作ってみます!