AndroidでTwitterを使ったアプリを作る時はTwitter SDKを使うのが便利です。導入方法はDocsに詳しいのでそちらを見ていただくとして、Twitter SDKに内包されているTwitterApiClientに関するちょっとしたTipsについて書きます。
レスポンスをObservableにする
これはTipsとも呼べない当たり前の話なのですが。TwitterApiClientは内部的にはRetrofitが実装されています。そのため、レスポンスをObservableにすることが可能です。
まずはdependenciesにRxJavaを導入してあげます。
dependencies {
compile('com.twitter.sdk.android:twitter:1.13.0@aar') {
transitive = true;
}
compile 'io.reactivex:rxjava:1.1.0'
compile 'io.reactivex:rxandroid:1.1.0'
}
で、あとはTwitterApiClientクラスを継承したCustomApiClientを作ってあげます。
class CustomApiClient extends TwitterApiClient {
public CustomApiClient(TwitterSession session) {
super(session);
}
public CustomService getCustomService() {
return getService(CustomStatusesService.class);
}
public interface CustomStatusesService {
@GET("/1.1/statuses/show.json")
Observable<Tweet> show(@Query("id") Long id,
@Query("trim_user") Boolean trim_user,
@Query("include_my_retweet") Boolean include_my_retweet,
@Query("include_entities") Boolean include_entities);
}
}
Observableにすると引数にCallbackが不要になるのが嬉しいですね。
CustomTwitterApiClient client = CustomTwitterApiClient.getInstance();
client.getCustomStatusesService().show(id, null, null, null)
.subscribe(...); //ゴニョゴニョできる
受け取った結果をバックグラウンドで処理する
TwitterApiClientはリクエストをメインスレッドに返す
Tipsと言いながら、こっちが今回の主題。TwitterApiClientの謎仕様として、かならずレスポンスがメインスレッドに返るようになっています。この結果、例えばまとめてデータを受け取って、それをDBに格納したいといった場合に、一回メインスレッドを経由しなくてはならず、場合によってはUIスレッドをブロックするようになります。
なぜそのようになっているのかはTwitterApiClientのソースコードを追っていくと判明します。
TwitterApiClient(TwitterAuthConfig authConfig, Session session, TwitterApi twitterApi, SSLSocketFactory sslSocketFactory, ExecutorService executorService) {
//中略
this.apiAdapter = (new Builder())
.setClient(new AuthenticatedClient(authConfig, session, sslSocketFactory))
.setEndpoint(twitterApi.getBaseHostUrl()).setConverter(new GsonConverter(gson))
.setExecutors(executorService, new MainThreadExecutor())
.build();
//後略
}
}
これがTwitterApiClientのコンストラクタです。ちょっとわかりにくいのですが、Apiの処理を司るapiAdapterを作る時に、BuilderクラスでsetExecutors()
を使ってExecutorを定義しているとわかります。
public Builder setExecutors(Executor httpExecutor, Executor callbackExecutor) {...}
TwitterApiClientだとCallbackExecutorがMainThreadExecutorが設定されているために、必ずメインスレッドに返るようになっています。この値をnullにするとhttpリクエストと同じスレッド(特にスレッドを定義しなかった場合は、Retrofit-Idleというスレッド)に結果が返るようになります。
バックグラウンドで処理するには
TwitterApiClientを継承してexecutorを再定義します。
httpリクエストを自体をスレッド化している場合はnullでよいですし、httpリクエストが順次呼ばれるような実装の場合は適宜スレッドを定義してあげる必要があります。多くの場合はhttpリクエストは別スレッドで呼ぶので、nullで良いはずです。
public class CustomTwitterApiClient extends TwitterApiClient {
private CustomTwitterApiClient(TwitterCore twitterCore){
super(twitterCore.getSessionManager().getActiveSession());
Gson gson = (new GsonBuilder()).registerTypeAdapterFactory(new SafeListAdapter()).registerTypeAdapterFactory(new SafeMapAdapter()).create();
AuthenticatedClient client
= new AuthenticatedClient(twitterCore.getAuthConfig(), twitterCore.getSessionManager().getActiveSession(), twitterCore.getSSLSocketFactory());
RestAdapter adapter = (new RestAdapter.Builder())
.setClient(client)
.setEndpoint(new TwitterApi().getBaseHostUrl())
.setConverter(new GsonConverter(gson))
.setExecutors(TwitterCore.getInstance().getFabric().getExecutorService(), null).build(); //nullを設定
}
上記のようにnullを設定します。このCustomClientを呼ぶ時は返り値の実行スレッドを定義します。RxJavaを使う場合はobservableOnを定義することになります。
final CustomTwitterApiClient client = CustomTwitterApiClient.getInstance();
client.getCustomStatusesService()
.userTimeline(...)
.observeOn(Schedulers.from(threadPoolExecutor)) //受け取った結果の実行スレッドを定義
.subscribe(...) //結果の実行
実際に実装してみたものはこちらのレポジトリにあります。Twitterから3000件のデータを取得しRealmに格納するというサンプルアプリです。