NetworkOnMainThreadException

携帯向けのAndroidアプリをAndroid3.0のタブレットで動作させると、NetworkOnMainThreadExceptionが発生している箇所があった。
どうもAndroid3.0からメインスレッド(UIスレッド)から通信を行うとNetworkOnMainThreadExceptionが発生するようになったようだ。

まぁ通信に問題があると一発でApplication Not Respondingになるから、それを予防するために当然の対応ともいえる。
詳しくはこちら
Application Not RespondingにさせないためのはDesigning for Responsivenessを参照。

ウサビッチの9パズルが絶対に解けない件

会社でウサビッチの9パズルが難しいと話題になった。
話題のパズルはこれ。

9枚のピースがあり、一つのピースにはその四辺にキャラの上半身と下半身が描かれている。
そのピースのキャラの絵が繋がるように置いていき、9枚の絵を3×3に収めればクリアだ。

どうしても解けないとの話なので、答えを探索するプログラムをサクッと作って確認してみた。
すると本当に解が無い!!
後1枚で解けるという組み合わせが440通りあるが、解ける組み合わせがない。

会社で出た話では、「同じ絵のピースが2枚あるのが怪しい。間違ったピースが入っており、解けなくなっているのではないか」と言う話だった。
「そんなことあるわけ無いw」と思って解を調べてみたんだけど、確かに解けなかった。

で、Amazonの写真を見てみると、確かに手元の9枚の中に入っていないピースがある!!w
Amazonの写真で言うと、真ん中上に描かれているピースが入っていない。
そして真ん中に描かれているピースが2枚入っている。

ちなみに、入っていないピースをデータに入れ、解答を探すとちゃんと解が見つかった。

なんとこのパズルは製造ミスで絶対に解けないパズルになっていた!!
コレはひどいw

Androidで他のアプリのデータベースを読み込む方法

Androidでは、通常他のアプリのデータベースを読み込むことはできない。
しかし、次の方法をとることでデータベースを開くことができる。

  1. 読み込む先のアプリと、そのDBを読み込むアプリのAndroidManifest.xmlのmanifestタグに同じIDを指定したandroid:sharedUserId属性を追加する。
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="jp.rainbowdevil.test"
          android:versionCode="1"
          android:versionName="1.0"
          android:sharedUserId="foo.bar">
  2. SQLiteDatabase.openDatabaseで他のアプリのDBを開く
    private static String DB_PATH = "/data/data/jp.rainbowdevil.test/databases/";
    private static String DB_NAME = "test.db";
    //-----------
    String myPath = DB_PATH + DB_NAME;
    SQLiteDatabase myDataBase = 
        SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);

Androidでは、アプリごとの読み込み権限をLinuxのユーザを変えることで実現している。
したがって、そのユーザを同じにしてやれば他のアプリのデータも開くことができる。
これを同じにするにはandroid:sharedUserId属性を同じにする必要がある。
同じになっているかどうかは、/deta/detaをls -lしてやればOK。app_99とかアプリ専用ユーザでディレクトリが作成されており、sharedUserIdを同じにしたアプリはユーザが同じになっていることがわかる。

なお、sharedUserId属性には、最低一つのピリオドを含める必要がある。
含めないと次のエラーが出てしまう。
Installation error: INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID
foo.barのように、ピリオドを含めよう。

任意のデータベースを開く方法は、次のページが詳しい。
Blog – Using your own SQLite database in Android applications

sharedUserIdに関しては、次のページが詳しい。
他パッケージ(他のapk)のリソースを読む part2

 

 

 

よくわからない端末のADBドライバをインストールする

Android端末で開発するとき、ADBドライバーが必要だ。

んで、メーカーからADBドライバーが提供されていないor入手できない場合は、Android SDKに含まれるADBドライバを入れる。

入れ方は、ハードウエアのベンダーIDとプロダクトIDを調べ、Google公式のandroid_winusb.infを変更し、インストールする。

ベンダーIDはデバイスマネージャーで調べることができる。認識していないデバイスのプロパティを開き、詳細タブを選択する。そして、ハードウエアIDを見る。VIDとPIDという部分があるので、それをメモしておく。
そして、winusb.infに以下のVID_1234とPID_1234を書き換えたものを追加する。

#謎のデバイス
%SingleAdbInterface% = USB_Install, USB\VID_1234&PID_1234
%CompositeAdbInterface% = USB_Install, USB\VID_1234&PID_1234&MI_01

そしてドライバを入れればOK。
情報を教えてもらった@keiji_ariyamaに感謝。

PipedOutputStreamを使うときはBufferedでラップしよう

一瞬はまったのでメモ。

PipedOutputStreamとPipedInputStreamはスレッド間でデータを受け渡しするのに便利だ。

使い方はこんな感じ。

// パイプを初期化
pipedOutputStream = new PipedOutputStream();
pipedInputStream = new PipedInputStream();
pipedInputStream.connect(pipedOutputStream);

// バッファする(しないとPipedInputStreamのバッファである1024が有効となる)
int BUFFER_SIZE = 512 * 1000;
outputStream = new BufferedOutputStream(pipedOutputStream,BUFFER_SIZE);

//-------------  とあるスレッド  ---------------
// 何かしらのbufferをiバイト分だけ書き込む
outputStream.write(writeBuffer,0,i);

//-------------  別のスレッド  ---------------
// 読み込む
int i = pipedInputStream.read(readBuffer);

BufferedOutputStreamを使用せず、PipedOutputStreamにwriteすると、すぐにPipedInputStreamにデータを渡しちゃう。PipedInputStreamにはデフォルトで1024のバッファしかないので、そのバッファが読み込まれないとwriteでブロックしてしまう。
したがって、PipedOutputStreamはBufferedOutputStreamでラップしよう。

