Android4.0(ICS)のオプションメニューに物申す

AndroidのICSにおいて、オプションメニューは、タイトルではなく、画面の右下に表示したい。
なぜなら、画面の右下に表示したほうが右手だけで操作しやすいからだ。タイトルにオプションメニューがあると、どうしても、両手で操作しないといけなくなる。また、タイトルのないアプリだと、オプションメニューが表示されない。
しかし、ICSでアプリを作るとタイトルにしかオプションメニューを置けない。何故か。
答えは以下のページ。
http://stackoverflow.com/questions/8346064/options-menu-not-showing-in-ics-using-compatibility-library
ポイントは、Androidマニフェストファイルから、

android:targetSdkVersion="14"

をはずすこと。最新バージョンを指定すると、オプションメニューが画面の下側に表示されなくなる。
しかし、何故、ICSではオプションメニューの位置を変えるのだろうか。ICSからハードキーをなくそうとしていることはわかるが、ユーザの操作性を考えて、キーレイアウトをアサインすべきだと思う。
このあたりのちぐはぐさがAndroidのfragmentation問題を引き起こしている。根が深い。

OpenMPIでダイナミックな環境を構築

MPIを使ってスパコンのような並列コンピューティング環境を構築するにあたって、一番、気になった点は、「どうやってそれぞれのマシンで実行プログラムを共有するか?」である。
学校や企業でスパコンのような環境が用意されている人たちはあまり気にしなくてもなんらかの運用方法が提供されているだろうが、一から環境を組み上げる人にとっては気になる点だと思う。
Webで調べてみると、どうやらNFSで実行プログラムを共有するのが一般的なやり方のようだ。全てのコンピュータがNFSでマウントされたファイルを読み、そこから実行プログラムを取り出す。うーん、趣味に合わない。スパコンってダイナミックでヘテロ*1な環境で動くんじゃないの?というのが私の主張。世の中、NFSが主流のようだが、なんだかしっくりこない、というわけでいろいろ調べてみたので、その結果を残しておく。

実行プログラムの共有方法

実行プログラムの共有方法はいくつかある。

  1. 実行プログラムをNFSでマウントして、複数コンピュータで使う
  2. 実行プログラムをそれぞれのマシンに転送(scp,ftp,etc)して使う
  3. 実行プログラムをそれぞれのマシンでコンパイルして使う
  4. 実行プログラムを実行時にそれぞれのマシンに自動転送して使う

一般的なのが1、2。3は面倒だが、ヘテロなマシン(Linux,Mac,Win,etc)で環境を構築できる。そして、私がしたいのは4。

OpenMPI

MPIにはMPICH2、LAM、OpenMPIといった様々な実装がある。私は、Macに初めから搭載されていたOpenMPIを使って見ることにした。
まずは、OpenMPIで簡単なプログラムを作って、動作させる。動作方法は上記2と3。どちらもうまく動くことを確認。
次に4にトライ。OpenMPIのmpirunコマンドのマニュアルを読むと、"--preload-binary"というオプションがある。まさしく、私が求めていたもの。

実行プログラムの自動転送

"--preload-binary"動かしてみる。

% mpirun -host 192.168.1.1 --preload-binary -np 10 ./a.out

以下のエラーメッセージが出て、動かない。

                                                                                                                                                  • -

WARNING: Remote peer ([[26776,0],1]) failed to preload a file.

Exit Status: 256
Local File: /tmp/openmpi-sessions-xxx@localhost.localdomain_0/26776/0/a.out
Remote File: a.out
Command:
scp host2:/home/hoge/a.out /tmp/openmpi-sessions-hoge@localhost.localdomain_0/26776/0/a.out

Will continue attempting to launch the process(es).

                                                                                                                                                  • -
                                                                                                                                                  • -

mpirun was unable to launch the specified application as it could not access
or execute an executable

                                                                                                                                                  • -

scp時にパスワードを手動設定するところに問題があるようなので、以下のページを参考にして、マシン間の双方向でsshのログインを自動化。

http://www.turbolinux.com/support/document/knowledge/152.html

それでも変わらないエラーメッセージ。
しかたなく、ググッてみると、以下のページに出会う。

http://www.open-mpi.org/community/lists/users/2009/12/11479.php

