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 通信を行う為のライブラリとして、HttpURLConnection API が提供されています。(Android 4.4以前ではApache HttpClientも提供されていましたが、最近のAndroidでは提供されていません)
Get リクエストや Post リクエストの為の様々な API が用意されており、これを用いて、様々な Web サービスの API にリクエストを投げ、そのレスポンスを以って何らかの処理をするような使い方をします。
ただし、HttpURLConnectionを直接利用することはあまりなく、実際は OkHttpなどを利用してAPIにアクセスすることが多いでしょう。

HttpURLConnectionは、Http 通信のための基本的な API を提供しています。よって、I/O ストリームを用いた通信の入出力処理を実装する必要があります。

非同期処理

ネットワーク接続の処理は、予測不能な遅延を含む可能性があります。
このため、ネットワーク接続の処理は 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();
        }

実習と課題

プロジェクトの開き方は課題プロジェクトの開き方を参照してください。

実習

この実習では 1-9-NetworkPracticeを使用します。

  1. NetworkActivityのTODO部分にHttpURLConnectionを利用したネットワーク通信処理を実装し、取得できたhtmlの文字列を画面に表示してください。

課題

この課題では 1-9-NetworkAssignmentを使用します。

  1. github.comからandroidで検索をした時にstarの多いRepository10個のRepository名を取得し表示してください。