AndroidアプリでNetworkOnMainThreadExceptionを避けつつTwitter4Jを使用したアプリ連携を行う #Twitter4J

  • 投稿日:
  • by
  • Category:

あけましておめでとうございます。今年もよろしくお願いします。今年のプログラミング初めはAndroidです。

[追記]Qiitaへ転記しました。更新はQiitaにて行っています:AndroidアプリでNetworkOnMainThreadExceptionを避けつつTwitter4Jを使用したアプリ連携を行う

Androidアプリ作成でTwitterを使用する際に欠かせないTwitter4Jですが、アプリの連携(認証認可)処理を適当に処理を書いていくとNetworkOnMainThreadExceptionが発生します。onCreateなどのメインスレッドでHttpURLConnectionなどの通信をする処理がある場合に発生する例外です。

今日は、この例外を避けつつアプリ連携を実現する方法の一つをご紹介します。

アプリ連携の方法

今回は、アプリ外のブラウザでPINを入力して連携する方法で作ってみました。他にはWebViewを用いてアプリ内のブラウザで処理する方法(WebViewの脆弱性に注意)や、コールバックURLを用いて処理する方法があります。

 PIN コールバックURL
外部ブラウザ 今回はここ(intent-filterを使用)
アプリ内のブラウザ(省略)(WebViewでURLを監視等)

連携のためのコード8行

連携のための一連の処理を見てみます。

// 準備
Twitter twitter = new TwitterFactory().getInstance();
twitter.setOAuthConsumer(consumerKey, consumerSecret);
twitter.setOAuthAccessToken(null);
RequestToken requestToken = twitter.getOAuthRequestToken(); // ここで通信
// このURLでブラウザで認証
String url = requestToken.getAuthorizationURL();

ここでユーザーが認証してPINコードを得ます。

// PIN入力
AccessToken accessToken = twitter.getOAuthAccessToken(pin); // ここでも通信
// トークンの取得
String token = accessToken.getToken();
String tokenSecret = accessToken.getTokenSecret();

これらのコード中に通信が発生する箇所が2つあります。これらをAsyncTaskLoaderLoaderCallbacksを使用して別のスレッドにしてみました。

AsyncTaskLoaderLoaderCallbacksの使い方についてはこのページが詳しいです。

解決のためのコード例

以下、コード例は抜粋です。

リクエストトークンを取得する

まず、リクエストトークンを取得するところ(Twitter#getOAuthRequestToken())を切り出すために、AsyncTaskLoaderを継承したTwitterOAuthRequestTokenLoaderを作成します。バインドするのはRequestTokenです。コンストラクタでContextTwitterをもらっておき、実行されるloadInBackgroundTwitter#getOAuthRequestToken()を実行します。その結果のRequestTokenを戻します。

public class TwitterOAuthRequestTokenLoader extends AsyncTaskLoader<RequestToken> {
    private Twitter mTwitter;
    public TwitterOAuthRequestTokenLoader(Context context, Twitter twitter) {
        super(context);
        mTwitter = twitter;
    }
    @Override
    public RequestToken loadInBackground() {
        RequestToken requestToken = null;
        try {
            requestToken = mTwitter.getOAuthRequestToken();
        } catch (TwitterException e) {
            requestToken = null;
        }
        return requestToken;
    }
}

TwitterOAuthRequestTokenLoaderを実行するのに、今度はLoaderCallbacksを実装したTwitterOAuthRequestTokenCallbacksを作成します。コンストラクタでContextTwitterをもらっておき、onCreateLoaderでそのままTwitterOAuthRequestTokenLoaderの生成時に渡します。onLoadFinishedの引数に結果のRequestTokenが来るので、RequestToken#getAuthorizationURL()で認証のURLを取得し、ブラウザへIntentで渡します。ユーザーはここで認証し、PINコードを得ます。

public class TwitterOAuthRequestTokenCallbacks implements LoaderCallbacks<RequestToken> {
    private Context mContext;
    private Twitter mTwitter;
    public TwitterOAuthRequestTokenCallbacks(Context context, Twitter twitter) {
        mContext = context;
        mTwitter = twitter;
    }
    @Override
    public Loader<RequestToken> onCreateLoader(int id, Bundle args) {
        TwitterOAuthRequestTokenLoader loader = new TwitterOAuthRequestTokenLoader(mContext, mTwitter);
        loader.forceLoad();
        return loader;
    }
    @Override
    public void onLoadFinished(Loader<RequestToken> arg0, RequestToken requestToken) {
        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(requestToken.getAuthorizationURL()));
        mContext.startActivity(intent);
    }
}

TwitterはConsumer keyを設定して用意しておきます。

mTwitter = new TwitterFactory().getInstance();
mTwitter.setOAuthConsumer(getString(R.string.twitter_consumer_key), getString(R.string.twitter_consumer_key_secret));
mTwitter.setOAuthAccessToken(null);

あとはActivity#getLoaderManager()LoaderManagerを取得して、LoaderManager#initLoader()TwitterOAuthRequestTokenLoaderを指定するだけです。

TwitterOAuthRequestTokenCallbacks oAuthRequestTokenCallbacks = new TwitterOAuthRequestTokenCallbacks(this, mTwitter);
getLoaderManager().initLoader(0, null, oAuthRequestTokenCallbacks);

アクセストークンを取得する

ブラウザが表示され、ユーザーが認証をすると、PINが表示されます。

PINからアクセストークンを取得するところ(Twitter#getOAuthAccessToken())についても上記のリクエストトークンを取得する場合と同様です。

サンプルプロジェクト

この方法でアプリ連携を行うアプリのプロジェクトをGitHubに置きました。

このエントリーをはてなブックマークに追加

コメントする