要点をまとめると以下。

  • OpenMPIの1.3とか1.4では"--preload-binary"の動作にバグがある可能性がある
  • sshの自動ログインは双方向でできるようにしておく必要がある

なるほど。そういうことか・・・
って、バグかよ!

と疑いたくなるところをぐっとこらえて、もう一度エラーメッセージを見てみる。

WARNING: Remote peer ([[26776,0],1]) failed to preload a file.
Exit Status: 256
Local File: /tmp/openmpi-sessions-xxx@localhost.localdomain_0/26776/0/a.out
Remote File: a.out
Command:
scp host2:/home/hoge/a.out /tmp/openmpi-sessions-hoge@localhost.localdomain_0/26776/0/a.out

Will continue attempting to launch the process(es).

これは、ファイルのコピー(scp)に失敗していることを意味する。もしかしたら、この時点でsshの双方向ログインがうまくいっていない。いつもは、IP直打ちならうまくいくことを確認していたが、このメッセージ内ではホスト名が使われている。

以下のコマンドを打ち込んでみる。

scp host2:/home/hoge/a.out /tmp/openmpi-sessions-hoge@localhost.localdomain_0/26776/0/a.out

パスワードを入力するよう確認してきた・・・
ホスト名を使った場合、双方向通信ができていない。

ということで、sshで自動ログインできるようホスト名で再設定。もちろん、双方向ができるように設定。

そして、再度、トライ・・・・
やった、できた!

最後に

バグかと疑ったが、自分が間違えていたい。よくあることだ。反省反省。
ちなみに、1.5でもうまく動きました。
私の考えるダイナミックな並列コンピューティング環境*2に一歩近づいた。

*1:調べてみると、同一CPUアーキしかサポートしてない

*2:sshの自動ログイン設定はしないといけないが・・・

new iPadの性能課題に対する疑問

new iPadが発売されて、一番気になったことは、解像度が4倍になったこと。解像度が4倍になるということはUI開発者にとってはとんでもない問題であるはず。つまり、性能が4分の1に劣化することを意味する。この課題意識は解像度の問題に直面してきた携帯電話やAV機器の開発者にとってはあまりにも当たり前のことである。
しかし、あのAppleがいとも簡単に解像度を4倍にしてみせた。4倍にしつつ、性能劣化を防ぐことができたとしたら、new iPadは恐るべき能力を持つこととなる。Appleが解像度を4倍にしつつ、さくさく動くアプリを作る基盤を構築したとしたら、世界中の組み込み機器の技術者はいったい今まで何をしてきたのだろうか、と危惧した。
Appleに脅威を感じていた今日この頃であったが、そんなすごい技術をAppleが持っている訳ではないことを知り安心した。
http://satoshi.blogs.com/life/2012/03/ipad3.html
上の記事によると4倍の解像度に対応するためには、アプリ側でチューニング必要となるのである。ハードウェアアクセラレータのビットマップの転送速度はUIが求める60fpsを実現するにあたっては、それほど十分な性能を持っているわけではない。アプリ側でチューニングを施すことでユーザに気持ちよく使ってもらえるUIを実現することが可能となる。これは組み込み技術者がよくやってきたチューニング方法の一つである。
ハード性能とソフト性能のバランスを保つためのソフトチューニングは永遠の課題であると、再認識した。Appleでさえ、クリアできていない。Androidのフラグメントがよく取り上げられるが、アプリ開発者にとっては、Androidだけにそういった問題がある訳ではなく、iPhone/iPadにも根強く残る問題なのである。

Handlerクラスの正しい使い方(Androidでスレッド間通信)

AndroidのHandlerクラスは別スレッドからUI部品操作を用いる際に、よく使われる。Androidの場合はUIスレッドからでないとUI部品を操作できないという制約がある。どのサイトを見てもUIスレッドへイベントを送るための仕組みとして語られている。

いやいや、それは事実だが、それだけでない。

Handlerクラスはスレッド間通信のための仕組みである。もっと正確に言うと、Handlerインスタンスを生成したスレッドへイベントを送るための仕組みなのである。当たり前だと思う人も多いかもしれないが、多くの人はこのことを理解できていない。

ソースレベルで説明してみる。よく書かれるソースは以下のような感じである。

