View on GitHub
mixi-inc/AndroidTraining
ネットワーク通信
この章では、ネットワーク通信について解説します。

参考:HttpURLConnection | Android Developers

目次

ネットワーク通信の準備

パーミッション

ネットワークアクセスを行うためには AndroidManifest ファイルにネットワークアクセスを許可するパーミッションを追記する必要があります。 このパーミッションがない場合には、ネットワーク接続がエラーとなります。

<uses-permission android:name="android.permission.INTERNET" />

また、ネットワークの接続状態を確認するためには、以下のパーミッションが必要です。

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

このパーミッションを取得することで、Wifi または 3G などのネットワーク接続が可能な場合は通信を行い、接続が不可能な場合はオフラインモードでアプリケーションを動作させる、等のハンドリングが可能となります。

ネットワークの接続状態を知る

ネットワークの接続状態を確認するには、ConnectivityManagerを使用します。
これを以って、以下の様なユースケースに対応することが出来るようになります。

  • ネットワークに接続されていない場合は、ローカルにキャッシュされているデータを表示する
  • ファイルサイズが大きいためwifiに接続されている場合のみダウンロードする
public class MyActivity extends Activity {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // do something...

        // Context 経由でインスタンスを取得する
        ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
        NetworkInfo info = cm.getActiveNetworkInfo();
        if (info.isConnected()) {
            Toast.makeText(this, info.getTypeName() + " connected", Toast.LENGTH_LONG).show();
        }
    }
}

接続されているネットワークの種別を知りたい場合は、NetworkInfo.getType()で取得することができます。
wifiに接続時のみに動作させたい処理は下記のように記述することで実装できます。

        ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
        NetworkInfo info = cm.getActiveNetworkInfo();
        if (info.getType() == ConnectivityManager.TYPE_WIFI) {
            // wifi時のみ動作する処理を記述
        }

Http 通信のためのライブラリと通信処理

Android では、Http 通信を行う為のライブラリとして、HttpURLConnectionApache HttpClientの 2 種類の API が提供されています。
Get リクエストや Post リクエストの為の様々な API が用意されており、これを用いて、様々な Web サービスの API にリクエストを投げ、そのレスポンスを以って何らかの処理をするような使い方をします。
Http 通信ですので、html を取得する用途にも用いることができますが、このような場合には、WebView を用いるほうが一般的です。

HttpURLConnectionは、Http 通信のための基本的な API を提供しています。よって、I/O ストリームを用いた通信の入出力処理を実装する必要があります。
Apache HttpClientは、よりレイヤの高い実装となっており、入出力処理が抽象化されています。

Froyo より前の OS では、HttpURLConnectionにはコネクションプールを汚染する深刻なバグが含まれているため、Apache HttpClientの使用が推奨されています。
GingerBread 以降、HttpURLConnectionのサポートが強化され、Apache HttpClientの置き換えが推奨されるようになりました。
Android 2.2 をサポート端末に含める場合はApache HttpClientの使用を推奨しますが、そうでなければ、HttpURLConnectionを使用するのがよいでしょう。

こちらにより詳細な説明が記載されています。

非同期処理

ネットワーク接続の処理は、予測不能な遅延を含む可能性があります。
このため、ネットワーク接続の処理は UIThread 実施してはいけません。
よって、ネットワーク接続は基本的に前章の非同期処理と組み合わせて使うことになります。

HttpURLConnection

HttpURLConnection は、指定された URL に接続してデータの送受信をするための Client です。
API がシンプルで、サイズが小さいのでリソースに限りがある Android に適しています。

Froyo より前の OS における、特定の条件でのコネクションプール汚染問題は、下記のようなコードで回避ができます。

private void disableConnectionReuseIfNecessary() {
    // HTTP connection reuse which was buggy pre-froyo
    if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) {
        System.setProperty("http.keepAlive", "false");
    }
}

HttpURLConnection の使用手順は下記のとおりです。

  1. URL#openConnection()を呼び出し、接続を開始する
    URL#openConnection()で取得できる型はURLConnection型なので、HttpURLConnection型へキャストする必要があります。
  2. 必要なヘッダーを設定する
    Session cookie や認証情報、Content-type などを必要に応じて設定します。
  3. リクエストに body を設定する場合は、HttpURLConnection#setDoOutput(true)で body が存在することを明示する
    接続を確立後、getOutputStream() で取得したOutputStreamに body の内容を書き込みます。
  4. connect()で接続を確立する
    HttpURLConnection#setDoOutput(false)とした場合、この段階で getInputStream() でレスポンスを取得できるかと思います。 HttpURLConnection#setDoOutput(true)とした場合、getOutputStream() で取得したOutputStreamを close() するまでデータ送信中の状態となるため、OutputStreamへの書き込みが完了したら close() を呼び出すようにしてください。
  5. レスポンスを getInputStream() で取得する

上記のような手順でサーバーからのレスポンスを得ることができます。

ただし、実際には getInputStream() の前に getResponseCode() でステータスコードなどを確認し、必要に応じてエラーハンドリングなどを行う必要があります。
また、実際のアプリケーションでは、HttpURLConnectionをラッピングするクラスを作成し、アクセスする API に対応する Client の実装を経由して行うと良いでしょう。

GET処理

        URL url = new URL("http://mixi.jp");
        HttpURLConnection connection = null;
        try {
            connection = (HttpURLConnection) url.openConnection();
            connection.connect();
            InputStream is = connection.getInputStream();

            StringBuilder src = new StringBuilder();
            while (true) {
                byte[] line = new byte[1024];
                int size = is.read(line);
                if (size <= 0)
                    break;
                src.append(new String(line, "euc-jp"));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally{
            connection.disconnect();
        }

POST処理

        URL url = new URL("http://mixi.jp");
        HttpURLConnection connection = null;
        try {
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);

            String postData = "hoge=fuga&piyo=test";
            OutputStream os = connection.getOutputStream();
            os.write(postData.getBytes());
            os.flush();
            os.close();

            InputStream is = connection.getInputStream();

            StringBuilder src = new StringBuilder();
            while (true) {
                byte[] line = new byte[1024];
                int size = is.read(line);
                if (size <= 0)
                    break;
                src.append(new String(line, "euc-jp"));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally{
            connection.disconnect();
        }

Apache HttpClient

Apache HttpClientは、Apacheによって開発が進められている Http 通信ライブラリの総称です。
HttpURLConnectionよりも扱いやすいですが、Android での利用はHttpURLConnectionが推奨されいます。
また、Multipart への対応が無いバージョンのものが使用されているため、Multipart への対応を行う場合には別途自前で実装が必要です。

GET の処理は下記のようになります。

        HttpClient client = new DefaultHttpClient();
        try {
            client.execute(new HttpGet("http://mixi.jp"),
                    new ResponseHandler<String>() {
                        public String handleResponse(HttpResponse response)
                                throws ClientProtocolException, IOException {
                            return EntityUtils.toString(response.getEntity());
                        }
                    });
        } catch (IOException e) {
            e.printStackTrace();
        }

実習と課題

  1. (実習) HttpURLConnectionを利用してmixi.jpへアクセスをし、取得できたhtmlの文字列を画面に表示してください。
  2. (実習) HttpClientを利用して実習1と同様のことを行なってください。
  3. (課題) youtubeで日本で前日の評価が最も高い動画のタイトルをAPIで取得し表示してください。