2013年09月20日

[Android]VolleyでGsonとBasic認証


Volleyとは
Google I/O 2013で発表されたネットワーク処理に関するライブラリです。
このライブラリを使えば今までAsyncTaskやAsynkTaskLoaderで実装していた非同期処理を簡素化、その他にもキャッシュ処理やリクエストのスケジューリングなども簡単に実装することができます。



今回はVolleyでのBasic認証、Gsonを組み合わせたJSONのパースをやってみました。

まずはVolleyをGitから入手しましょう。
git clone https://android.googlesource.com/platform/frameworks/volley
続いてGsonはこちらから入手しましょう
https://code.google.com/p/google-gson/

VolleyとGsonをプロジェクトにインポートしたら、次はこちらを参考にGson用にRequestクラスを拡張したGsonRequestというクラスを作ります。
import java.io.UnsupportedEncodingException;
import java.util.Map;

import com.android.volley.AuthFailureError;
import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener;
import com.android.volley.toolbox.HttpHeaderParser;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;

public class GsonRequest<T> extends Request<T> {
	@SuppressWarnings("unused")
	private static final String TAG = "GsonRequest";

	private final Gson gson = new Gson();
	private final Class<T> clazz;
	private final Map<String, String> headers;
	private final Listener<T> listener;

	public GsonRequest(int method, String url, Class<T> clazz,
			Map<String, String> headers, Listener<T> listener,
			ErrorListener errorListener) {
		super(method, url, errorListener);

		this.clazz = clazz;
		this.headers = headers;
		this.listener = listener;
	}

	@Override
	public Map<String, String> getHeaders() throws AuthFailureError {
		return headers != null ? headers : super.getHeaders();
	}

	@Override
	protected void deliverResponse(T response) {
		listener.onResponse(response);
	}

	@Override
	protected Response<T> parseNetworkResponse(NetworkResponse response) {
		try {
			String json = new String(response.data,
					HttpHeaderParser.parseCharset(response.headers));
			return Response.success(gson.fromJson(json, clazz),
					HttpHeaderParser.parseCacheHeaders(response));
		} catch (UnsupportedEncodingException e) {
			return Response.error(new ParseError(e));
		} catch (JsonSyntaxException e) {
			return Response.error(new ParseError(e));
		}
	}
}

続いて実際にBasic認証後にJSONパースしたオブジェクトを取得する部分の実装です。
private RequestQueue requestQueue;

@Override
public void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	requestQueue = Volley.newRequestQueue(getApplicationContext());
	url = "http://api.openweathermap.org/data/2.5/weather?q=Tokyo,jp";
	requestVolley(url);
}

private void requestVolley(String url) {
	// Volleyでリクエスト
	GsonRequest<Result> request = new GsonRequest<Result>(Method.GET, url,
			Result.class, createAuthHeaders(), createSuccessListener(),
			createErrorListener());
	requestQueue.add(request);
	requestQueue.start();
}

private Map<String, String> createAuthHeaders() {
	// HTTPのヘッダに認証情報を追加する
	String userpassword = "username:password";
	final String encoded = new String(Base64.encode(
			userpassword.getBytes(), Base64.DEFAULT));
	Map<String, String> headers = new HashMap<String, String>();
	headers.put("Authorization", "Basic " + encoded);
	return headers;
}

private Response.Listener<Result> createSuccessListener() {
	return new Response.Listener<Result>() {
		@Override
		public void onResponse(Result result) {
			//リクエスト成功時の処理
		}
	};
}

private Response.ErrorListener createErrorListener() {
	return new Response.ErrorListener() {
		@Override
		public void onErrorResponse(VolleyError error) {
			//リクエスト失敗時の処理
		}
	};
}
Volleyに関しては正式なドキュメントがあるわけではないので探り探りでの使用にはなると思うんですが、拡張性がとても高く自由にカスタマイズできる部分がとても魅力的に感じました。
今後はAsynkTaskLoaderの代わりにVolleyを使用するのが標準になってくるんでしょうか…
posted by Seesaa京都スタッフ at 10:00| Comment(0) | Android | このブログの読者になる | 更新情報をチェックする

2013年08月09日

[Android]TabHost+WebViewでハマる

久しぶりの更新になります。Androidを担当しています、森脇です。

Androidの開発をしていると、デザイン上どうしてもiPhoneと同じ下位置でのタブレイアウトを求められることがあります。
ですがこちらにもあるようにAndroidでの下位置でのタブレイアウトは推奨されていません。
Don't use bottom tab bars

Other platforms use the bottom tab bar to switch between the app's views. Per platform convention, Android's tabs for view control are shown in action bars at the top of the screen instead. In addition, Android apps may use a bottom bar to display actions on a split action bar.

You should follow this guideline to create a consistent experience with other apps on the Android platform and to avoid confusion between actions and view switching on Android.
それどころかActionBarを使うとタブ位置を下に配置することがそもそもできなくなっています。

ということで今回はActionBarの代わりにTabHostを使うことにしました。
その際TabHostとWebViewとの組み合わせで大いにハマったのでメモを残しておきます。

1.WebView上で<input>タグに入力しようとするとWebViewからフォーカスが外れる
4.1系で確認されました。正直頭を抱えてしまいましたが、どうやらキーボードを押したタイミングでなぜかTabWidgetがフォーカスを奪ってしまうということが判明。以下の様に対応しました。
		for (int i = 0; i < 4; i++) {
			tabHost.getTabWidget().getChildAt(i).setFocusable(false);
		}
2.WebView上の<select>タグで項目を選択しても反映されない。反映されないまま、以後は選択すらできなくなる
4.1と4.2系で確認されました。こちらは具体的な原因は最後までわからなかったのですが、TabHostをFragmentTabHostに代えることで対応しました。
FragmentTabHostも通常のレイアウトではタブ位置が上に固定されてしまうので、以下の様なレイアウトでタブ位置を下に固定しています。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <FrameLayout
        android:id="@+id/realtabcontent"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <android.support.v4.app.FragmentTabHost
        android:id="@android:id/tabhost"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <FrameLayout
            android:id="@android:id/tabcontent"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_weight="0" />
    </android.support.v4.app.FragmentTabHost>
</LinearLayout>
どちらも特定OS下でTabHostとWebViewを組み合わせた時のみ発生する事案のようでした。
4.X系のWebViewクラスに元々バグが多いだけなのか、それともTabHostを使わずActionBarを使えという無言の圧力なのか…
どちらにしてもつい先日ActionBarが念願のSupport Library入りを果たしたところなので、今後タブレイアウトを組む際は積極的にActionBarを使っていきたいですね。
posted by Seesaa京都スタッフ at 12:48| Comment(1) | Android | このブログの読者になる | 更新情報をチェックする

2013年06月25日

[Android]チームでdebug.keystoreを共有する

Android担当の森脇です。

今回は開発チーム内でdebug.keystoreを共有する手順を書いておきます。

1.共有用のdebug.keystoreを用意。
デフォルトのdebug.keystoreを使用する場合は、ADTのPreferences→Android→Buildを開いてDefault debug keystoreのパスに保存されているdebug.keystoreを使用する。
debug_keystore_00.jpg

2.同画面のCustom debug keystoreに共有用debug.keystoreを設定する。
debug_keystore_01.jpg

以上です。
今回はADT(Eclipse)での手順だったので機会があればAndroidStudioでの手順も書こうと思います。
posted by Seesaa京都スタッフ at 13:01| Comment(0) | Android | このブログの読者になる | 更新情報をチェックする