開発時にAndroidをUSBで接続するとFailed to start monitoringと出る

仕事で新しいPCを用意してもらったので、さっそく使おうとAndroid端末をUSBで接続すると、Failed to start monitoringと言われた。

ちょっとググると、「ほかのポートに刺せ!」と言われている人がいた。

新しいPCには、5インチベイにUSB3.0用のポートがあった、最初こちらに接続していたのだけれど、USB2.0のポートに接続すると正しく認識された。

何が悪かったのかよくわからないが、とりあえずこの問題が発生した場合、USBポートを変えるとうまくいくことがあるようだ。

事前条件(Precondition)を使おう

Guiceのソースコードを読んでいて、至る所に事前条件をチェックしている部分があった。
たとえばAbstractModuleのconfigureメソッドは次にようになっている。

  public final synchronized void configure(Binder builder) {
    checkState(this.binder == null, "Re-entry is not allowed.");

    this.binder = checkNotNull(builder, "builder");
    try {
      configure();
    }
    finally {
      this.binder = null;
    }
  }

checkStateでメソッドを実行する前のメンバの状態をチェックし、checkNotNullで引数がnullで無いことをチェックしている。
もしチェックに失敗すると、第2引数をメッセージとして持つIllegalStateExceptionやNullPoがスローされる。

ちょっとの手間をかけるだけで頑健なプログラムを作るのに役に立つし、ソースコードを見る側にも「このメソッドが呼ばれたときにどういう状態になっているべきか」がわかりやすくなる。
次作るプログラムは事前条件をチェックするようにしてみよう。

なお、Guice3.0のPreconditionsクラスには次のチェックがある。実際のソースコードはこちら

  • checkArgument
    引数をチェックする。チェックに失敗するとIllegalArgumentException。
  • checkState
    メソッドを実行した際の、インスタンスの状態をチェックする。チェックに失敗するとIllegalStateException。
  • checkNotNull
    nullではないことをチェックする。checkNotNullメソッドの戻り値を使うことで、nullでないことを保証する。チェックに失敗するとNullPointerException。
  • checkContentsNotNull
    Iterableにnullが含まれていないことをチェックする。ArrayListなどはadd(null)を許容するため。チェックに失敗するとNullPointerException。
  • checkElementIndex
    要素のインデックスをチェックする。負のインデックスになっていないか、サイズを超えていないか。チェックに失敗するとIndexOutOfBoundsException。
  • checkPositionIndex
    ポジション(?)のインデックスをチェックする。checkElementIndexと違い、たとえば要素のサイズが5の時に、5のインデックスはOK。チェックに失敗するとIndexOutOfBoundsException。

Preconditionsクラスをコ(ry 参考にすれば、すぐに事前条件を使ってバグの少ないメンテナンス性の高いコードを書くことができるので、是非とも使ってみよう!

ASCII.technologies6月号に寄稿しました

技術誌ASCII.technologies 6月号「Google API大辞典」の一部パートを担当しました。
有名どころからマイナーなものまでGoogleのAPIを解説しているので、GoogleのAPIに興味がある方には良い特集になっていると思います。

購入するとPDFもダウンロードすることができるので、iPadなどでも読むことができるようになっています。

ぜひ購入して読んでみてくださいっ!

Android Marketでダウンロードができなくなったときの対策

Android DevPhone1(Android 1.6)で、Android Marketからアプリをダウンロードしようとしても、すぐにダウンロードが中断してしまい、アプリがインストールすることができなくなってしまった。

検索すると解決策があり、それを実行すると解決したのでメモ。

情報元はAndroid Marketからのアプリダウンロード

  1. 「Setting」->「Applications」->「Manage applications」->「マーケット」->「Clear cache」を選択してキャッシュをクリアする。
  2. 「Setting」->「Applications」->「Manage applications」->「Google Apps」->「Clear data」を選択してデータをクリアする。
  3. 「Setting」->「Data synchronization」を選択して、アカウント情報を入力する。

これでいけた。

Eclipse(TCP/IP Monitor)でHTTPの通信をモニタリング

とあるHTTP通信を行うプログラムを作成していて、その通信内容をモニタリングしたいときがある。
そういうときは通信内容を表示できるプロキシサーバが欲しい。

そんなときにEclipseの TCP/IP Monitorプラグインの出番である。
TCP/IP Monitorプラグインは、「自分の○番ポートに接続があると、設定した×サーバの△ポートへ接続する」機能を持つ。
テスト対象サーバを設定し、クライアントでEclipseを動作させているマシンへ接続すると、通信の中継をしてその内容を表示してくれるわけだ。
テキストや画像を識別し、画像であれば画像を表示することができる。

・インストール方法

TCP/IP MonitorはEclipse WTP(Web Tools Platform)に含まれているので、WTPがインストールされていない場合はWTPをインストールする。
WTPのインストールはこの辺を参照。

・実行方法

TCP/IP Monitorはビューなので、Window->Show Viewを選択し、TCPで検索したら出てくる。出てこない場合はWTPがインストールされていないので、先にインストールしよう。ちなみにShow Viewで開いたDebugの中にTCP/IP Monitorはある。

・モニタリング方法

モニタリングするには、まず設定が必要だ。
ビューの下△のメニューを開き、Preferencesを選択する。

ダイアログが開くので、Addボタンから設定を追加しよう。テストしたいサーバのアドレスとポートを設定し、Eclipseを動作させているマシンの何番ポートをListenするかを入力する。
そして忘れずStartボタンを押そう。

あとはクライアントでEclipseを実行しているマシンの設定したポートへ接続すると、その内容がビューに表示される。

便利!