Beginning Android 2D Video Games

Android上で2Dゲーム作成を始めてみたい方へ

AndroidHttpdClientを利用したWebアクセス(POST/GET)

Androidアプリに何らかの配信機能やソーシャル機能,データの共有・リモート保存の機能を持たせる場合,最も合理的な1つの解決方法はWeb上に配置したアプリケーションを介すること,となります*1

Web(HTTPやHTTPS)を介したアプリケーションは,CGIによるPerlPHP, Ruby(独自のフレームワークもあり),JSPServletといったものがあります.
これらは,HTTPを解釈するWebサーバ(ApacheTomcatなど)上で動作するプログラムとデータを保管するファイルやデータベース(MySQLPostgreSQLなど)から成ります.

通常,Webアプリの利用は,HTMLを介してブラウザから行うことが圧倒的に多いのですが,HTTPを発行できるクライアントであれば,ブラウザに限ることはありません.
AndroidでもHTTPクライアントとして,AndroidHttpClientというクラスが提供されています.
以下にWebを介したデータのやりとりの模式図を示します.

f:id:hidenaoA:20131013003025p:plain

以下の例では,単純にHTMLで以下のように作成した

をresponse.phpという送信された文字列を表示するWebアプリに送信するものとして説明します.

<form action="http://localhost/work/response.php" method="POST">
  <input type ="text" name="send_str" size="30">
  <input type="submit" value="Send">
</form>
<form action="http://localhost/work/response.php" method="GET">
  <input type="submit" value="Reload">
</form>

Manifest.xmlの設定

AndroidHttpClientを使ってWebアクセスを行うためには,ネットワーク通信が必須のため,以下のpermissionを受ける必要があります.

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

AndroidHttpClientによるPOSTとGETによる要求発行

まず,画面レイアウトですが,EditTextとButtonを適当に配置した下にTextViewが配置してあります.
TextViewについては,WebViewとすることで,WebアプリケーションからのHTMLレスポンスを整形して表示することも可能となります.

それぞれのボタンについては,クリック時のイベント処理として,要求の発行を実行してあります.
ここでDialogを表示していますが,必要であれば,キャンセル処理を実装して下さい.
注意しておきたいことは,AsyncTaskによる実行は別スレッドのため,UIスレッドへのアクセスは,このボタン押下イベント処理内で行わないといけない,ということです.

AsyncTaskの利用

Webアクセスのように処理の時間が予め予想できない処理については,AndroidアプリではAsyncTaskを用いることが勧められています.
AsyncTaskは,doInBackgroundメソッドに実装された内容とこの実行後に呼ばれるonPostExecuteメソッドの内容がexecuteメソッドを呼ぶことで実行されます.
なお,doInBackgroundの返値(String)はonPostExecuteの引数として受け取れます.

詳しいAsyncTaskの説明については,文献や以下の解説を参照してください.
非同期処理の基礎 - Android 開発入門

実装

AndroidHttpClientを利用したWebアプリケーションへのアクセス実行コードを以下に示します.
POSTによって送信するときの変数名と値のペアは,ここではBasicNameValuePairといNameValuePairのサブクラスを利用しています.
このNameValuePairを複数送る場合は,Listにして,UrlEncodedEntityに変換することで複数の変数と値のペアを送付することが可能となります.

GETの場合は,HttpGetのオブジェクトを生成(new)する際に渡すURL文字列によってデータ送信することも可能です.
その場合,URLエンコードによって文字列を変換する必要があります.

なお,これらのデータ送信を受けとるWebアプリケーションは,以下の例では冒頭にあるresponse.phpとしています.

package jp.ac.bunkyo.a2dgames;

/**
 * HTTP POSTを利用してレスポンス(ボディー文字列)を表示するActivity
 * @author Hidenao Abe (hidenao@shonan.bunkyo.ac.jp)
 *
 * POSTを受けとるPHPコード(このサンプルではhttp://localhost/work/response.phpに設置)は以下の通り
 * <?php
 *   //send_strは「送信」ボタンを押してPOST送信されてきた文字列
 *   $posted = mb_convert_encoding($_REQUEST["send_str"],"UTF-8","auto");
 *
 *   //必要であれば,ここで保存処理などを行う
 *
 *   //レスポンス本体となる応答出力部分(ここから)
 *   echo "送信された文字列:$posted\n";
 *   //レスポンス本体となる応答出力部分(ここまで)
 * ?>
 *
 */

/* Copyright 2013 Hidenao Abe (hidenao@shonan.bunkyo.ac.jp)

本ソースコードは,Apache License Version 2.0(「本ライセンス」)に基づいてライセンスされます。あなたがこのファイルを使用するためには、本ライセンスに従わなければなりません。本ライセンスのコピーは下記の場所から入手できます。
http://www.apache.org/licenses/LICENSE-2.0
適用される法律または書面での同意によって命じられない限り、本ライセンスに基づいて頒布されるソフトウェアは、明示黙示を問わず、いかなる保証も条件もなしに「現状のまま」頒布されます。本ライセンスでの権利と制限を規定した文言については、本ライセンスを参照してください。
(この日本語訳は,http://sourceforge.jp/projects/opensource/wiki/licenses%2FApache_License_2.0によるものです)
*/

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.app.ProgressDialog;
import android.text.SpannableStringBuilder;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.message.BasicNameValuePair;
import android.net.http.AndroidHttpClient;

