2013年4月19日金曜日

[Android]Fragmentはバックグラウンドスレッドから操作できる

AndroidはUIを操作をメインスレッドのみで行うシングルスレッドモデルで設計されています。 バックグラウンドスレッドから、UIの操作を行う時は、Handler経由で行うのが定石とされています。

 で、今日、コードレビューしていると、バックグラウンドスレッドの中で、DialogFragmentを表示する処理を書いているのを見つけました。 ってかこれだめだよね。と思って動作させてみると、普通にダイアログが出てくる。 なんで例外でないんだろう?と思って調べてみると、まわりまわってHandler経由でUIの操作をしているのがわかりました。

流れはこんな感じ。
  1. FragmentManager#beginTransaction→(FragmentManagerImpl#beginTransaction)
  2. (Fragmentをいろいろ操作)
  3. FragmentTransaction#commit→(android.app.BackStackRecord#commit)
  4. (FragmentManagerImpl#enqueueAction)→(Activity#mHandlerに処理委譲)
つまり、バックグラウンドスレッドからFragmentを切り替えたとしても、Handlerを経由してメインスレッドで実行される。ということです。

これの何が嬉しいかというと、処理全体をバックグラウンドスレッドにできるという利点があります。
たとえば、定期的にデータを取得して画面を更新するみたいな処理は、デーモンスレッドを1つ起こしておいて動かしっぱなし。というのが可能です。そしてFragmentは画面の一部を更新するのに向いています(たぶん更新はreplaseしかダメかなぁ)。

 それとは別の観点では、Androidは通信処理をバックグラウンドスレッドで実行する必要があります。そのため、
  • 通信はバックグラウンド
  • UI更新はメインスレッド
のようなめんどくさいことをやるため、AsyncTaskやLoaderを使います。
AsyncTaskではコールバックでメインスレッドとバックグラウンドスレッドを呼び分けてくれますが、処理が分断されるため、一連の流れがわかりづらくなるというデメリットがあります。
使ったことがある人はわかると思いますが、実装上のコードのネストがどうしても深くなってしまいます。

しかし、すべてをバックグラウンドで処理できるとなると、たとえばボタン押下以降の処理全体をバックグラウンドスレッドで実行することが可能になります。
私はHandlerTheadを結構使います。このクラスは、以下のような特徴があります。
  • バックグラウンドスレッドである
  • Queueで処理するため、一連の順序性が保たれる(逐次処理ができる)
  • 遅延実行できる
  • Queueをキャンセルすることができる
しかし、ここからUIを操作するのがめんどくさい(Handler経由になる=HandlerのくせにHandler持ってないとダメ)ので完全にバックグラウンドの逐次処理だけに限って使っていましたが、これがありなら、

 通信→データ加工→UI更新

みたいな一連の流れをカプセル化し処理することが可能になります。

しかし、実装上こうなっているだけで、これをやってOKとはどこにも書かれていません。将来的にこの実装のまま。というわけではないので、注意は必要ですね。
OSのバージョンを固定してもいい場合(一品もの)などは、この方式もありかもしれません。。。

2013年1月5日土曜日

[Android]SQLiteで実行計画を取得する

SQLServerやOracleでは、実行するクエリーに対してどれくらいのコストがかかるかを評価することができます。で、Androidで利用されるSQLiteでは?とおもって調査してみました。 実は簡単で、実行するクエリーの前に、「explain」ってつけるだけ。
コードはこんなかんじ。
explain select * from TEST_TABLE
で、実行結果はというと、こんな感じ。
addr    opcode    p1    p2    p3    p4    p5    comment
0    Trace    0    0    0        00    null
1    Goto    0    14    0        00    null
2    OpenRead    0    4    0    4    00    null
3    Rewind    0    12    0        00    null
4    Rowid    0    1    0        00    null
5    Column    0    1    2        00    null
6    Column    0    2    3        00    null
7    RealAffinity    3    0    0        00    null
8    Column    0    3    4        00    null
9    RealAffinity    4    0    0        00    null
10    ResultRow    1    4    0        00    null
11    Next    0    4    0        01    null
12    Close    0    0    0        00    null
13    Halt    0    0    0        00    null
14    Transaction    0    0    0        00    null
15    VerifyCookie    0    2    0        00    null
16    TableLock    0    4    0    TEST_TABLE    00    null
17    Goto    0    2    0        00    null
・・・みかたがさっぱりわからない。今度調査してみよう。

2013年1月3日木曜日

[Android]スキーム起動でクエリーを付加する

ブラウザのハイパーリンクで起動するのはいいとして、それに条件を付けたいと考えて、クエリーつかえねーかなぁと思ってやってみました。・・・あっさりできました。以下のようなURLで起動した場合、
xxx://com.foo.bar/appnam?url=http%3A%2F%2Fwww.google.co.jp%2Fig%3Fhl%3Dja

コードはこんなかんじ。意外と簡単。

Uri uri = getIntent().getData();
if (uri != null){
    String url = uri.getQueryParameter("url") + "";
    android.util.Log.d("test""url=" + url);
}

2012年12月20日木曜日

[他]入力フォームからHTTPSにしなければいけない理由

Webサイトで、会員登録など個人情報を扱う場合、入力フォームからHTTPSにしなければいけない理由があるのは知っているんですが、なんでって言われるとうまく説明できなかったりします。ということで備忘録代わりにメモ。
HTTPSを使う際に入力フォームのページからそうしないといけない理由はなんですか
  • データの盗聴を防ぐ
  • データの改ざんを防ぐ
  • アクセスしているドメイン名が正しいものであることを保証する

2012年11月20日火曜日

[Android]ダイアログ風なアクティビティ

Alertで表示するとちょっと都合が悪い。そんな場合が時々あります。
そんな時、Activityをダイアログ風に見せる方法があります。えぇまぁ AndroidMAnifest.xmlにテーマ(スタイル)を指定するだけ。

<activity android:name="com.hoge.DialogLikeActivity"
 android:label="DialogLike"
 android:theme="@android:style/Theme.Dialog" />

2012年10月3日水曜日

[Android]レイアウトxml定義でクリックのメソッドが指定できる

ぐは。知りませんでした。レイアウトxmlでクリックのメソッドが指定できるってことを!

<Button
    android:id="@+id/button1"
    android:text="button1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="onButton1Click" />

public void onButton1Click(View v) {
    //処理
}

2012年9月3日月曜日

[android]DefaultHttpClientで取得した結果を簡単にStringやbyte[]に変換する

DefaultHttpClientでHTTP通信し、その結果を受け取るパターンというのは結構あります。 そのアウトプット(xmlやjson)をpullParser等に食わせるときはいいんですが、これを文字列やbyte[]に変換しようとすると、定型のめんどくさい処理を実装する必要があります。 InputStreamをバッファに読み込み、StringWriterやByteArrayOutputStreamに出力する的な感じ。
Androidでは、これを簡単にやってくれるメソッドがあることを最近知りました。 スタティックメソッドなんですが、以下の通りです。
  • EntityUtils.toString
  • EntityUtils.toByteArray

引数にHttpEntity を渡すだけでそれらしく処理してくれます。チョー便利。コードで書くとこんな感じ。
 String data = EntityUtils.toString(response.getEntity());