sendBroadcast()のコスト

極端な話、普通のメソッド呼び出しとどの程度違うのか、と気になったので試してみた。

package com.komamitsu.bench;

import java.util.concurrent.Executors;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;

public class BenchActivity extends Activity {
  private static final String ACTION_HOGE = "hogehoge";
  private static final int LOOP_COUNT = 10000;

  private volatile int countForIntent;

  private final BroadcastReceiver receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
      // Log.d("#####", "intent onReceive");
      countForIntent++;
    }
  };

  private volatile int countForThread;

  void onRecieveFromThread(String label) {
    countForThread++;
  }

  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    findViewById(R.id.intent).setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View v) {
        Executors.newSingleThreadExecutor().execute(new Runnable() {
          @Override
          public void run() {
            IntentFilter intentFilter = new IntentFilter(ACTION_HOGE);
            registerReceiver(receiver, intentFilter);
            Intent intent = new Intent(ACTION_HOGE);
            intent.putExtra("hoge", "hogehoge");

            long start = System.currentTimeMillis();
            Log.d("#####", "intent start: count=" + countForIntent);
            for (int i = 0; i < LOOP_COUNT; i++) {
              sendBroadcast(intent);
            }

            long end = System.currentTimeMillis();
            Log.d("#####", "intent end: count=" + countForIntent + ", time=" + (end - start));
            unregisterReceiver(receiver);
          }
        });
      }
    });

    findViewById(R.id.methodcall).setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View v) {
        Executors.newSingleThreadExecutor().execute(new Runnable() {
          @Override
          public void run() {
            long start = System.currentTimeMillis();
            Log.d("#####", "methodcall start: count=" + countForThread);
            for (int i = 0; i < LOOP_COUNT; i++) {
              onRecieveFromThread("hogehoge");
            }

            long end = System.currentTimeMillis();
            Log.d("#####", "methodcall end: count=" + countForThread + ", time=" + (end - start));
          }
        });
      }
    });
  }
}

こんな感じで、10000回、メソッド呼び出しをしたり、ブロードキャストインテントを投げたりして時間を計ってみた。

結果、

09-19 01:22:08.264 D/#####   (16201): intent start: count=0
09-19 01:22:22.984 D/#####   (16201): intent end: count=10000, time=14728
09-19 01:22:25.325 D/#####   (16201): methodcall start: count=0
09-19 01:22:25.335 D/#####   (16201): methodcall end: count=10000, time=3

メソッド呼び出しはまぁ置いといて、ブロードキャストインテントを投げると一回辺り1.5msec近くかかる。

vmstat見てたら、freeが減ってanonが増えてた. swap outが必要な領域が増えたぽい。

AndroidでTesseractを使ってみた

http://code.google.com/p/tesseract-android-tools/ を使うとAndroidからでも簡単にOCRれるみたいなので、夏休み中で時間もあることだし試してみようかと。

ビルドの仕方はREADMEに書いてある通りに以下のようにすればOK

cd <project-directory>
wget http://tesseract-ocr.googlecode.com/files/tesseract-3.01.tar.gz
wget http://leptonica.googlecode.com/files/leptonica-1.68.tar.gz
tar -zxvf tesseract-3.01.tar.gz
tar -zxvf leptonica-1.68.tar.gz
rm -f tesseract-3.01.tar.gz
rm -f leptonica-1.68.tar.gz
mv tesseract-3.01 jni/com_googlecode_tesseract_android/src
mv leptonica-1.68 jni/com_googlecode_leptonica_android/src
ndk-build
android update project --path .
ant release

でハマりやすいのが、学習データ的なものがないので http://code.google.com/p/tesseract-ocr/ の /tessdata から適切なディレクトリに配置しておく必要がある点。これを忘れるとSEGVしまくります。


一応、ギャラリー、カメラから読み込むサンプルを作ってgithubにあげておいた。
https://github.com/komamitsu/Android-OCRSample

肝心の精度は、ちょっとこのまま使うにはナイーブだな〜という感じ。

テトリスっぽいやつに自動保存機能つけた

