倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

Twitter4J の非同期 API 試し隊

Twitter4J のサンプルを書いていて、非同期 API 使った方が良さそうだなぁという場面に出くわしたので、ちょっと 非同期 API を試してみることに。

非同期 API 概要


非同期 API を使用する大まかな手順は

  1. AsyncTwitterFactory オブジェクトを生成
    • c.f. 同期 API では TwitterFactory オブジェクトを生成
  2. AsyncTwitterFactory オブジェクトから getInstance() メソッドによって AsyncTwitter オブジェクトを生成
    • c.f. 同期 API では Twitter オブジェクトを生成
  3. TwitterListener オブジェクトの作成
  4. AsyncTwitter オブジェクトに対して addListener() メソッドによって TwitterListener オブジェクトを追加*1
  5. AsyncTwitter オブジェクトを介して twitter の処理を行う
    • c.f. 同期 API では Twitter オブジェクトを介して

といった感じです。 概ね

  • TwitterFactory → AsyncTwitterFactory
  • Twitter → AsyncTwitter

となっているだけですが、TwitterListener インターフェースインスタンスの作成・設定が同期 API との違いです。

TwitterListener インターフェース

TwitterLisener インターフェースは、(AsyncTwitter を介して) twitter に何らかの処理を行った後、そのレスポンスに対して処理を続行したい場合に使用します。 そう言えば JavaDoc 見ると、AsyncTwitter インターフェースには Twitter インターフェースと同名、同引数のメソッドが定義されてますが、返り値は全て void になってますね。 AsyncTwitter のどのメソッドを呼ぶと、レスポンスが TwitterListener のどのメソッドに返されるのかはメソッド名を見比べて判断するしかないかと思います(大抵簡単に分かります)。 

で、TwitterListener インターフェースにはたくさんのメソッドが定義されていますが、1つの TwitterListener を作るのにそれら全てを実装するのは面倒過ぎるので、通常は TwitterAdapter クラスを使用して TwitterListener オブジェクトを作成します。 メソッドを1つ2つしか実装しないなら、無名クラスで充分でしょう。 使い方は以下のサンプル・コードを参照のこと。

サンプル・コード


では、非同期 API のサンプルを見ていきましょう。 OAuth 認証に必要な twitter4j.properties は既に用意されているとします(設定はこちら参照)。

ステータスの更新

同期 API でステータスを更新する方法はこちら。 非同期 API では以下のようにします:

@Grab('org.twitter4j:twitter4j-async:2.2.5')

import twitter4j.*

def twitter = new AsyncTwitterFactory().getInstance()

// TwitterListener オブジェクトの作成
def listener = new TwitterAdapter() {
    @Override
    void updatedStatus(Status status){
        println "Successfully updated the status to [$status.text]."
    }

    @Override
    void onException(TwitterException ex, TwitterMethod method) {
        println method.name()
        ex.printStackTrace()
    }
}

twitter.addListener(listener)    // AsyncTwitter に TwitterListener を設定
twitter.updateStatus('async twitter test.')

Thread.sleep(10000)    // 処理完了待ちなぅ
  • 非同期 API を使うのに必要なライブラリは twitter4j-async です。 同期 API が定義されている twitter-core も使用されますが、twitter-async から辿ってダウンロードされるので特に書く必要はありません。
  • 最後の「Thread.sleep()」がないと、処理の完了を待たずにメインスレッドが終了し、それに伴って非同期で実行されるハズの処理も終了されてしまうようです。 ここでは10秒(=10000ミリ秒)だけスリープしてますが、それが多いか少ないかは環境によるかと。 非同期実行しているスレッドに対する join() メソッドみたいなのがあれば便利かも*2

まぁ、コードのほとんどが TwitterListener (というより TwitterAdapter)の作成部分に費やされてます。

ユーザーリストを作成してメンバーを加える

次は「ユーザーリストを作成してメンバーを加える」サンプル。 同期 API の場合はこちら参照。 「ユーザーリストの作成」と「ユーザーリストへのユーザーの追加」が別々のリクエストになるためか、同期 API だとうまくいかない(ユーザーリストが2つ作成されるなど)場合があります。 非同期 API ならそういったことは起こらないんでしょう:

@Grab('org.twitter4j:twitter4j-async:2.2.5')

import twitter4j.*

def twitter = new AsyncTwitterFactory().getInstance()

def members = [
    'scandal_haruna',
    'scandal_tomomi',
    'scandal_mami',
    'scandal_rina'
] as String[]

// TwitterListener オブジェクトの作成
def listener= new TwitterAdapter() {
    @Override
    void createdUserList(UserList userList) {
        println "Created user list : $userList.name"
        println "Member count : $userList.memberCount"

        // AsyncTwitter オブジェクトを再度使って処理をリクエスト
        twitter.addUserListMembers(userList.id, members)
    }

    @Override
    void addedUserListMembers(UserList userList){
        println "Added users to user list : $userList.name"
        println "Member count : $userList.memberCount"
    }

    @Override
    void onException(TwitterException ex, TwitterMethod method) {
        println method.name()
        ex.printStackTrace()
    }
}

twitter.addListener(listener)    // AsyncTwitter に TwitterListener を設定
twitter.createUserList('scandal', true, 'THE Girls Band.')

Thread.sleep(20000)    // 処理完了待ちなぅ
  • AsyncTwitterFactory オブジェクトは使い回しが利くように実装されているそうですが、AsyncTwitter オブジェクトもこのサンプルでやっている程度には使い回しが利くようです。

Twitter API ポケットリファレンス (POCKET REFERENCE)

Twitter API ポケットリファレンス (POCKET REFERENCE)

*1:Twitter4J のサイトの「コードサンプル」では、AsyncTwitterFactory#getInstance() メソッドに引数として TwitterListener を渡すようにしてありますが、twitter4j-async 2.2.5 では(おそらくもっと以前に)変更になっているようです。

*2:存在しているスレッドを調べて、そのスレッドに対して join() メソッドを呼び出す、ってこともできますがサンプル・コードとして脱線しすぎなのでやめました。