あけましておめでとうございます。今年もよろしくお願いします。今年のプログラミング初めは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つあります。これらをAsyncTaskLoader
とLoaderCallbacks
を使用して別のスレッドにしてみました。
AsyncTaskLoader
とLoaderCallbacks
の使い方についてはこのページが詳しいです。
解決のためのコード例
以下、コード例は抜粋です。
リクエストトークンを取得する
まず、リクエストトークンを取得するところ(Twitter#getOAuthRequestToken()
)を切り出すために、AsyncTaskLoader
を継承したTwitterOAuthRequestTokenLoader
を作成します。バインドするのはRequestToken
です。コンストラクタでContext
とTwitter
をもらっておき、実行されるloadInBackground
でTwitter#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
を作成します。コンストラクタでContext
とTwitter
をもらっておき、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に置きました。
コメントする