12
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

updated at

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

MarkdownとQiitaの練習として投稿します。この内容は私のブログ記事と同等の内容です。[追記]2か所の更新は大変なので、Qiitaのみ更新としました。


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つあります。これらをLoaderLoaderManager.LoaderCallbacksを使用して別のスレッドにしてみました。

[追記]Android Support Libraryを使用するようにして、Android2.x系にも対応しました。

解決のためのコード例

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

まず、リクエストトークンを取得するところ(Twitter#getOAuthRequestToken())を切り出すために、AsyncTaskLoaderを継承したクラスを作成します。バインドするのはRequestTokenです。コンストラクタでTwitterを渡し、loadInBackground()でリクエストトークン取得を実行します。実行の戻り値をそのまま返します。

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;
    }
}

上のローダークラスを実行し、結果を処理するために、今度はLoaderCallbacksを実装したコールバッククラスを作成します。こちらもコンストラクタでAPIキーをセットしたTwitterを渡し、onCreateLoader()でそのままローダークラスのインスタンス生成時に渡します。onLoadFinished()の第2引数のRequestTokenが処理結果なので、RequestToken#getAuthorizationURL()でTwitter認証の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) {
        Loader<RequestToken> loader = new TwitterOAuthRequestTokenLoader(mContext, mTwitter);
        loader.startLoading();
        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);
    }
}

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

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

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

使う

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

mTwitter = new TwitterFactory().getInstance();
mTwitter.setOAuthConsumer(KEY), KEY_SECRET);
mTwitter.setOAuthAccessToken(null);

あとはコールバッククラスのインスタンスを生成し、

FragmentActivity#getLoaderManager()LoaderManagerを取得し、LoaderManager#initLoader()にコールバッククラスを指定するだけです。

LoaderManager.LoaderCallbacks<RequestToken> requestTokenCallbacks = new TwitterOAuthRequestTokenCallbacks(this, mTwitter);
getSupportLoaderManager().initLoader(0, null, requestTokenCallbacks);

[追記]上記のように2.x系の互換性のためにAndroid Support Library内のLoaderを使用するように変更する場合は、LoaderManagerの不一致があるので、Activityは継承先をFragmentActivityに変更する必要があります。

サンプルプロジェクト

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
12
Help us understand the problem. What are the problem?