public class HTTPClientTest extends Activity {

	TextView text_view;
	EditText text_input;

	ProgressDialog dialog;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_httpclient_test);
		text_view = (TextView)findViewById(R.id.textView1);
		text_input = (EditText)findViewById(R.id.editText1);

		Button btnUpload = (Button) findViewById(R.id.button1);
		btnUpload.setOnClickListener(new View.OnClickListener() {
			public void onClick(View view) {
				//ダイアログを表示
				dialog = new ProgressDialog(HTTPClientTest.this);
				dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
				dialog.setMessage("送信中");
				dialog.show();
				//アップロード(POST)実行
				httpPost();
				dialog.dismiss();
			}
		});

		Button btnReload = (Button) findViewById(R.id.button2);
		btnReload.setOnClickListener(new View.OnClickListener() {
			public void onClick(View view) {
				//ダイアログを表示
				dialog = new ProgressDialog(HTTPClientTest.this);
				dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
				dialog.setMessage("更新中");
				dialog.show();
				//GET実行
				httpGet();
				dialog.dismiss();
			}
		});

	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.httpclient_test, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {//Menuボタンを押して出たメニューが選ばれたときの動作を記述
		super.onOptionsItemSelected(item);
		switch(item.getItemId()){ //それぞれのitemの表示はres/menu/*.xmlで行う
		case R.id.menu_finishing :
			//強制的にActivityを終了する
			finish();
			return true;
		}
		return false;
	}

	private void httpPost(){ //HTTP POSTを使って送信を行う(Bottonのアクションリスナから呼ばれる)
		new AsyncTask<Void, Void, String>() {

			@Override
			protected String doInBackground(Void... params) {
				// HTTPリクエストの構築
				HttpPost request = new HttpPost("http://localhost/work/response.php");
				List<NameValuePair> sendParams = new ArrayList<NameValuePair>();
				SpannableStringBuilder sb = (SpannableStringBuilder)text_input.getText();
				sendParams.add(new BasicNameValuePair("send_str", sb.toString()));
				try {
					request.setEntity(new UrlEncodedFormEntity(sendParams, "UTF-8"));
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}

				// HTTPリクエスト発行
				AndroidHttpClient httpClient = AndroidHttpClient.newInstance("Android HTTP Client Test");
				HttpResponse response = null;
				String response_str = "NG";
				try {
					response = httpClient.execute(request);
					// HttpResponseのEntityデータをStringへ変換
					BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "UTF-8"));
					StringBuilder builder = new StringBuilder();
					String line = null;
					while ((line = reader.readLine()) != null) {
						builder.append(line + "\n");
					}
					response_str = builder.toString();
				} catch (IOException e) {
					e.printStackTrace();
					response_str = e.toString();
				}

				if(httpClient != null){
					httpClient.close();
				}

				return response_str; //返値はonPostExecuteに渡される
 			}


			@Override
			protected void onPostExecute(String result) { //引数はdoInBackgroundの結果
				// 画面に文字列を表示
				text_view.setText(result);
			}
    	 }.execute();

    }

    private void httpGet(){ //HTTP GETを使ってリクエストする
   	 new AsyncTask<Void, Void, String>() {

			@Override
			protected String doInBackground(Void... params) {
				// HTTPリクエストの構築
				HttpGet request = new HttpGet("http://localhost/work/response.php");

				// HTTPリクエスト発行
				AndroidHttpClient httpClient = AndroidHttpClient.newInstance("Android HTTP Client Test");
				HttpResponse response = null;
				String response_str = "GET NG";
				try {
					response = httpClient.execute(request);
					// HttpResponseのEntityデータをStringへ変換
					BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "UTF-8"));
					StringBuilder builder = new StringBuilder();
					String line = null;
					while ((line = reader.readLine()) != null) {
						builder.append(line + "\n");
					}
					response_str = builder.toString();
				} catch (IOException e) {
					e.printStackTrace();
					response_str = e.toString();
				}

				if(httpClient != null){
					httpClient.close();
				}

				return response_str; //返値はonPostExecuteに渡される
			}


			@Override
			protected void onPostExecute(String result) { //引数はdoInBackgroundの結果
				// 画面に文字列を表示
				text_view.setText(result);
			}
 	 }.execute();

    }

}

以上の例ではテキストを送信しましたが,通常のタグと同様にPOSTを用いてファイルを送信することも可能です.
以下のサイトにファイルを送信する方法が掲載されています(サーバ側はファイル保存が可能であれば,Servletである必要はありません.).

Android HttpClientでサーバーにファイルをアップロードする

*1:独自サーバプログラムをネットワーク上のサーバに立ち上げてもいいのですが,セキュリティ対策やメンテナンス,汎用性の面で不利になことが多いでしょう.