テトリスっぽいゲーム作った - komamitsu.log で書いた Not Found なんですが、アプリを終了させると(というか画面を閉じるだけで)、次はまた最初からになってしまうのが嫌だったので自動的に保存する機能を付けました。で、アプリスタート時に続行可能に。

テトリスっぽいゲーム作った

https://play.google.com/store/apps/details?id=com.komamitsu.ketris

以前、ちょっと時間が空いていた頃、SurfaceViewの練習がてら適当にブロックを書いて動かしてたらテトリスになったので、ちょこちょこと手を加えてそれっぽくしてリリースしました。

途中で幾つか革新的なあいであが浮かんで来たのですが、実装してみてチューニングしてみて結局使い勝手が良くないなぁということで没になり、最終的にごく普通のテトリスになってます。というかそもそもテトリス良く知らないので間違ってるかも...

基本的に、いざというときの自分の暇つぶしに使えるようにしたいなぁと。で、そういう状況は大体がAndroidの電波が届かない地下鉄に乗っているときで、自分的にはそういうとき片手で操作できると嬉しいので、その辺の操作感まわりはちょっと気にしました。

あと、好奇心半分お小遣い目当て半分で、広告を入れてみています。でもゲーム画面が小さくなってちょっと嫌だなぁとか、それだけのためにuse-permission増やす(INTERNETとか)のも嫌だなぁという感じがしているのでどうにかしたいですねぇ。

MediaPlayerでerror (1, -2147483648)が出る件

エラーが発生しても数字をボソっとlogに呟くだけで有用な情報を教えてくれないことに定評があるAndroidのMediaPlayerですが、Player画面を終了しようとすると表題のエラーがこそっと出ていることがあります。

大抵、再生の停止時なので、あまり影響がなく放っておいたのですが、さてMediaPlayerを引き続き使おうか、と思うとErrorのStateに落ちていて結構困ります。

で、結論からいうと、surfaceDestroyed()される際に、MediaPlayerにsetDisplay()されているSurfaceHolderをdetachしてあげれば良さげです。

なので、こんな感じに。

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		Log.d(TAG, "surfaceDestroyed");
		mediaPlayer.setDisplay(null);
	}

support v13を使うプロジェクトのビルド(with maven-android-sdk-deployer)

mavenAndroid projectをビルドする際、support v13を使っている場合にちと困ります。というのも、http://search.maven.org/ にはまだ、support v13のartifactが無いのですね。で、そんな場合、mosabua/maven-android-sdk-deployer · GitHub を使って解決できたので、書いておこうかと。

インストールされているAndroid SDK (extra含む) を、簡単にMaven local repositoryにインストールしてくれるやつです。

  • 使い方
$ git clone git://github.com/mosabua/maven-android-sdk-deployer.git
$ cd maven-android-sdk-deployer
$ mvn install
      :
   (すべてのSDKがMaven installされる)
  • pomの記述
        <dependency>
          <groupId>android.support</groupId>
          <artifactId>compatibility-v13</artifactId>
          <version>r8</version>
        </dependency>

versionはインストールされているSDKのほうにあわせる。


で、だいたいこれが欲しくなるのは、手元のEclipseでは問題ないけど、Jenkinsサーバーだと動かない、というケースで、手元のEclipseで上記のdependencyが存在するとエラーになる。で、ひとまずJenkins用のpom.xmlを生成し、凌ぐのはありかと。

Android Emulator Plugin を試してみた

Android Emulator Plugin を Plugin Managerからインストールしてみて、適当にいじってみた感をつれづれと。

Job -> Configure から "Run an Android emulator during build" 辺りを設定していけばそれっぽく動く(詳細は上のURL参照)のだけど、デバイスが見つからないとか言われて失敗してしまう。

[INFO] Found 1 devices connected with the Android Debug Bridge
   :
cause : No device found for android.device=emulator

いろいろ情報をあさってみたら、[#JENKINS-12821] android.device - Jenkins JIRA が原因っぽいなぁ、と。

で、Proof of concept fix for JENKINS-12821. · 5e52444 · oldelvet/android-emulator-plugin · GitHub こんなpatchもできているんだけども、まぁ Jenkinsが動いているサーバーで、Android Emulatorを立ち上げとけば当面いいかなぁということで、件のpluginは使わずに様子見。