Twitter4J を使って、Android アプリ上で Twitter のフォロー機能を実現しようとした時に少しはまったので、その解決方法のメモを置いておきます。
次の followTwitterUser()
メソッドを実行すると android.os.NetworkOnMainThreadException
エラーが発生し、アプリが強制終了してしまいました。なぜ?
public void followTwitterUser(String username) {
// Twitter インスタンスの取得
Twitter twitter = TwitterUtils.getTwitterInstance(this);
// TwitterUtils クラスは以下のページを参照:
// Android再入門 - Twitterクライアントを作ってみよう
// http://qiita.com/gabu/items/f6f39900fd5e449045f9
try {
// Twitter フォロー実行
twitter.createFriendship(username);
} catch (TwitterException e) {
e.printStackTrace();
}
}
String username = "twitter";
followTwitterUser(username);
エラーの内容は以下の通り。
FATAL EXCEPTION: main
android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1133)
at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl$SSLOutputStream.write(OpenSSLSocketImpl.java:708)
at java.io.BufferedOutputStream.flushInternal(BufferedOutputStream.java:185)
at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:85)
at libcore.net.http.HttpEngine.readResponse(HttpEngine.java:824)
at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:283)
at libcore.net.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:497)
at libcore.net.http.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:134)
at twitter4j.HttpResponseImpl.<init>(HttpResponseImpl.java:35)
at twitter4j.HttpClientImpl.handleRequest(HttpClientImpl.java:143)
at twitter4j.HttpClientBase.request(HttpClientBase.java:53)
at twitter4j.HttpClientBase.post(HttpClientBase.java:82)
at twitter4j.TwitterImpl.post(TwitterImpl.java:1986)
at twitter4j.TwitterImpl.createFriendship(TwitterImpl.java:480)
(以下省略)
エラー中にある AndroidBlockGuardPolicy.onNetwork
というキーワードで調べてわかったのですが、どうやら Android 4.0 以降は、Main スレッド( UI スレッド)上でネットワーク通信を行うとこのエラーが発生するようです。
このエラーを回避するには、ネットワーク通信を別スレッド(サブスレッド)で実行する必要があります。サブスレッドで非同期処理を行う AsyncTask
を作って doInBackground()
コールバック内でネットワーク通信が必要な処理(Twitterのフォロー)を実行するように書き換えます。
// サブスレッドから参照できるように Twitter クラスのインスタンスを保持する変数はメンバ変数にしておく
// 例:
// public class TwitterActivity extends Activity {
//
// private Twitter mTwitter = null;
// // ...(以下省略)
// }
public void followTwitterUserAsync(String username) {
// Twitter インスタンスの取得
mTwitter = TwitterUtils.getTwitterInstance(this);
// サブスレッドで実行するタスクを作成
AsyncTask<String, Void, Boolean> task = new AsyncTask<String, Void, Boolean>() {
@Override
protected Boolean doInBackground(String... params) {
username = params[0];
try {
// Twitter フォロー実行
mTwitter.createFriendship(username);
return true;
} catch (TwitterException e) {
e.printStackTrace();
return false;
}
}
@Override
protected void onPostExecute(Boolean followResult) {
if (followResult) {
// フォロー成功時の処理
} else {
// フォロー失敗時の処理
}
}
};
task.execute(username);
}
String username = "twitter";
followTwitterUserAsync(username);
これで Twitter フォローができるようになりました。
ネットワーク通信を伴う処理はサブスレッドで実行する、というのがミソですね。