Handler handler = new Handler(); // (1)
handler.post(new Runnable() {
	@Override
	public void run() {
		// UI部品への操作;
		return;
	}
});

このソースのポイントは(1)である。(1)を実行したスレッドへHandler.post()で指定したRunnableが送られる。つまり、UIスレッドで(1)を実行すれば、UIスレッドへ送られ、別スレッドで(1)を実行すれば、別スレッドへ送られる。

では、その実体をAndroidのソースHandlerのコンストラクタのソースを見て、確認してみる。

    public Handler() {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper(); // ポイント(1)
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue; // ポイント(2)
        mCallback = null;
    }

ポイントは2点。ポイント(1)で呼び出しスレッドの宛先(Looper)を取り出し、ポイント(2)で宛先のポスト(mQueue)へRunnableを送りつけている。このソースを見るとよくわかるのだが、Handlerクラスは決してUIスレッドへRunnableを送りつけるためのものではない。Handlerインスタンスを生成したスレッドへイベントを送りつけるための仕組みである。

Runnableを送りつけたい宛先を明示したい場合は、もう一つのコンストラクタであるHandler(Looper looper)を使う。これを使えば、任意のThreadへRunnableを送りつけることができる。

この仕組みさえ知っていれば、任意のスレッドとの通信が可能となる。例えば、WebViewを使っている場合は、WebViewの本体であるWebViewCoreThreadと通信することが可能となる。

例えば、こんな感じ。

public class HogeTask extends Thread {
	public HogeTask() {
	}
	
	public void execute(Looper toLooper, String data) {

		// ここでスレッドでしたい処理を記述

		new Handler(toLooper).post(new Runnable() {
			public void run() {

				// ここで宛先toLooperでしたい処理を記述

				return;
			}
		});
	}
}

呼び出し側はこんな感じかな。

new HogeTask().execute(toLooper, "hogehage");

toLooperには宛先スレッドのLooperを入れるとよい。Looperの取得の仕方は、対象となるスレッド上でLooper.myLooper()とすればよい。ちなみにUIスレッドのLooperを取得したい場合は、Looper.getMainLooper()を呼べばよい。

これで、HandlerとLooperの神髄を理解することができたと思う。これだけの知識があれば、スレッド間通信はお手の物のはずだ。

WebViewの使い方

ベーシック認証の仕方

WebViewを使いこなしたいんなら、ベーシック認証くらい理解して、実装しておこう。

http://mrd.ne.jp/2011/04/webviewweb.html

ダイジェスト認証ってのもあるから忘れないように。

http://x68000.q-e-d.net/~68user/net/http-auth-2.html

iOS5.1にアップデートできないときは

iOS5.1へのアップデートを試みていたが、うまくアップデートができなかっった。
アップデートサーバへのアクセスが集中しているのだろうか。
と思い、MaciPhoneの両方でアップデートを試みたがうまくいかない。

  • Mac経由でアップデートした場合

「ソフトウェア・アップデートサーバに接続中」でいつまでたっても動かない。

  • iPhoneでアップデートした場合

「アップデートを確認できません ソフトウェア・アップデートの確認中にエラーが起きました。」とエラーメッセージが表示される。

  • 解決策

DNSの設定を「8.8.8.8」「8.8.4.4」へ変更する。そうするとあっさりとアップデートが開始された。
「8.8.8.8」「8.8.4.4」はGoogleが提供するDNS。その他、使えるDNSとしては、NTT America Technical Operationsの129.250.35.250がある。いろいろ、DNSの設定を変えて、試してみるのがよさそう。

  • 考察

DNSの設定の問題だろう。正しいDNSへアクセスしようとしていると考えるとやはりアクセスが集中していると考えられる。大量のiOSアップデートに備えて、アップデートサーバを強化したが、DNSのレコードが従来のものかわ変わっておらず、アクセスが集中してしまっている、とかなんとか。

MacでAndroidの開発環境構築

Androidの環境構築はなかなかうまくいかない。という訳で、備忘がてら、手こずった事象の解決策を書いておく。

ソース取得はAndroid理解への早道なので、上達したい人は取得しておくこと。特に、初心者ほど取得しておき、興味のある機能のソースを読むことをお勧めする。Androidのソースがどのような思想で作られているのか、Androidのアプリをどのような思想で作っておくべきかがわかるるようになる(かもしれない)。