ひだまりソケットは壊れない

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

Android アプリ開発独習メモ #3 — ネットワーク操作

今回は少し飛んで “Building Apps with Connectivity & the Cloud” の “Performing Network Operations” の内容を読んだ。 この資料では例としてサンプルプロジェクトも提供されているので、それを見ながら読み進めるとわかりやすい。

ネットワークへの接続

ネットワーク接続をアプリが行うには、manifest に適切な permission が必要。 このレッスンで使われる機能に関していうと、manifest に次の permission を含めておけばよい。

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

HTTP クライアントの選択

ネットワーク通信を行うほとんどの Android アプリは HTTP でやり取りする。 Android は 2 つの HTTP クライアントを含んでいる。

どちらも HTTPS やストリーミングアップロードとダウンロード、タイムアウトの設定や IPv6、そしてコネクションプールをサポートしている。 Gingerbread (Android 2.3) 以降をターゲットにしている場合は HttpURLConnection が推奨されているらしい。 このトピックに関する議論の詳細は下記ページ。

ネットワーク接続の確認

アプリでネットワーク接続を使用する前に、ネットワーク接続が利用可能かどうかを確認すべき。 下記のメソッドを使う。

    ConnectivityManager connMgr = (ConnectivityManager) 
        getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
    if (networkInfo != null && networkInfo.isConnected()) {
        // fetch data
    } else {
        // display error
    }

デバイスがネットワーク接続できない場所にあったり、ユーザーがネットワーク接続を無効にしていることもあることを忘れてはいけない。 次の節で詳しく見ていく。

別スレッドでネットワークに関する操作を行う

  • ネットワーク操作は遅延しうるので、ユーザー体験を損なわないように UI とは別のスレッドでネットワーク操作を行うべき
  • UI スレッドとは別のスレッドでタスクを実行するためのもっとも簡単な方法の 1 つが AsyncTask クラス を使うこと
  • 詳細 : Multithreading For Performance | Android Developers Blog

ネットワーク操作中に Activity が終了させられた場合のこととか考えると難しそうだけど、この資料には書いてなかった。 そこら辺に関しては “Processes and Threads” とか読むと良さそう。

ネットワーク接続してデータの取得

別スレッドで実行することさえできれば、あとは HttpURLConnection を使って HTTP 接続してデータの取得をすればよい。 簡単。

        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setReadTimeout(10000 /* milliseconds */);
        conn.setConnectTimeout(15000 /* milliseconds */);
        conn.setRequestMethod("GET");
        conn.setDoInput(true);
        // Starts the query
        conn.connect();
        int response = conn.getResponseCode();
        Log.d(DEBUG_TAG, "The response is: " + response);
        is = conn.getInputStream();

InputStream からのデータの取り出しは特に Android 特有の部分はないので Java を知ってればそのままやればよい。

Managing Network Usage

ネットワークリソース使用についてきめ細かな制御を行うアプリの書き方の説明。 多くのネットワーク通信を行う場合、アプリの設定としてネットワークに関する様々な設定項目を提供すべきである (例えば、WiFi 接続の場合にのみ通信するか、あらゆる場合に通信を許可するか、といった設定)。 ダウンロードやネットワーク接続のバッテリーに対する影響を最小化するための一般的なガイドラインとしては下記ページを参照。

端末のネットワーク接続の確認

端末は様々な種類のネットワーク接続が可能である。ネットワークの種類のすべてのリストは ConnectivityManager | Android Developers に書かれている。

アプリがネットワーク接続を使用する前に、ネットワーク接続の状態を確認すべき (前のレッスンでも軽く触れているが)。 次のクラスを使う。

  • [:title=ConnectivityManager] : ネットワーク接続の状態に対する問い合わせに答える。 また、ネットワーク接続の状態が変化したときに通知する。
  • [:title=NetworkInfo] : 指定の種類のネットワークインターフェイスの状態について説明する。
private static final String DEBUG_TAG = "NetworkStatusExample";
...      
ConnectivityManager connMgr = (ConnectivityManager) 
        getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI); 
boolean isWifiConn = networkInfo.isConnected();
networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
boolean isMobileConn = networkInfo.isConnected();
Log.d(DEBUG_TAG, "Wifi connected: " + isWifiConn);
Log.d(DEBUG_TAG, "Mobile connected: " + isMobileConn);

ネットワーク操作を行う前には、常に isConnected メソッド を使って状態を確認すべき。 より簡潔な方法は次のとおり。

public boolean isOnline() {
    ConnectivityManager connMgr = (ConnectivityManager) 
            getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
    return (networkInfo != null && networkInfo.isConnected());
}

よりきめ細かな状態取得のために NetworkInfo.DetailedState を使うことができるが、必要となることはめったにないだろう。

Manage Network Usege

ネットワーク接続とネットワーク使用の管理が可能なアプリの開発のために、manifest に正しい permission と intent filter を書く必要がある。

資料の例では次の permission が使われている。

また、資料では次の intent filter が使われている。

  • ACTION_MANAGE_NETWORK_USAGE : Android 4.0 で導入された。 ネットワークの使用法を設定するための Activity をアプリが持っているならば、その Activity の intent filter にこれを記述すべき。

資料では、そのような設定を表示するための Activity の記述例や、設定が変更されたときの応答の例が書かれている。

接続の変化の検出

接続が変化したときにそれを検知するには、BroadcastReceiver のサブクラスを作って CONNECTIVITY_ACTION 送出時 (?) に呼び出されるようにしておけばよい。

CONNECTIVITY_ACTION 送出時 (?) に呼び出されるようにするのは、receiver 要素を manifest に記述することでも可能だが、そうするのではなく MainActivity の onCreate で registerReceiver し、onDestroy で unregisterReceiver すること。 そうしないとアプリ終了後でも呼び出されてしまう可能性がある。 manifest に定義する必要がある場合、setComponentEnabledSetting() メソッド を使って有効、無効を切り替えられる。

(ここら辺ちゃんと理解してないので間違ってるかも。)

XML のパース

ネットワーク接続とは直接関係ないが、ネットワーク接続によって取得したデータが XML であることが多いからここに書かれているらしい。

パーサの選択

XmlPullParser が推奨されている。 このインターフェイスを実装したものとして、次の 2 つが Android で提供されている。

2 つあるのは歴史的経緯で、どっちを選んでもいいらしい。 資料の例では前者が使われている。

いろいろ書かれているが、特に難しいところはない。 例とクラスのドキュメントを見れば使い方はわかるはず。

まとめ

  • ネットワーク接続を使用するために manifest に permission を適切に記述すること
  • HTTP クライアントは、Android 2.3 以降なら HttpURLConnection が推奨されている
  • ネットワーク接続を使用する前には、ネットワーク接続の状態を確認すること
  • ネットワーク接続の操作は UI とは別スレッドで行うこと
  • ネットワーク接続で多くのデータをやり取りする場合は、ネットワークの使用法に関する設定を提供すべき
  • ネットワーク接続の状態変化の検知は、BroadcastReceiver のサブクラスを作って CONNECTIVITY_ACTION の通知を受ける (という表現でいいのかな?)
  • XML のパースは XmlPullParser を使うのが推奨されている