2010年05月05日

機種リスト

Android 機の主要スペックリストを作ってみた。
内容については保障しないので、実際は自分で調べてください。
解像度は、広い方を先に書くように統一しています。

名前 メーカー キャリア / MID CPU RAM ROM 内蔵ストレージ OS 解像度 外部ストレージ(最大容量)
HT-03A HTC docomo Qualcomm MSM7201A 528MHz 192MB 512MB 270MB Android 1.5 480x320 microSDHC 4GB
Xperia Sony docomo Qualcomm QSD8250 1GHz 384MB 1GB 440MB Android 1.6 854x480 microSDHC 16GB
IS01 Sharp au Qualcomm Snapdragon1GHz ? ? 4GB(ユーザー領域約3GB) Android 1.6 960x480 microSDHC 16GB
Desire X06HT HTC SoftBank Qualcomm QSD8250 1GHz 576MB 512MB 約80MB(共有) ? Android 2.1 800x480 microSDHC 32GB
T-Mobile G1 HTC T-Mobile Qualcomm MSM7201A 528 MHz 192MB 256MB 1GB Android 1.5 480x320 microSD
HTC EVO 4G HTC Sprint Qualcomm Snapdragon 1GHz 512MB - 1GB Android 2.1 800x480 microSD
Google Nexus One
HTC どこでも Qualcomm Snapdragon 1GHz 512MB - 512MB Android 2.1 800x480 microSDHC
Motorola Droid Motorola Verizon Wireless TI OMAP 3430 550MHz 256MB - 512MB Android 2.0 854x480 microSD
HTC Incredible verizon HTC Verizon Wireless Qualcomm Snapdragon 1GHz 512MB 512MB 8GB Android 2.1 800x480 microSDHC 16GB
WebStation Camangi MID Marvell PXA303 624MHz 128MB - 256MB Android 1.5 800x480 microSDHC 16GB
Archos 5 Internet Tablet Archos MID Cortex-A8 800MHz
+ DSP 430MHz
256MB - SSD 8 ~ 32GB
HDD 160 ~ 500B
Android 1.6 800x480 microSDHC (SSDモデル)
SmartQ5 SmartDevices MID Samsung S3C6410 667MHz 128MB - 1GB ? 800x480 microSDHC
Zii EGG クリエイティブ(ZiiLABS) MID デュアルコアARM
+ プログラマブル Processing Elements (PE) 24基
256MB - 32GB ? 480x320 microSDHC 32GB
Liquid Acer ? Qualcomm Snapdragon QSD8250 768MHz ? ? ? Android 1.6 800x480 microSDHC 32GB

投稿者 Takenori : 16:19 | トラックバック

2011年01月25日

Android アプリストア まとめ

Android Market
Google オフィシャル。
PC 用サイトは英語のみで、検索等は出来ない。
PC からのアクセス性は極めて悪く、なぜこのような作りになっているのか理解に苦しむ。
検索等は AndroLibpandroid 出来るので、事実上 PC ではこちらを参照する必要がある。
Android Market アプリも、基本的に検索して探す作りなので、ある種類のアプリを探そうと思ったら、まず検索サイトで検索してレビューなどを参考にする必要がある。
ランキングの表示と PC からの表示を改善してくれれば、かなりマシになると思うけど、なぜ対応されていないのか謎。
開発者向けのページは良くできているから余計になんとか利用者側も改善して欲しいと思う。

※ 2011/2/3 検索できるようになった。またレビュー等も見られるのでアクセス性はだいぶ改善された。

Andronavi
PC サイトの見やすさやランキング、レビュー等充実していて、現時点では日本語では一番使いやすいと思われる。
ただ、Andronavi アプリはなぜかよくつっかかるような動作をする辺り改善して欲しい。
Andronavi 登録アプリ以外に Android Market 登録アプリのレビュー等もある。
開発者登録は、xls ファイルに必要事項を記入して送付等少し煩雑。
パッケージ名は Android Market に登録のものとは別名にする必要があるので、両方に登録する場合はパッケージ名を置換してビルドするスクリプトなどを組んでおくなど一手間必要。
アクティベーション機能は現時点では法人のみ利用可能となっている。
月額課金やアイテム課金は2011年中に対応予定となっている。

AndroApp ( Vector )
フリーソフト等のサイトとして有名な Vector のサイト。
最近かなり充実していっている。
ドコモマーケットや au one market、Android Market への登録代行も行われている。
Android Market への登録代行は、仕組み上開発者名が Vector のものになるので、Android Market への登録は自分で行った方が良いと思われる。
ドコモマーケットや au one market は、開発者名で登録される。
有料販売やアクティベーション機能などの提供はまだ始まっていない。
少し動きが遅いように感じる。

Camangi Market
Android Market がインストールされていないタブレットデバイスで利用可能だったり、デフォルトでこのマーケットのアプリがインストールされている。
主にタブレットデバイスを対象としたマーケット。
最近はアプリの数もだいぶ充実してきている。

appli.jp
アプリ登録数など少なく利用も少ない様子。
ただ、一応更新等されていっている様子。
使い勝手等はそれほど悪くないように思うんだけど、知名度の問題か?
他と違う点は、アダルトが禁止されていないように見えるところ。
規約上にポルノがどうこうや18歳未満にふさわしくないどうこうといった記述が見られない。
ただ、本当に可能かどうかは問い合わせてみないとわからない。

Amazon
日本でいつから開始されるのかは現時点ではわからないが、Android アプリストアで標準となるのではないかと期待している。
PC からの購入や値引き等他のマーケットにない特色もある。
ニュース「Amazonの革新的なAndroidアプリケーションストア 今日はデベロッパのための’店頭’が開店」

ドコモマーケット
ドコモのスマートフォンには、アプリというか、HP へのショートカットが入っている。
現状、Android Market からダウンロードする形のよう。
課金は、SPモードで行う。
まだ SPモード契約していないので、具体的にどうなのか未調査。

AppBrain
あまり詳しく調べてない。

アプリ☆ゲット
あまり詳しく調べてない。

au one Market
あまり詳しく調べてない。

投稿者 Takenori : 18:57 | トラックバック

袋文字の描画

// 袋文字描画
// @param canvas 描画対象キャンバス
// @param text 描画文字列
// @param start 描画文字列中の描画開始文字位置
// @param end 描画文字列中の描画終了文字位置
// @param x 文字描画位置X座標値
// @param y 文字描画位置Y座標値
// @param color 描画文字色
// @param bgcolor 縁取り色
// @param width 縁取り太さ
// @param size フォントサイズ
private void drawOutlineText( Canvas canvas, String text, int start, int end, int x, int y, int color, int bgcolor, int width, int size ) {
    // これらは毎回生成せずに使い回した方がよい
    Paint paint = new Paint();
    Path textPath = new Path();

    paint.setAntiAlias( true );
    paint.setTextSize( size );
    paint.setTextAlign( Paint.Align.LEFT );

    // まずは後ろの影を書く
    // テキストのパスを取得する
    paint.getTextPath( text, start, end, x, y, textPath );
    paint.setColor( bgcolor );
    // ストロークで文字の縁を width 幅で描画
    paint.setStyle( Paint.Style.STROKE );
    paint.setStrokeWidth( width );
    canvas.drawPath( textPath, paint );

    // 文字を書く
    paint.setColor( color );
    paint.setStyle( Paint.Style.FILL );
    canvas.drawText( text, start, end, x, y, paint );
}


他の方法があるかもしれないが、とりあえず上記の方法で描画出来る。
ただ、少し重いのでゲーム等のリアルタイム性のあるアプリでは文字のキャッシングなどの必要がある。
TextView では影付き文字が描画出来るが、袋文字はない。
影の指定次第で近いものは出来るが、縁がブラーをかけたようなものになるので、視認性で劣る。
上記関数はあくまでもサンプルコード。

投稿者 Takenori : 19:24 | トラックバック

2011年01月26日

抜き色が倒せない

AvoidXfermode は、opColor を除いた場所に描画するか、opColor 上だけに描画します。みたいな説明になっている。
この説明を読むと一見カラーキー使えるのか?と思うんだけど、実は判定に使われるのは Destination color のみなので、抜き色指定に使えない。

関数はこんな感じ。
AvoidXfermode(int opColor, int tolerance, AvoidXfermode.Mode mode)

tolerance の意図するところがちょっとよくわからない。
tolerance は 0~255 の範囲で指定する(それ以外では例外発生)。

AvoidXfermode.Mode.AVOID は、Destination color が opColor 以外の時、描画する。
AvoidXfermode.Mode.TARGET は、Destination color が opColor の時描画する。

効果がわかりやすいように、以下にソースコードとその実行結果を

// 適当にしましまに塗る
mPaint.setColor(Color.GREEN);
canvas.drawRect(new Rect(0,  0,mCanvasWidth, 50), mPaint);
canvas.drawRect(new Rect(0,100,mCanvasWidth,150), mPaint);
canvas.drawRect(new Rect(0,200,mCanvasWidth,250), mPaint);
canvas.drawRect(new Rect(0,300,mCanvasWidth,350), mPaint);
canvas.drawRect(new Rect(0,400,mCanvasWidth,450), mPaint);

// それぞれのモードで矩形を描く
mPaint.setColor(Color.RED);
// 左にAVOIDで
mPaint.setXfermode( new AvoidXfermode( Color.GREEN, 255, AvoidXfermode.Mode.AVOID ) );
canvas.drawRect(new Rect(mCanvasWidth/8,0,mCanvasWidth/8+mCanvasWidth/4,mCanvasHeight), mPaint);

// 右にTARGETで
mPaint.setXfermode( new AvoidXfermode( Color.GREEN, 255, AvoidXfermode.Mode.TARGET) );
canvas.drawRect(new Rect(mCanvasWidth/2+mCanvasWidth/8,0,mCanvasWidth/2+mCanvasWidth/8+mCanvasWidth/4,mCanvasHeight), mPaint);
mPaint.setXfermode( null );

で、このソースコードで描かれる画像がこれ(半分のサイズにしている)。
avoidxfermodeimage.png
始めに塗った緑の上かそれ以外に描画されているのがわかると思う。

と言うことで、AvoidXfermode が抜き色には(1パスでは)使えないことがわかった。
ワーク用の Bitmap を準備して、そこにまず画像を描画後、次に AvoidXfermode を使用して抜き色に対して透明色を描画し、そのワーク画像を描画すれば期待の結果が得られると思うが、2回コピーすることになるのと、ワーク用のメモリが必要になるのが気になる。

PorterDuffColorFilter も試したんだけど、これはどうもブレンディングモード指定のようなもののようで、これも期待している用途には使えない。
ColorMatrixColorFilter は、近い結果を得られる。

ColorMatrixColorFilter は、ColorMatrix を色に掛け合わせてその結果を描画する。
マトリックスは 4x5。
マトリックスと演算式は以下のような感じ。
| a, b, c, d, e |
| f, g, h, i, j |
| k, l, m, n, o |
| p, q, r, s, t |
R' = a*R + b*G + c*B + d*A + e;
G' = f*R + g*G + h*B + i*A + j;
B' = k*R + l*G + m*B + n*A + o;
A' = p*R + q*G + r*B + s*A + t;
これを見ればどのような結果になるかすぐにわかると思う。
緑を透過色として扱いたければ、以下のようにすればそれなりに動く。
| 1, 0, 0, 0, 0 |
| 0, 1, 0, 0, 0 |
| 0, 0, 1, 0, 0 |
| 1, -1, 1, 0, 255 |

サンプルソースは以下のような感じ。

float matrix[] = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
 1.0f, -1.0f, 1.0f, 0.0f, 255.0f};
mPaint.setColorFilter( new ColorMatrixColorFilter( matrix ) );
canvas.drawBitmap( bitmap, x, y, mPaint );
mPaint.setColorFilter(null);

演算式を見ればすぐにわかるが、緑が入っていると透過されてしまうケースがあるので、この方法は不完全。
カラーマトリックスをしばらく考えたが、完全なものは無理だと思う。

結論、素直にアルファチャネルを使いましょう。
RGB565 などでとにかくメモリを節約したいのなら、AvoidXfermode で何とかならないこともない。
もっとうまくやる方法がありそうな気がするけど、調べた限りではわからなかった。
NDK 使って自前で実装とかなら出来るだろうけど。

投稿者 Takenori : 23:40 | トラックバック

2011年01月27日

止まらないウィジェットを作る

Android は、メモリが少なくなってくると動作しているアプリを停止させてメモリを空ける。
これはサービスも同じで、ウィジェットを更新するために動かしているサービスも止められてしまうことがある(いっぱい立ち上げると止められる)。
また、Android はいろいろと立ち上がっていると動作が遅くなるし電池食うしであんまりいいことないので、タスクキル系のアプリが一般的になっていて、そのようなアプリでもサービスが止められてしまう。
システムがサービスを停止させた場合、メモリに余裕が出来るとシステムが再度起動してくれるが、タスクキル系のアプリで止められた時はそうではないようだ。
Service.setForeground(true) を使うとシステムにキルされづらくなるというのは、2.1 で意味のないメソッドとなったよう。
AlarmManager で定期的に処理するというのもユーザーに停止させられた場合は無意味になるし、あんまり無駄な処理をすると電池を食う原因となる。

完全に停止させられないようにする方法は見付からなかった(停止不可に出来ると、そのようなアプリがいっぱい起動されたらどうしようもなくなるので、停止できないようにするのは無理ではないかと思われる)。
そこで、動作させ続ける必要があるウィジェットを探して入れて、タスクキル系のアプリで殺しても少ししてみたらまた起動しているものがあることに気付いた。
何かしたら時々システムから起こしてもらえるようになる方法がある?
この方法である程度回避することが出来そうだ。

探すと android.intent.action.SCREEN_ON が該当しそうだと思ったが、これは BroadcastReceiver を登録しないと受けられない。
プロセスが終了していると、BroadcastReceiver は無効になるので意味がなく、起動はしてもらえない。
AndroidManifest.xml に書いて受けられるインテントが必要。
この用途で探すと android.intent.action.USER_PRESENT が見付かった。
これはデバイスが起動した時 ( ユーザーがロックを解除した時 ) にシステムから送られる。
プロセスが停止していても起こしてもらえる。
スマートフォンでは、スクリーンの ON/OFF はよくする動作なので、スクリーン ON した時に起動させてもらえれば、実用上あんまり停止しているようには見えないはず。

AppWidgetProvider で android.intent.action.USER_PRESENT を受けるように AndroidManifest.xml に書いて、AppWidgetProvider::onReceive でインテントが android.intent.action.USER_PRESENT ならサービスを起動するようにすれば OK。
その時、AppWidgetID は不明なので、AppWidgetID は ContentProvider 等で保存するようにしておく。
SharedPreferences ではなく、ContentProvider なのはウィジェットは複数配置される可能性があるから、ContentProvider の方が都合がよい。
ウィジェットの更新に AppWidgetID が必要な時と必要でない時があるので、単純にウィジェットが今ホームに置かれているかどうか判定するだけなら、単に SharedPreferences で数をカウントすればいいので、ContentProvider は要らない。

また、USER_PRESENT で起こされた時以外 ( システムから起こされた時 ) のためにも何かしら保存しておく必要がある。
一度プロセスが停止させられると変数等はクリアされてしまうのと、システムから起動された時は Service::onStart は呼ばれず、Service::onCreate までしか呼ばれないので、Service::onCreate が呼ばれた時に既にウィジェットが今ホームに置かれているかどうかの判定が必要になる ( 通常のユーザー操作でホームに配置された時と識別出来る必要がある )。

AppWidgetID が必要になるケースは、ウィジェットごとに何かしら表示を設定出来るケースとウィジェットのクリックが必要な時。

投稿者 Takenori : 23:32 | トラックバック

2011年01月30日

Windows で Android のソースコードを取得する

Get Android Source Code を見ると Linux と Mac OS X でしか書いておらず、Windows での方法については書かれていない。
Windows でのダウンロード方法を検索すると、cygwin を使ってやる方法が見付かるくらい。
TortoiseGit を使ったりしてもっと手軽に出来ないかと試してみたら、ある程度簡単に出来た。
ソースコードのダウンロード方法に書いてある「git://android.git.kernel.org/platform/manifest.git」を TortoiseGit で Clone すると、manifest/default.xml が出来るだけで、ソースなどは取得されない。
このXMLの中を見てみると、project タグに path、 name と言う属性があるものが並んでいる。

この中で、例えば、
<project path="frameworks/base" name="platform/frameworks/base" />
と言うのは、
git://android.git.kernel.org/platform/frameworks/base.git
を Clone すればソースコードが取得出来る。

他に、例えば、
<project path="packages/apps/Calculator" name="platform/packages/apps/Calculator" />
なら、
git://android.git.kernel.org/platform/packages/apps/Calculator.git
を Clone すればソースコードが取得出来る。

これでルールはわかったも同然。
後は同じように Clone していけばソースが取得していけるし、全部取得したいのならスクリプトでも書けばいい。
ただ、frameworks/base があればだいたいは事足りる。
ベース部分のクラスはだいたいこの中に入っている。

Linux 使ったり、cygwin 入れたりせずに、TortoiseGit でエクスプローラで手軽にソースコードを取得したいなら、こういう方法もあると言う話。

投稿者 Takenori : 02:13 | トラックバック

背景タイリングモードの適用

こんな感じの XML ファイルを drawable フォルダにおいて、これを android:background に設定すれば、背景に任意画像を単純拡大ではなく任意のタイリングモードで表示できる。

<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
     android:src="@drawable/bg_image"
     android:antialias="false"
     android:dither="false"
     android:filter="false"
     android:gravity="top|center_horizontal"
     android:tileMode="clamp" />

通常の拡大は fit なので、縦横比が変わってしまう。
タイリングモードは、クランプやミラーなどテクスチャのラッピングモードとだいたい同じ。
背景に画像を置いて複数の解像度に対応する場合は、このタイリングモードを意識して背景画像を作った方が良い。
タイリングモードではなく、9-patch 形式の方が場合によっては扱いやすいかもしれない。
9-patch 形式は、画像の中で拡大する部分と等倍で表示する部分を指定して画像を引き延ばすための形式。

XML によるタイリングモードの指定は便利なのだが、Android 1.6 だとうまく適用されない場合がある。
どうも、Bitmap の density 周りがうまく適用出来てないようなのだが、あまり詳しくはソースコードを追っていない。
Android 2.1 なら、この不具合は解消されている。

Android 1.6 でこの不具合を回避するには、以下のようにレイアウト適用後1個1個関数を呼んで設定していけばよい。

setContentView(R.layout.main);
final Drawable drawable = getResources().getDrawable(R.drawable.bg_image);
if( drawable instanceof BitmapDrawable ) {
    final BitmapDrawable bmpDrawer = (BitmapDrawable)drawable;
    bmpDrawer.setGravity(Gravity.TOP|Gravity.CENTER_VERTICAL|Gravity.CLIP_HORIZONTAL|Gravity.CENTER_VERTICAL);
    bmpDrawer.setTileModeXY( TileMode.CLAMP, TileMode.CLAMP );
    bmpDrawer.setAntiAlias( false );
    bmpDrawer.setFilterBitmap( false );
    bmpDrawer.setDither( false );
    view.setBackgroundDrawable( bmpDrawer );
}

XML で設定出来るものをわざわざソースコードで設定するのは面倒だけれども、不具合のようなので仕方ない。
Android 2.1 以降を対象とするのなら気にしなくても構わない。

投稿者 Takenori : 21:53 | トラックバック

2011年01月31日

複数配置されたウィジェットでクリックに反応する

よくあるサンプルソースでは、ウィジェット更新時以下のようなソースコードが書かれている。

RemoteViews remoteViews = new RemoteViews( getPackageName(), R.layout.main );
remoteViews.XXX; // 更新
ComponentName widget = new ComponentName( context, AppWidgetProvider.class );
AppWidgetManager manager = AppWidgetManager.getInstance( context );
manager.updateAppWidget( widget, remoteViews );

これでもウィジェットの表示は更新されるが、同じウィジットが複数配置されている時、クリックに反応するのが最後に配置されたウィジェットのみになったり、反応しなくなってしまうようだ。
複数配置するようなウィジェットではなくても、複数配置することは可能なのでクリックで何かしらリアクションするウィジェットの場合はそのことを意識して作った方が良い。
で、複数あっても全てのウィジェットで反応するようにするには以下のように1個1個 updateAppWidget をコールしていく。

RemoteViews remoteViews = new RemoteViews( getPackageName(), R.layout.main );
int count = mAppWidgetIds.size();
for( int i = 0; i < count; i++ ) {
  int appWidgetId = mAppWidgetIds.get(i).intValue();
  remoteViews.setOnClickPendingIntent( R.id.widget_frame, makeClickPendingIntent(context, appWidgetId) );
  AppWidgetManager.getInstance(context).updateAppWidget(appWidgetId, remoteViews);
}


投稿者 Takenori : 21:04 | トラックバック

2011年02月01日

解像度まとめ

開発時に注意する必要がある解像度についてまとめる。

480x320 以下の解像度の端末は古い機種のみかと思いきや、廉価版等で新たに出てきているので単純に未対応とするのは微妙な状況に。
800x480、854x480 辺りがメインストリームだと思われる。
ここに対応していればある程度カバーできるはず。
IS03 で、そうも言えない状況になっている気もするけど。
シャープは変な解像度の端末出し過ぎ。
アスペクト比はバラバラ。

アプリの複数解像度対応は、画像の準備と表示確認・調整が大変。
細かいことを気にせず、解像度非依存レイアウトで作って(作り方を理解して)いれば、それほどでもない気もする。
ゲームは、800x480 を主体にして、それ以外の解像度は内接拡大縮小辺りが現実的?

解像度 アスペクト比 端末 備考
320x240 4:3 IDEOS
Pocket WiFi S
 
480x320 3:2 HT-03A
Optimus chat L-04C
HTC Aria
SoftBank 004HW
Creative ZEN Touch 2
 
800x480 5:3 LYNX 3D SH-03C
GALAXY S SC-02B
HTC Desire/2/HD
Libero SoftBank 003Z
DELL Streak
GALAPAGOS SoftBank 003SH
GALAPAGOS SoftBank 005SH
SIRIUS α IS06
Creative ZiiO 7インチ
 
854x480 約 16:9 Xperia SO-01B
REGZA Phone T-01C
REGZA Phone IS04
IS05
 
960x480 2:1 IS01
LYNX SH-10B
 
960x640 3:2 IS03  
1024x600 約 16:9 Galaxy Tab
LuvPad AD100
 

投稿者 Takenori : 20:59 | トラックバック

2011年02月03日

GC 対策

アプリなら時々 GC が走ってカクついても大して気にしなくてもいいと思うが、ゲームだと気になる。
そこで幾つかそのことに対する対策を。

余計なGCの発生を抑止

import dalvik.system.VMRuntime;
...
private static final long INITIAL_HEAP_SIZE = 4*1024*1024;
...
long oldHeapSize = VMRuntime.getRuntime().setMinimumHeapSize(INITIAL_HEAP_SIZE);

setMinimumHeapSize の説明を見ると――
最小ヒープサイズを設定し、以前の最小サイズを返す。
もし、指定の最小サイズが最大サイズよりも大きい場合は、最大サイズが使用される。
サイズが 0 かマイナスの時、最小サイズ制限を抑止する。
Synchronized to make the order of the exchange reliable.(変更の順序は保障される? どう訳すの?)
この設定は GC へヒントを与えるだけであり、無視されるかもしれない。
――と書かれている。
組み込みアプリのソース中で余計なGCの発生を抑止するために、上記のサンプルソースのように記述されている箇所があるので、ある程度は効果が期待出来るのではないかと思われる。
ただ、実際に振る舞いの検証等は行っていない。


オブジェクトプール
何度も new せず、出来るだけオブジェクトを使い回す。
不要になったオブジェクトをリスト等に入れておき再利用する(オブジェクトプール)。
等して、ゴミがたまるのを出来るだけ避ける。


GC を明示的に呼び出す
暗転時などある程度処理が止まっても差し支えないタイミングで java.lang.System.gc(); を明示的に呼び出して GC を実行し、不用意なタイミングで GC が呼び出されることを出来るだけ避ける。

投稿者 Takenori : 22:01 | コメント (4) | トラックバック

2011年02月04日

任意の画像を読み込めるようにする(ロード時の画像縮小)

任意の画像を読み込めるようにすると、とたんに OutOfMemoryError 例外が出て落ちる。
特にカメラフォルダの中の画像を読み込もうとしたらすぐに落ちる。
そこで読み込む時はまず大きさだけ読み込んで、大きすぎた場合は縮小されたものを読み込む。

private Bitmap getImage( String fullPath ) {
    // まずは大きさだけ得る
    BitmapFactory.Options bmOpt = new BitmapFactory.Options();
    bmOpt.inJustDecodeBounds = true;
    BitmapFactory.decodeFile( fullPath, bmOpt );
    int width = bmOpt.outWidth;
    int height = bmOpt.outHeight;
    // 表示対象の大きさ
    int w = getWidth();
    int h = getHeight();
    if( width > (w*2) || height > (h*2) ) {
        // 大きすぎる(2倍以上の)時は、どちらがより大きいか計算
        int scaleW = width / (w*2);
        int scaleH = height / (h*2);
        int scale = Math.max(scaleW, scaleH);
        bmOpt.inJustDecodeBounds = false;
        bmOpt.inSampleSize = scale;  // 1 / scale の大きさの画像を得る
        return BitmapFactory.decodeFile( fullPath, bmOpt );
    } else {
        return BitmapFactory.decodeFile( fullPath );
    }
}

ある程度の大きさで読み込んだら、次に欲しいサイズに縮小する。

int width = bmp.getWidth();
int height = bmp.getHeight();
int w = getWidth();
int h = getHeight();
float scaleWidth = (float)width / (float)w;
float scaleHeight = (float)height / (float)h;
float scale;
if( scaleWidth > scaleHeight ) {
    // 横の方が大きすぎる
    scale = 1.0f / scaleWidth;
} else {
    scale = 1.0f / scaleHeight;
}
Matrix matrix = new Matrix();
matrix.postScale( scale, scale );
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap( bmp, 0, 0, width, height, matrix, true);

元々ちょうどのサイズならそのまま読めばいい。

投稿者 Takenori : 23:46 | トラックバック

2011年02月08日

サムネイルを読み込みつつ表示する

サムネイルの生成はある程度時間がかかるので画像が多い場合、一気に表示しようとすると表示前に待たされる。
そこで、GridView でまず全てのサムネイルのリストを表示して、バックグラウンドで画像を読みつつ、サムネイルを更新していく方法。

バックグラウンドで画像を読み込むのは AsyncTask を使うと楽。
android では UI スレッド以外で UI の更新は出来ない。
そのため UI スレッド側のメッセージキューにメッセージ投げて更新してもらうというような処理をする必要があるが、AsyncTask を使うとこの更新処理を呼び出す部分をある程度勝手にやってくれる。
UI 更新のためのメソッドをコール、もしくはコールされるタイミングがあるので、そのメソッド内に処理を書いておけば UI 更新出来る。
( ゲームの場合はロックして描画して、アンロックするという普通の排他処理で書ける )

具体的な方法は 1枚読むごとに AsyncTask::publishProgress をコールして、AsyncTask::onProgressUpdate で BaseAdapter::notifyDataSetChanged をコールしてやると再度 BaseAdapter::getView がコールされるので、新たに読み込まれたサムネイルを表示できる。
ただ、これだと 1個読むごとに GridView の中の全部のビューを再生成していることになるのでその辺りどうにかならないものかと思う。
新たに読まれたサムネイルだけ更新する方法が何かありそうな気がするがわからない。

投稿者 Takenori : 09:10 | トラックバック

2011年02月09日

1.6 で ListView が透過されない状況への対処

ListView の背景や後ろに画像を設定した状態で、少し画面に触れてそのまま指を移動し、離すとリストのアイテムがある位置が黒くなり、背景が見えなくなってしまう ( ロングタップにならないくらいの時間触れてから滑らす ) 。
少し特殊な操作なので気付かれないかもしれないが、作っていてあれ?と気付くので、この問題に対処できればしたい。
android:theme に Theme.Translucent を設定すれば、2.1 以降であればこの問題は解消される ( 透けて欲しくないところも透けてしまったりするが ) が、1.6 ではそれでも直らない。
1.6 で対処するには、ListView::setScrollingCacheEnabled で false にするか android:scrollingCache="false" を ListView に設定する。
これで、上記のような操作をしても黒くなったりしない。

ちなみに黒くなった後、他のところをタップすると直るので、たいした問題ではない。

投稿者 Takenori : 21:20 | トラックバック

2011年04月12日

PDFBox を使おうとしたが無理そう

Android で PDF を表示するために、ライセンス的に使いやすく JAVA で書かれた PDFBox を使おうとしたが、どうもそのまま使うのは難しそうだ。
まず、java.awt.* などの Android で使えないライブラリが使われているので、それらを使用しているクラスは使えない。
PDFBox はレンダリング時に java.awt.* を使用しているので、ここはまず書き換えないと無理。
パーサー部分については、PDFBox はどうも一気にドキュメント構造全部を調べて、内部構造に変換してしまうようだ。
この処理が時間かかる上にメモリ不足で落ちる。
小さいドキュメントなら大丈夫だと思うが、ある程度の大きさがあると無理。

と言うことで、PDFBox をそのまま使うのは断念した。
そこで、最初にPDFのページ数とページツリー構造を読み込む処理を行い、次に任意のページがレンダリング出来るような形でライブラリを作ることにした。
PDFBox から使えそうな部分はそのまま持ってきて。

作るに当たって最初はソースコードを読んでいたんだけど、それだけでは PDF の構造がわかりづらいので、オンライン上にある英文の PDF の仕様書を見ていたんだけどそれも辛かったから、PDFリファレンス第2版 を見ながら作ることにした。
この本があるとだいぶ作りやすい。

投稿者 Takenori : 21:19 | トラックバック

2011年05月02日

設定画面のリストに画像を埋め込む

設定画面 ( PreferenceActivity ) で、addHeaderView を使って任意のビューをリストに埋め込むのは厳しいようだ。
最初に埋め込んだ下の列が 1個ずつずれて、表示と動作が変わってしまう。
設定画面に画像を入れて、設定変更でその画像が変化するようにしようとしていたんだが、この方法では無理だった。

結局、Preference を継承した独自の Preference を作って、それを組み込むことに。
リストに表示されるビューは、preference の XML の方に、android:layout="@layout/pref_image_layout" と言うようにレイアウト入れることで、これが使用されるようになる。
継承した Preference でメソッド呼んで設定してもいけると思うが、XML で設定しておいた方が楽。
継承した Preference で実際に画像を表示するには、onBindView をオーバーライドして、ここからレイアウトから ID で ImageView を得て、これに画像を設定してやればいい。
設定する画像は、setImageBitmap 等適当なメソッド追加して、これに PreferenceActivity (を継承したクラス) から画像を設定すれば、設定画面内に画像が表示され、設定変更に従って画像の切り替えが出来るようになる。

継承した Preference のソースは以下。
上に書いていることそのまんま。

class InlineImagePreference extends Preference {
    private Bitmap mImageBitmap;
    private ImageView mInlineImage;

    public InlineImagePreference(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    @Override
    protected void onBindView(View view) {
        super.onBindView(view);
        if( mImageBitmap != null ) {
            mInlineImage = (ImageView)view.findViewById(R.id.inline_image);
            if( mInlineImage != null ) {
                mInlineImage.setImageBitmap( mImageBitmap );
            }
        }
    }
    public void setImageBitmap( Bitmap bmp ) {
        mImageBitmap = bmp;
        notifyChanged();
    }
};

レイアウトファイルは以下。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:layout_gravity="center_horizontal">

    <ImageView
        android:id="@+id/inline_image"
        android:scaleType="center"
        android:layout_height="wrap_content"
        android:layout_width="fill_parent" />

    <ImageView
        android:layout_height="1dip"
        android:layout_width="fill_parent"
        android:src="@drawable/divider_horizontal_light" />

</LinearLayout>

preference の XML には、以下の要素を追加。xxx はパッケージ。

<xxx.xxx.xxx.InlineImagePreference
    android:key="inline_image_key"
    android:selectable="false"
    android:persistent="false"
    android:layout="@layout/pref_image_layout" />

設定画面にプレビュー画像を入れたいような場合は、このような方法が役に立つ。

投稿者 Takenori : 21:04 | コメント (4) | トラックバック

2011年05月10日

Androidアプリはどの程度売れるのか?

やはり、現在 Android Market で有料アプリを出しても売れないようだ。
現時点では他の市場で出した方がマシに思える。
無料アプリであれば、数は出る。

台数等の市場予想は以下のページがよくまとまっている。
国内Androidマーケット現況(上)Android端末の普及台数予測とユーザ属性、国内アプリマーケットまとめ [2011年4月]

ホームページは、後数年で PC から見るよりもスマートフォンから見る方が多くなる予想なので、PC 専用のサイト以外は対応を進めた方がよさそう。

現時点の 400万台で、ヒットしている日本のアプリを見ると 1000~5000本がほとんど。
Android Market で表示されているのは、インストール数なので、売れた数ではないが、DL数とインストール数の比は、だいたい 50%~80%くらい、有料ならもっと高い比率かもしれない。
だから、よくてこの数の倍出てて、普通に考えるなら、インストール数とあんまり変わらない数出てる。

仮に、1000本として価格が100円~1000円でヒットしてどれくらいの売り上げが見込めるのか計算してみる。
普及台数予測は上のリンク先のものを使う(既に予想よりも実数は下回っているようだが)。
100円で売れても、実際に入ってくるのは70円なのでそれで計算している。

台数(万台)予想本数売り上げ予想
現時点400100070,000円~700,000円
201110002500175,000円~1,750,000円
201220005000350,000円~3,500,000円
201335008750612,500円~6,125,000円
2014450011250787,500円~7,875,000円
2015500012500875,000円~8,750,000円
20166000150001,050,000円~10,500,000円

売れる数が現在のまま推移した場合なので、こうなる確率は低いと思う。
ユーザー層が変わっていくのは確実だろうし、現在の Android Market はお世辞にも使いやすいとは言えない。
初期の頃から比べるとだいぶ改善されてはいるが、正直なところ辛い状況なのは間違いない。
だから、Android Market の改善によって販売数が延びる余地は十分にある。

日本語版と英語版が出ているものを見比べたりすると、世界ではこの十倍以上は売れているようだ。
日本向けではなく、世界向けで作るのであれば、現時点でもヒットすれば結構出そう。
ただ、その分競争激しいが。

販売予想から開発費を逆算して作るとしても、現時点では話にならない金額なので、開発費をペイするのは難しそうだ。
予想から考えると開発費20万くらいで作れるものを作る感じだろうか?
これは一人で1~2週間程度のものになるので、かなり厳しい。
個人の開発者ならもっと作り込めるが。

ギャルゲーを見てみると、キラ☆キラ で、インストール数50~100となっている。
インストール数の比率から見て、50~200本売れているとして、70,000~280,000円だろうか?
これ単体では全然開発費回収できていないと思う。

わかっていたけど、まだまだ厳しい。

投稿者 Takenori : 15:34 | コメント (2) | トラックバック

2011年05月18日

ヘルプはWebViewで

ヘルプを入れているアプリは少ないと言うか、そもそも必要のないアプリも多いけど、ヘルプを入れる時は WebView を使うのが楽。
xml のレイアウトファイルでちまちま作ってもいいけど、HTML で書いた方が今までと同じように作れるので楽だろう。
その場合、HTML ファイル等は assets フォルダに入れる。
assets フォルダ内のファイルは file:///android_asset/ でアクセス出来る。

assets フォルダ内に index.html ファイルを入れて、それを表示するためのアクティビティのソースコードは以下の通り。

public class HelpActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.help_layout);
        WebView webview = (WebView)findViewById( R.id.webview );
        webview.loadUrl("file:///android_asset/index.html");
    }
}

レイアウトファイルは次のようになる。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <WebView
        android:id="@+id/webview"
        android:layout_width="fill_parent" 
        android:layout_height="fill_parent" 
        android:layout_weight="1" />
</LinearLayout>

かなり少ないコードで実現できるし、次回はヘルプの HTML ファイルを書き換えるだけでソースコード類はそのまま使い回せる。

投稿者 Takenori : 14:06 | トラックバック

2011年05月28日

足きり端末の確認

使用する機能がない端末でインストールできないように uses-feature で必要な機能を指定できるが、ここには必要以上に追加しない方がよいようだ。
ゲームカメラと言う、カメラアプリを作ったのだが、最初カメラとフラッシュ、オートフォーカス ( android.hardware.camera、android.hardware.camera.flash、android.hardware.camera.autofocus ) を指定していたのだが、これらを指定していると初代 Xperia ではインストール出来なくなっていた。
開発時のインストールは問題なく行われるが、マーケットに上げてそこから落とそうとしたときに落とせない状態になる。
フラッシュとオートフォーカスは現時点ではサポートしていなくて、後からオプションとしてサポートする予定だった。
オプションなので、必須というわけではないので、指定を外して上げ直すと問題なくダウンロード&インストール出来るように。

デベロッパーコンソールのアプリページを開いて、「サポートされている端末」の「端末を表示」で、想定している主要な端末があるかどうか確認しておいた方が良さそうだ。
ここで、想定している端末がない場合、AndroidManifest の設定を見直した方がいいかもしれない。

投稿者 Takenori : 23:15 | トラックバック

2011年06月02日

Android Market で有料アプリが1ヶ月でどれくらい売れたか

5/3~5/31 の約1ヶ月間で集計結果が出たので、書いておく。
有料アプリは、透子さんのバッテリーメーター
売れたのは 259本で、金額は 18,130円。
Androidアプリ、有料版の8割はダウンロード数が100未満――米Distimo調査
という話、なので上位2割には入っていることになる。

休日と平日の放課後or定時後に主に売れている様子。
若干、休日との関連性は微妙な部分もあるけど。
時間区切りは、太平洋標準時(PST)となっているので、日本だと17時区切りとなって、少し前の日にずれ込むので注意が必要。

日付本数累積備考
2011/5/3910円13910円公開開始
2011/5/4210円31,120円 
2011/5/51,330円192,450円 
2011/5/61,470円213,920円萌えドロイドに取り上げられる 現在6,358PV
2011/5/71,680円245,600円 
2011/5/81,050円156,650円 
2011/5/9700円107,350円 
2011/5/102,100円309,450円ツイッターである程度拡散する 約100RT
2011/5/111,680円2411,130円 
2011/5/12630円911,760円 
2011/5/13420円612,180円 
2011/5/14770円1112,950円 
2011/5/15630円913,580円 
2011/5/16700円1014,280円 
2011/5/17560円814,840円 
2011/5/18420円615,260円 
2011/5/1970円115,330円 
2011/5/20210円315,540円 
2011/5/21420円615,960円 
2011/5/22210円316,170円 
2011/5/23140円216,310円 
2011/5/24140円216,450円 
2011/5/25420円616,870円 
2011/5/26350円517,220円 
2011/5/27210円317,430円 
2011/5/28280円417,710円 
2011/5/2970円117,780円 
2011/5/30280円418,060円 
2011/5/3170円118,130円 
合計 259  

後半落ち込んできているので、このまま下がっていったとして、最終的にどの程度売れるのかはわからない。
比較的伸びた方だと思うが、現時点では Android アプリはこの程度だろうか?
ちなみに開発費は20万円程度 ( CG 4枚×2万円 + 開発6日×2万円 )。
1年かかっても回収できる見込みは薄そうだ。


萌えドロイドだと、アクセスランキングで比較的上位にいた様子。
5/1 ~ 5/7 萌えアプリレビューのアクセスランキング!☆今週のトップ10!人気のアクセスランキングを発表!
『5/8 ~ 5/14 萌えアプリレビューのアクセスランキング!』☆今週の萌えアプリランキング!トップ10を発表!!
『5/15 ~ 5/21 萌えアプリレビューのアクセスランキング!』☆今週の萌えアプリランキング!トップ10を発表!!

透子さんのバッテリーメーター

投稿者 Takenori : 00:53 | トラックバック

2011年07月08日

海外でどれくらい売れたか(現状役立たない数値)

透子さんのバッテリーメーターを6月6日に英語に対応して、英語圏でも公開開始した。
6月中に売れた本数は132本で、そのうち海外は15本。
英語対応してリリースした時は新着に出るので、それで売れたと思われる本数は2本。
残りの13本は検索で見つけて購入だろうか?
海外の人にリーチする手段が思いつかず、放置状態でこれくらい売れたと言うのは、この系統のものも意外と海外でも売れるんじゃないか?と思えたりはするが、全然ダメな気もする。
売れているのはほとんどアメリカで、他はイギリス、オーストラリア。

日本では、公開してツイッターでツイートしたら、自分の周りで少し盛り上がって、それが間接的に届いた人が萌えドロイドにレビュー依頼して掲載、その数日後にツイッターで拡散と言う流れで、ある程度数が伸びた。
6月は、EXドロイドレビュー記事 が載った。
つまり、コンテンツを作った後は、ツイッターでこんなの作ったーと言った後、ほとんど何もせずに広まっていった。
とは言っても、5、6月の収益は、27,383円と開発費の回収にはほど遠いんだけど。
5月は259本、6月は132本と半減しているので、長く見ても回収出来る気がしないと思っていたんだけど、なんか今月少し変化があった。
まだ数日しか経っていないのでどうなるかわからないけど、来月また何か書くかもしれない。
ただ、やはりまだ開発費の回収すら難しいという気はしている。

海外である程度メジャーなレビューサイトなどで取り上げられれば、海外でも数が伸びそうだけど、それがどこかわからない。
運良く海外の販売数が増加した時、それがどこからか調べてみて、そこで気付くかもしれないとは思っているけど。
このような状態なので、現在の海外で売れた本数というのは、リリースしてずっと放置していた場合どれくらい売れるのかと言うのを表しているに過ぎない。

やはり、まだまだ難しい状況だ。
もうそろそろ次のアプリが公開出来そうなので、そちらの推移も見てみたいところ。

投稿者 Takenori : 21:15 | トラックバック

2011年07月12日

スライドノベル

こういうコンテンツが欲しいと思って作ったシステムで、PC などのノベルゲームとはだいぶ違う。
去年の1月くらいにこの辺りのことはつぶやいていた。
ノベルゲームのバックログだけで読み進められないか云々と。
ログゲーとかレスあった。

ちょっとした暇な時間をつぶすのに、いつも鞄にライトノベルを入れているんだけど、ちょっとした電車の待ち時間などはスマートフォンでツイッターを読むなどしている。
少し長めの待ち時間がある時はライトノベルを読む。
ツイッターは、手軽なんだけど読み終わってしまうこともあるし、パケット通信料が気になったりする。

と言うことで作った。
ちょっとした時間をつぶすための片手で操作できるコンテンツ。

まずは参考実装としてコンテンツを作った。
シリンダーヘッド 第1話

開発用のエンジンとパッケージ化するツールはこれから開発する。
開発用のエンジンは、Windows 共有に置いたスクリプトと画像を読み込んで動作する形。
そう遠くないうちにリリース出来ると思う。

リリースされたものには、スライドノベル情報ページへのリンクが入っている。
Android アプリには、とっかかりをどうするかという大きな問題がある。
このページは同種のコンテンツを集約し、一つのコンテンツから他へつなげられる道を準備することを目的としている。
同種のコンテンツが増えれば、とっかかりの問題はある程度解消されるのではないかと考えている。

まあ、自分が欲しいから作ったと言うのが大きいんだけど。

投稿者 Takenori : 22:57 | トラックバック

2011年07月16日

スライドノベルエンジン

スライドノベルエンジン開発用を公開した。
使い方の説明も書いた。
まだ装飾等していないので、読みづらいかもしれないが。
あと、残りは Android アプリ化するツールを作ってリリースすれば、誰でも作れる環境が整う。
一通りそろえたら直した方がいいところや足りない機能を補っていく。

ツール類はフリーソフトとして公開し、販売等特に制限は設けない。
自由に作って、コンテンツが増えるといいと思っている。
リリースしたものの連絡をもらえれば、スライドノベル新作情報ページに追加する。
登録は半自動化したいところ。
別にこのページを強制するつもりはなく、Android アプリ化するツールでは、任意の情報ページを入れられるようにする。
入れ替えるとしたら、「情報」でリリース元とかの情報を入れるところがあるので、何らかの集約したページにすることになると思う。
あった方がいいだろうから、作っただけであまり管理することは乗り気ではなかったりするんだけど。

体験版代わりに1話は無料にして、1話の最後の続くと書いた後に2話の Market へのリンクを貼れば、順次読みながら購入していけるようになるはず。
また、今はないけど、シリーズリストもアプリに入れて、各話への Market へのリンクを入れられるようにする。
シリンダーヘッド 第1話のようにバージョンアップで続刊をお知らせするスタイルもいいんじゃないかと思っているけど、いっぱい出てきたら新作をチェックするだけのアプリを作るかもしれない。
増えるかどうかわからないけど。

個人的には、ちょっとした時間にさくっと読むアプリとしていいと思っているんだけど、その辺りの評価はあんまり聞いてないのでよくわからない。
作っている途中は、ADV・ノベルシステムを作る誘惑に何度も駆られたけど、ぐっと我慢して割り切って作った。
途中、知り合いにこんな感じのどうかな?と聞くと、「ありだとは思うけど……」と微妙な評価だったので、いいのか悪いのかさっぱりわからない。
現状開発コストがかけづらい Androidアプリでとりあえず作って感触を見るような用途にもいいと思うんだけど。
自分でも評価しかねるコンテンツ形式なので、どうなるかはさっぱりわからない。

シリンダーヘッドの2話以降は、50KB~100KBくらいで200円くらいでリリースしようと思ってる。

投稿者 Takenori : 02:36 | トラックバック

2011年07月19日

Android アプリストア まとめ2

増えたり、減ったりしているのでまたまとめる。
他にもあった気がする。マーケットではないところも混じっているかもしれない。

Android Market

Camangi Market

au one Market

Ameba AppMarket

Gゲー

appliko ( 女性向け )

mopitaマーケット

アプリ☆ゲット

appli.jp

AppBrain

myappee

Openroid

M-trix Market ( 成人向け )

エロゲマーケット ( 成人向け )

sonscripter

Android APPS


バナドロイド ( バンダイナムコ ) (今夏予定)

Amazon (日本はまだ?)

独自マーケット提供終了
早期に開設したものの独自マーケットの提供を終了してしまったもの

andronavi ( NECビッグローブ )

AndroApp ( Vector )

投稿者 Takenori : 16:54 | トラックバック

2011年07月21日

スライドノベルapkジェネレーター

スライドノベルapkジェネレーター

ジェネレーター出来た。
これで一通り開発するための環境は整った。
ローカルでAndroidアプリ作るので環境構築など少しハードルが上がって、誰でも作れるというわけにはいかないかもしれないけど、何か開発したりしている人にはそんなに難しくないと思う。

投稿者 Takenori : 19:42 | トラックバック

リソースIDの採番をずらす

Android のリソースIDは自動的に割り当てられる。
プロジェクトが違っても、開始番号は同じで、リソースを含んだjarファイルを作ろうとしても番号が重複してしまうので出来ない。
アプリでは、0x7f000000 辺りが使われるようだ。
でも、標準で使われる android.jar にはリソースが含まれているので、何とかする方法はありそうな気がする。
android.jar 内部では 0x01000000 辺りが使われている。
ただ、この Androidでライブラリプロジェクトを作成する際の考慮事項 を見ると出来ないようなことが書いている。

Eclipse の助けを借りず、自分でリソースをコンパイルするのなら採番をずらすことは可能だった。
リソースのコンパイルは、aapt を用いて行うが、このときに「-x」オプションを使うと採番がずれる。
この時、0x02000000 辺りが使われる。
aapt の使い方 を見ると、コマンドの意味がある程度わかる。
コマンドはこんな感じで書いた。
aapt package -u -x -m -M %WORKDIR%\AndroidManifest.xml-F resoureces.jar -J %TMPDIR% -S %TMPDIR%\res -I %ANDROID_JAR%

%~%ってやつは、batファイル内で割り当てている。

A Detailed Look at the Build Processを見る限り、外部リソースを入れてビルドすることは出来るように見えるが、Eclipse で読ませてやろうとするといろいろとうまくいかない。
コマンドやjarに含めるものの構成が間違っているんだと思われる。

と思って見直していたら、「--custom-package」オプションがあった。
もしかしてこの辺りかな?
また今度試してみようと思う。

apkファイルの構成は、バッチファイルからAndroidアプリを生成する このページを読むとある程度わかる。

投稿者 Takenori : 21:02 | トラックバック

2011年10月08日

TJS2 for Java

C++BuilderXE2 が、Mac のクロスコンパイラを備えていると言うことで、吉里吉里2の Mac版を比較的楽に作れるかなと思い購入したものの、VCL から FireMonkey への移行は手作業と言うことで、普通に移植工数がかかることがわかった。
はじめから、FireMonkey ベースで作っていれば、Win/Mac 両対応になるんだけど。
普通に工数がかかるとなると、Mac版を作るのは躊躇する。
Mac は持っていないし、技術的好奇心だけだから。
技術的好奇心 + 自分が欲しいとなると、Android版になるし。


Android で使うスクリプト言語は何がいいか検討していた。
Squirrel の Java版はあるようだけど、安定性など怪しい。
やはり、Rhino かと思ったが、プロトタイプベース・オブジェクト指向ってことで躊躇する。
そして、TJS2 を Java に移植することにした。

構文解析器の bison 部分をどうするかだけど、再帰下降法で書き下すことにした。
Java 用の bison やそれに類するものはいろいろとあるようだけど、手書きの方が結合部分や移植性等なにかと都合がいいかと思ったわけだけど、実装中これは失敗したかもしれないと思った。
可読性や構文の変更を考えると、圧倒的に bison などの方が良いけど、ほぼ構文が固定されているのであれば、その辺りはそれほど気にすることもないというのもあったわけだけど。

TJS2 を大きく分けると、字句抽出器、構文解析器、構文木→バイトコード、VM 、組み込みクラス群と 5工程ある。
構文解析器は、TJS2 のメイン構文とプリプロセッサ部分、日時部分の 3つ。
字句抽出器もそれに伴いあるわけだけど、メイン以外はそれほど大きくない。
プリプロセッサ部分は、字句抽出器と構文解析器(含む評価)は完成した。
久しく本格的なスクリプト言語のインタプリタに触れていなかったので、プリプロセッサ部分は再帰下降法で書くのにいい練習になった。
プリプロセッサ部分はちょっと高機能な電卓みたいなものなので、良くある例で実装できる。
現在は、メイン部分の字句抽出器と構文解析器部分をデバッグ中。
再帰下降法で書かれた構文解析器をデバッガで追うのは少し楽しい。

C++ → Java での一番問題は、Java がポインタを明示的に扱えないことだろうか。
なんだかんだでほぼ全書き換えになってる。
後は演算子のオーバーロードがないのも辛い。

TJS2 for Java で気になるのは、コンパイル時間とバイナリサイズ。
これらはできあがってみないと何とも言えないけど、あまりにコンパイル時間が長いと起動に時間がかかって困る。
ある程度はスプラッシュでも出してしのげるが、あんまり長いとバイトコードでファイル書き出しして、それを読み込むようなことも考えている。
バイナリサイズは、数MB とかになると Android で手軽に組み込むのに躊躇してしまう。
それほど大きくならないようであれば、気軽に組み込んで TJS2 で Win上で手軽に開発出来るように持って行けるんだけど。
実行時間はそれほど心配していない。
許容範囲内だろうと思っている。
動かしてみたら爆死ってこともありえるが。
まあ、なんにしてもまずは淡々と実装していくしかない。

投稿者 Takenori : 18:47 | トラックバック

2011年11月06日

TJS2 が少し動くように

Array クラス以外の組み込みクラスはまだ実装できていないが、それ以外は一通り実装は出来た。
結果はおかしいが、TJS2 スクリプトを読んで、実行することは出来るところまで来た。
吐かれるバイトコードを比較しつつデバッグしていく。
バイトコードの逆アセンブラは実装したので、比較的確認しやすい。
一通り動くようになったので、いろいろとやりやすくなった。

C++ から Java にしたこで、コピーと参照周りでバグがありそう。
既に1個発見して直したけど。
後は構文解析器周りは一から書いているので、まだまだバグがありそう。

ほぼ同じ動作をするはずだけど、支障のない範囲内で完全に同じではない実装になっている。
Java の組み込みソートの都合上 Array のソートは常に安定ソートになっていたり、メモリ管理は Java 任せなので、finalize の呼び出しタイミングが異なったりするはず。
後は、結果が異なるようなら修正するつもりのものとして、String.sprintf が Java の String.format で実装されていることから書式指定が異なるかもしれない。
ここは後で見直して致命的に違うようであれば実装し直す。
それと、オリジナルの正規表現は boost のものになっているが、Java 版は組み込みの正規表現で実装する予定。
構文解析器は、完全に別実装だけど、ここはオリジナルと同じように動作するようにする。
ただ、エラーメッセージは少し親切に出力できるようになる。

後から追加するクラスは、普通に Java で書いたものを手間なくバインドできるようにする。
TJS2 から Java の任意のクラスを生成して使うことも出来るけど、そうする意味はあまりないので、明示的に追加する形にしようと考えている。

思ったよりも実装に時間がかかっているが、ソースコード量を見たら仕方ないか。

投稿者 Takenori : 15:02 | トラックバック

2011年11月11日

吉里吉里2 for Java/Android の公開について考える

TJS2 は、オリジナルの C++ のソースコードをコピペして、Javaに書き換えと、パーサー部分の新規書き起こしで実装している。
Java への書き換えはポインタやアロー演算子、演算子のオーバーロードなどの問題から全体にくまなく及んでいる。
Variant 型はタイプを持たず、Object方の変数の型で判断するクラスとして実装している(これはいくつかの場面で微妙かもしれないと思ったりしている)。

そんな感じで吉里吉里2ライセンス的には「流用」として取り扱われると言う解釈で良いようだ。


吉里吉里2ライセンスのそのまま適用は、Android アプリとして組み込むことを考えると例外条項を追加しないと難しい。
・配布されているソースコード/jarファイルに変更を加えずに、そこから継承したクラスを用いて最終成果物を生成することについては改造とはみなさない。
・配布されているリソースファイルの内、アイコン、アプリ名称を変更する場合も改造とはみなさない。(他にもいくつかAndroidManifest.xml等の変更の必要があるからそれらの列挙も必要)
など、この辺りちょっとややこしい。

オフィシャルへのコントリビュートについて検討。
オリジナルのC++起源でJavaに書き換えられたものを含むものの、オリジナルとは全く独立しているけど、その上で KAG3 等は動く。
メンテナンスは自分。
どうだろう?と思ったけど、動画部分のメンテナンスは自分になってるか。
吉里吉里2ライセンス適用は上述の問題がある。

開発用のエンジン本体を Android Market へ登録と差し替えを考えると、デベロッパーのアカウントなどで面倒が増えるか。
吉里吉里で何か組織とかあるわけじゃないしなぁ。


と言うことで、修正BSDライセンスで独立したものとして公開して、本家に取り込まれるなら取り込まれるという流れに身を任せた方法でいいか。
初期は修正が多いだろうし、Android Market へ登録とか自分のアカウントでやっといた方が小回りが利くし。

そして、 SourceForge.JP へ 吉里吉里Java移植プロジェクト を作った。
ある程度動く TJS2 のバイナリ公開と開発中のソースもコミットしている。

投稿者 Takenori : 23:19 | トラックバック

2011年11月13日

Java版 TJS2 の速度

Java版 TJS2 で AO bench in TJS2 が動作させられるくらいは動くようになったのでベンチマーク。
動くと言っても、Window や Layer は未実装なので、演算のみで計測。
CPU は Core i7-2720QM 2.2GHz。
ネイティブ吉里吉里2 2.31.2011.615 では、433000 msec。
Java版 TJS2 で 570128 msec。
32% 程度遅いが、思っていたよりも速い。
数倍のオーダーかと思っていたが……
ただ、実行結果はまだ見られないので結果があっているかどうかは分からない。
バイトコードが一致して、VMがその命令を実行することは確認しているので、ほとんど同じ処理はしているはず。
後、Java版 はまずは動くことを目標としていたので、最適化の余地はかなり大きい。
TJS2 スクリプトの実行速度という点であれば、ネイティブ版と大差ない速度で動く可能性もある。
32% の時点で大差ないとも思えるけど。

実行環境のバイトコードへのJIT ( Javaバイトコード/Dalvikバイトコード ) を実装したならネイティブ版の速度を超える可能性は十分にある。
まあ、Java VM のJIT によって TJS2 → Javaバイトコード → ネイティブコード と一部ネイティブになって動く部分もあるだろうし。
こうなるとネイティブ版で逆転するには LLVM の実装だろうな。
Javaバイトコード から LLVM バイトコードへ変換するものとか既にあったりするんだろうか?


なんにしても Java 版が意外と速いことが分かったので、副産物と思っていた Linux/Mac で動くと言うのも実用的に使えるかもしれない。
もっとも、影響があるのは画像部分の SIMD での処理やマルチスレッド処理部分だろうから、その辺りでネイティブには遠く及ばないだろうけど。
でも、SIMD や マルチスレッドがなかったとしても、最近の PC ならそこそこ普通に動く気はする。
一部のゲームのようにひたすらに重いことしなければ。

投稿者 Takenori : 14:47 | トラックバック

2012年01月09日

AssetManager.list が遅い

Xperia arc のみかもしれないが、AssetManager.list が遅い。
100msec程度もこの呼び出しにかかっている。
呼び出す度に apk ( zip ) ファイルアクセスでも発生しているのだろうか?
AssetManager.list から返ってきた結果を HashMap に格納するなどしてキャッシュすれば、2回目以降は高速にファイルリストが得られるが、1回目は時間がかかってしまう。
何もせずに同じパスで AssetManager.list を何度も呼んでいると、そこで多くの時間がかかってしまう。

投稿者 Takenori : 22:10 | トラックバック

2012年02月28日

多言語対応で出る Lint のエラー

strings.xml や array.xml で、values と values-ja 等複数言語対応用のリソースを置いた時、文字列リソースが片方にしかない時 Lint が"... is not translated in ja"とエラーを出してパッケージ化出来ない。
数値や英語でも意味が通る物など、別に日本語リソースを用意する必要のないものもある。
これらのリソースには「translatable="false"」をつけてやる事で、別言語のリソースを用意していなくてもエラーにならない。
<string name="text_resource" translatable="false"> と言うように追加する。

Lint のエラーをワーニングに変更するなどと言う対策が出ていたりするが、せっかく漏れをチェックしてくれているのにそれだと気付きづらくしてしまう。
上記のように明示的にこれは訳さないとリソースに記述する方が見逃しを防ぐ意味でよい。

投稿者 Takenori : 22:00 | トラックバック

2012年02月29日

メッシュ

Canvas.drawBitmapMesh がメッシュワープの用途でどれくらい使えるか少し見てみた。
タッチしたところにぎゅっと収縮するようなものをテスト用に作った。
MeshTest.apk
Android でインストールして実行できる。
20x36分割して、Canvas.drawBitmapMesh で描画している。

FPS を見てみると……

初代Xperia Android 1.6 - 6fps ~ 4fps
初代Xperia Android 2.1 - 23fps ~ 16fps
IS03 - 23fps ~ 16fps
Xperia arc - 27fps ~ 16fps
Galaxy tab - 21fps ~ 16fps
Eee Pad Transformer TF101 - 27fps ~ 16fps

Android 1.6 の Xperia は異様に遅いが、それ以外は 20FPS以上出ている。
Paint.setFilterBitmap を true にしているが、これを false にすると数FPS上がる。
分割数を減らしたり、面積を減らせばもっと早くなる。
そこそこ使えそうな速度で動くようだ。

投稿者 Takenori : 23:32 | トラックバック

2012年03月15日

getPixels と copyPixelsToBuffer

Bitmap.getPixels でピクセル情報を取得し、Bitmap.setPixels で書き戻すのと、Bitmap.copyPixelsToBuffer でピクセル情報を取得し、Bitmap.copyPixelsFromBuffer で書き戻すのはどちらが早いか計測した。
Xperia arc で 800x600 32bitカラーのBitmap で計測すると、
1. Bitmap.getPixels/setPixels は、33msec 程度。
2. Bitmap.copyPixelsToBuffer/copyPixelsFromBuffer (array) は、6~7msec程度。
3. Bitmap.copyPixelsToBuffer/copyPixelsFromBuffer (direct) は、11~29msec程度。
array は、int[] pixels = new int[w*h]; IntBuffer buff = IntBuffer.wrap(pixels); としたもの。
direct は、ByteBuffer b = ByteBuffer.allocateDirect(w*h*4); IntBuffer buff = b.asIntBuffer(); としたもの。
array を wrap した、IntBuffer で、copyPixelsToBuffer を使うのが一番早い様子。
ByteBuffer.allocate を使ってもたぶん同じ結果になると思われる。

取得後、各ピクセルにグレースケール化処理を行い、書き戻した場合はそれぞれ以下のようになった。
1. 56~59msec ( 343~674msec )
2. 29~34msec ( 280~321msec )
3. 1107~1248msec ( 1718~2143msec )
括弧内の数値は初回で、それ以外は2回目以降の数値。
JIT 等の効果により、2回目以降は速度が上がっていると思われる。
3 が異様に遅いのは、IntBuffer.get/put によってピクセル値の取得と書き戻しを行っているせいだと思われる。
他は、配列で処理している。

array を wrap した Buffer で取得設定し、array で処理するのが一番か。

Canvas.drawBitmap(int[] colors, int offset, int stride, int x, int y, int width, int height, boolean hasAlpha, Paint paint) を使って、書き戻した場合はどうかも計測した。
1. Bitmap.copyPixelsFromBuffer 3 ~ 4msec
2. Canvas.drawBitmap 31 ~ 33msec
この場合も Bitmap.copyPixelsFromBuffer が大幅に早い様子。

自前でピクセル処理する場合、Bitmap.copyPixelsToBuffer/copyPixelsFromBuffer (array) を使うのが良さそう。

投稿者 Takenori : 00:59 | トラックバック

2012年03月23日

SD インストールに対応する時の注意点

SD インストールに対応する場合、AndroidManifest.xml の manifest タグの属性に android:installLocation="auto" か android:installLocation="preferExternal" を追加する。
auto はどちらか自動的に判断されてインストールされる。
preferExternal は、SD優先だけど、SDがいっぱいなどの時本体側にインストールされる。
このどちらかを指定した場合、ユーザーが本体とSDでアプリを移動できる。

使用する Project Build Target を Android 2.2 API Level 8 以上にする。
古いバージョンでも動作させたい場合は、AndroidManifest.xml に <uses-sdk android:minSdkVersion="4" /> 等指定する。
また、使用している API がここで指定したもの以下であるようにする。
Project Build Target で新しいバージョンを指定すると新しいAPIを使用していてもビルド自体は問題なく通るが、古いバージョンの Android マシンで動かした時に落ちるので注意が必要。

デベロッパーコンソールでアプリをアップロードする時、公開設定のオプションのコピー防止をオフにする。
ここをオンにしているとSDにインストール出来ない。

ある程度サイズが大きいアプリはアンインストールされやすいが、SDインストールに対応していると消されづらいようなので、対応して問題なアプリは対応しておいた方が良さそう。
サイズが大きいと言っても5MBでも大きいと思われたりするから、積極的に対応した方がいい。

投稿者 Takenori : 18:44 | トラックバック

2012年04月04日

メモリと OutOfMemoryError

Androidメモ メモリ このページが Android でのメモリについてわかりやすくまとまっている。

ネイティブヒープ + Java ヒープ の合計サイズというのは、Debug.getNativeHeapAllocatedSize() + (runtime.totalMemory() - runtime.freeMemory()) で計算できるサイズのようだ。
この合計値がアプリケーションヒープの最大値を超えた時 OutOfMemoryError 例外が発生する。
アプリケーションヒープの最大値は ActivityManager.getMemoryClass() で得られるもののようだが、runtime.maxMemory() と同じ (単位は異なる) ようなので、runtime.maxMemory() で計算できる。
ActivityManager の方は Context が必要なので、Runtime で得られる方がいろいろと扱いやすい。
つまり、擬似コードで書くのなら以下のような感じ。

Runtime runtime = Runtime.getRuntime();
if( (Debug.getNativeHeapAllocatedSize() + (runtime.totalMemory() - runtime.freeMemory())) > runtime.maxMemory() ) {
 throw new OutOfMemoryError();
}

この辺りがわかるとメモリ限界付近を調査しやすくなる。

Android 3.0 ( APIレベル11 ) から AndroidManifest.xml の application に android:largeHeap="true" を加える事でアプリケーションで使用するHeapサイズを拡大できるが、このサイズは 256MB 辺りのようだ。
上述のページや他のページ、手持ちの TF101 で調べたところ GalaxyNexus、Motorola Xoom(MZ600)、EeePad transformer TF101 で 256MB ということなので、256MB は使えるのではないかと思われる。
調べた範囲では、最低どの程度なのかという仕様は見あたらなかった。

なお、所有端末やネットで調べた範囲だと、アプリケーションヒープは以下のサイズのようだ ( largeHeap指定がない場合 ) 。
Xperia 24MB
Galaxy TAB 48MB
Xperia arc 32MB
IS03 40MB
EeePad transformer TF101 48MB
Galaxy S2 70MB
MEDIAS N-06C 30MB
NEXUS ONE 24MB
NEXUS S 32MB
ATRIX 4G 32MB
IS01 24MB
Optimus chat (L-04C) 24MB

投稿者 Takenori : 00:45 | トラックバック

2012年08月20日

TLG ファイルビューア

Android 端末でも TLG ファイルが見られるように 絵見る - 画像ファイルビューア を作った。
バージョン 1.1.0 からマルチタッチでの拡大縮小に対応したので、ビューアとしてそれなりに使えるようになったはず。
後、メール添付されたファイルもプレビューボタンから直接開けるようにしたので、TLG ファイルが添付されたメールで見やすいけど、そんなケースはあまりないかな。
拡大縮小はマウスのホイールでも出来るようにしたので、マウスつないでいる Android 端末でも拡大縮小できる。
今のところ TLG ファイル以外は Android 標準でサポートされているファイル形式のみ。

Android 端末で TLG ファイルを見るケースは少ないかもしれないけど、羽々斬でデータフォルダを Android に入れていたら、そのフォルダ内の TLG ファイルを開けるので外出時など少しは便利かも。

投稿者 Takenori : 16:23 | トラックバック

2012年09月22日

動画再生でサーバーエラーが出る場合の対処

端末によって、動画再生を開始すると MEDIA_ERROR_SERVER_DIED ( MediaPlayer error (100, 0) ) が発生して動画が再生出来ないことがある。

FileInputStream fis = new FileInputStream(path);
mMediaPlayer.setDataSource(fis.getFD());

このような形でファイルデスクリプタを指定して再生開始すると端末によっては動画フォーマット次第で発生する。
これを URI 指定にすると対応度が上がるようだ。
つまり、以下のように指定する。

mMediaPlayer.setDataSource( getBaseContext(), Uri.parse("file://" + path) );

ファイルデスクリプタ + offset + length 指定している時はどうしようもないが、そうでないのなら URI での指定にした方が再生出来る率が高いようだ。
これは端末の不具合のような気もするが、こういう振る舞いをする端末がある以上対処するのは仕方ない。

投稿者 Takenori : 16:51 | トラックバック

2014年03月29日

Google Play デベロッパー プログラム ポリシー 新旧差分

Google Play デベロッパー プログラム ポリシー が更新されたようなので、新旧差分をまとめてみる。
旧ポリシーはキャッシュから取得したもの。

コンテンツ ポリシー
露骨な性表現を含むコンテンツ: ヌード、性行為の画像、露骨な性表現を含むコンテンツは禁止します。Google は児童の性的虐待画像に対し、ゼロ トレランス ポリシーを採用しています。児童の性的虐待画像コンテンツの使用が判明した場合、Google は関係機関に報告し、このコンテンツの販売/配布に関与している者の Google アカウントを削除します。 露骨な性表現を含むコンテンツ: ポルノを含むアプリやポルノを助長するアプリは禁止します。これには、性的な表現、または露骨な描写を含むコンテンツ、アイコン、タイトル、説明が含まれます。Google では、児童の性的虐待画像に対し、ゼロ トレランス ポリシーを採用しています。児童の性的虐待画像コンテンツの使用が判明した場合、Google は関係機関に報告し、このコンテンツの販売/配布に関与している者の Google アカウントを削除します。
危険なプロダクト:
ウイルス、ワーム、欠陥のあるアイテム、トロイの木馬、不正なソフトウェアなど、ユーザーの端末、アプリ、個人データにセキュリティの脆弱性をもたらす可能性のあるアイテムや、害を与える可能性のあるアイテムを送信してはなりません。
危険なプロダクト:
ウイルス、ワーム、欠陥のあるアイテム、トロイの木馬、不正なソフトウェアなど、ユーザーの端末、アプリ、個人データにセキュリティの脆弱性をもたらす可能性のあるアイテムや、害を与える可能性のあるアイテムを送信したり、そのようなアイテムへのリンクを設定したりしてはなりません。
システムの妨害:
アプリとその広告は、サードパーティへのサービスとして、または広告の表示を目的に、ホーム画面のショートカット、ブラウザのブックマーク、アイコンをユーザーの端末に追加してはなりません。
システムの妨害:
アプリとその広告は、サードパーティへのサービスとして、または広告の表示を目的に、ブラウザ設定やブックマークを修正または追加したり、ユーザーの端末にホーム画面のショートカットやアイコンを追加したりしてはなりません。
アプリのプロモーション
  Google Play で公開されるアプリは、直接間接を問わず以下の活動に参加したり、以下の活動から利益を得たりしてはなりません。
  • ウェブサイト、アプリなどの所有物での虚偽の広告経由のプロモーション(偽のシステム、サービス、アプリの通知や警告など)
  • ユーザーに通知せずに Google Play へのリダイレクトやアプリのダウンロードを開始するプロモーションまたはインストール手法
  • SMS サービス経由の未承諾プロモーション
広告ネットワークやアフィリエイトがこうした手法を使用して、アプリをダウンロードさせるページにユーザーを誘導することがないよう、デベロッパーが責任を持って管理してください。
有料アプリと無料アプリ
デベロッパーは、自身が販売するアプリについて、またアプリ内サービス、商品、コンテンツ、機能について、ユーザーの誤解を招く説明を行ってはなりません。 デベロッパーは、自身が販売するアプリについて、またアプリ内サービス、商品、コンテンツ、機能について、ユーザーの誤解を招く説明を行ってはなりません。Google Play でのプロダクトの説明で、個別の料金または追加料金が適用されるアプリ内機能について言及した場合、それらの機能にアクセスするには支払いが必要になることをユーザーに明示する必要があります。
広告のポリシー
広告のコンテキスト
アプリのユーザー インターフェースまたはオペレーティング システムからの通知や警告を装った広告は認められません。
広告のコンテキスト
アプリのユーザー インターフェースまたはオペレーティング システムからの通知や警告を装った広告は認められません。それぞれの広告がどのアプリに関連付けられているか、どのアプリに組み込まれているかなどを、ユーザーにわかりやすく明示する必要があります。

投稿者 Takenori : 10:34 | トラックバック

2014年05月26日

C++ Builder XE6 で FireMonkey を使わず NDK の機能で作る

以下、手順。
FireMonkey モバイルアプリケーション を選んでプロジェクト作成。
「空のアプリケーション」を選ぶ。
フォームのソースコードは要らないので、削除してしまう。
プロジェクト名.cpp の方は削除できないので、中身を空にしてしまうか #if 0 で囲んでおく。
中身を消すとリンク時にエントリーポイントがなくてエラーになるので、以下の関数を追加しておく。
extern "C" int _NativeMain( void* param ) { return 0; }

NDK にある sources/android/native_app_glue/android_native_app_glue.c/.h をプロジェクトに加える。
NDK の native-activity サンプルの main.c と同じように void android_main(struct android_app* state) から記述し始める。
後は普通にビルドして実行やデバッガでのステップ実行が出来るので、開発が捗る。
まだ始めたばかりなので問題が出るかもしれないが、起動して画面表示までは問題なくできているので、この形でいけそう。

Android は Java での開発に比べて NDK を使った C++ 開発は不自由過ぎて大変だったが、C++ Builder XE6 のおかげで楽できそうだ。
C++ Builder XE6 は、Android 2.3 以降の arm のみサポートだけど、NDK の機能のみ使うのであれば、最終的に makefile を書いて、NDK のコンパイラでビルドすれば、NDK と同じ制限下で作れる ( x86 などもサポートできる ) 。
makefile も色々とはまりポイントがあるけど、ほぼ最後に書くだけなら我慢できる。

2014/6/28 までの RAD Studio XE6発売記念キャンペーン で今なら、¥116,000 と言う、キャンペーンでその価格かと言う値段だけど、開発効率考えれば ( まだ使い始めなのでわからないが ) 安いだろう。
と言うか、これでやっとまともに開発が出来ると言った感じだ。
今時 makefile 書いて、make して printf デバッグしては辛すぎる……

投稿者 Takenori : 17:05 | トラックバック

2014年06月09日

invoke で呼び出すシンプルな RenderScript

RenderScript は、非同期で実行され、非 static 関数が返り値を持つことは出来ない。
RenderScript 自体は、C言語(C99)で記述可能。

ネット上には実際に使用しているサンプルが極端に少ない。
と言うことで、シンプルな RenderScript を書いてみる。

test.rs

#pragma version(1)
#pragma rs java_package_name(com.your.package.name)
#pragma rs_fp_relaxed
static int result[1];
void add( int a, int b ) {
  result[0] = a + b;
  rsSendToClient(1,result,1);
}

加算して、値を返す関数を定義した RenderScript 。
この test.rs は、以下のように書けそうな気がする。

int value;
void add( int a, int b ) {
  value = value + a + b;
}

でも、うまくいかない。
value に入れた値を取得できない。
もしかしたら何か方法があるのかもしれないが、わからない。

話を戻して、Java の呼び出し側は以下のような形。

mRS = RenderScript.create(this);
mScript = new ScriptC_test(mRS);
mRS.setMessageHandler( new RSMessageHandler() {
  public void run() {
    switch( mID ) {
    case 1: {
      int result = mData[0];
      Log.v( "Result:", "value:"+result);
      break;
    }
    }
  }
});
mScript.invoke_add( 10, 20 );

RenderScript の rsSendToClient でクライアント(Java)側に通知を送れるので、Java 側でそれを受けてログに出力している。
ここまでわかれば、好きな C関数を RenderScript 化して Java から楽に呼び出せる。
ただ、RenderScript は malloc が使えない(Java側で確保して渡す形)ので、malloc が必要な処理は難しい。

うまくいかないパターンの補足
RenderScript.finish で完了を待つっぽいこと書いているけど、これで待ってもうまくいかない。
RenderScript は、非同期実行なので、他の何か完了待ちが必要か、値を Java 側からアクセスできるようにコピーする処理が走るタイミングがありそうだけど、invoke 系ではそれがうまく働かないのかもしれない。
rsForEach 系が必要なのだろうか。
この辺りは rsForEach 系を使う時に調査しようと思う。

投稿者 Takenori : 20:18 | トラックバック

2014年06月10日

RenderScript の invoke は非同期実行

前回のスクリプトを以下のように書き換えてログを見ればわかる。

Log.v( "Script:", "invoke");
mScript.invoke_add( 10, 20 );
Log.v( "Script:", "invoked");

ログには以下のように表示される。

V/Script:(31995): invoke
V/Script:(31995): invoked
V/Result:(31995): value:30

invoke も非同期で実行されているようだ。
では、finish を呼んでみたらどうだろうか?
つまり、以下のようにする。

Log.v( "Script:", "invoke");
mScript.invoke_add( 10, 20 );
mRS.finish();
Log.v( "Script:", "invoked");

V/Script:(32181): invoke
V/Script:(32181): invoked
V/Result:(32181): value:30

一緒だった。
finish は何も待っていない?
ドキュメントの finish を見てみると。
「Wait for any pending asynchronous opeations (such as copies to a RS allocation or RS script executions) to complete. 」と書いている。

RenderScript 側の rsSendToClient が問題か?
rsSendToClientBlocking で RenderScript 側がメッセージを送り終わるのを待つようにしてみる。
つまり、RenderScript 側を以下のようにしてみる。

void add( int a, int b ) {
  result[0] = a + b;
  rsSendToClientBlocking(1,result,1);
}

実行結果は以下の通り。

V/Script:(32590): invoke
V/Result:(32590): value:30
V/Script:(32590): invoked

また rsSendToClientBlocking を呼ぶが、finish を呼ばない場合は、以下のようなログになる。

V/Script:(7023): invoke
V/Script:(7023): invoked
V/Result:(7023): value:30

ちゃんと finish は終了を待っているし、invoke は非同期実行のようだ。
では、RenderScript 側を以下のようにして、mScript.get_value() でグローバル変数を得られるようにして、finish で待つとどうか?

int value;
void add( int a, int b ) {
  value = a + b;
  rsSendToClientBlocking(1);
}

結果は以下の通り。
value は反映されていない。

V/Script:(32736): invoke
V/Script:(32736): invoked
V/Result:(32736): value:0


mScript.set_value で invoke 呼び出し前に設定した値は、きちんと RenderScript 側で読めるが、やはり invoke の呼び出しのみでは、グローバル変数の結果が Java 側から get_xxx で読みだせないようだ。
Allocation には rsAllocationIoSend と言うメソッドがあるので、これで Allocation の形なら Java 側に値を返せそうではある。
Script.get_xxx は、いったいどのように使うのが正解なのだろうか……
rsSendToClient で値を返すしかないんだろうか。

投稿者 Takenori : 03:03 | トラックバック

2014年09月09日

ユニティちゃんアプリを作ると1回は消される?

ユニティちゃん 時報&アラーム を作って、Google Play に登録したけど、公開ボタンを押してから4時間後に削除された。

削除は権利侵害が理由。
削除通知メールには異議がある場合のリンクがあるので、そこからユニティちゃんライセンスのpdfを貼り付けて送った。
すると6時間後くらいにアプリ復活させたから、デベロッパーコンソールから再公開手続きしてねって書いてたから、公開ボタンを押した。
が、33時間経っても公開されない。
24 時間経ってもアプリが表示されない場合は~と言うQ&Aに問い合わせフォームがあるので、そこから再度33時間経っても公開されないと送った。
返信は2営業日以内くらいと書かれている。
問い合わせ送ったのが、確か金曜日だったので土日挟んで今日の火曜日に返事が返ってきて、公開されたと連絡があった ( この間何度か確認したが公開されていなかった。デベロッパーコンソールでは公開中になっている) 。
その後、見たら公開されてた。

結局公開されるまでに1週間もかかった。
Google Play としては、かなり長い時間かかったことに。
機械的に判定して削除しているだろうから最初は仕方ないにしても、復活してもらった後、再度問い合わせないと詰まってるのはなんとも……


普段 Google Play の公開までの時間は2~4時間程度という認識。
この時間は徐々に長くなっている。
最初はかなりスピーディに公開されていたが、色々とチェックが増えているんだろう。

Google Playの反映に思ったより時間がかかる件【4月-第4回】 では、26時間と書かれているので、かなり長くかかる場合もあるのかもしれない。
今回は、何か内部的に詰まってた感じだが。

削除される場合は、公開ボタン押してから4時間後くらいに削除メールが来る印象。
今回で2回目だからいつもそうなのかわからないけど。
アプリ名に他の商品名が入っていると高確率で削除されるんじゃないかと思う。
勝手アプリで○○チェッカーとかいう名前で、○○が商品名やサービス名で削除されるのがやりがちなんじゃないだろうか。
今回はユニティがまずかったのかもしれないし、ユニティちゃんがまずかったのかもしれない。
「ユニティちゃん」と言う名前をアプリ名に入れないとスピーディに進んだかも。
アプリ名に他の商品やサービス名が入らないように注意した方がいい。
意図せず入ってしまう場合がありそうだけど。

投稿者 Takenori : 22:17 | トラックバック

アラームアプリ

時々依頼があるアラームアプリ。
普通に工数見積もって作るとそれなりにかかるので、金額もそれなりになる。
基準となるアプリを作っておいて、そこからの差分で見積もって開発費下げられるかなと言うことで作った。
前にも作ったことあるけど、今回 4.0以降用にして新しいものに合わせた形で作った。

ユニティちゃん 時報&アラーム

アンロックボイス、時報、アラーム機能、時計ウィジェットを持ったアプリ。
時計ウィジェットは時間帯によって絵を変えたかったんだけど、リソースがないので変わらない。
夜は寝てたり、ご飯の時間はご飯食べていたりしたら楽しいと思うんだけども。

時報が思ったよりもいい感じに実用的。
アンロックボイスは、ロック解除時に再生されるボイス。
PC の起動音のような感じかな?
スマフォは起動しっぱなしなので、ロック解除時に鳴らすといいかと思い追加。

目覚ましボイスは、もうちょっと起こしに来るイベントのようなセリフがあると良かったんだけど……


と言うことで、今後アラームアプリ開発依頼があれば差分で見積もって作ります。

投稿者 Takenori : 22:54 | トラックバック

2014年09月14日

アラームアプリに必要なリソース

質問があったので アラームアプリ について、必要なリソースをまとめる。

ウィジェット、アラーム、時報、アンロックボイス の4種類の機能があるので、すべての機能を組み込む場合は、全リソースが必要。
特定の機能を省く場合は、その機能に付随したリソースは不要。
ひな形アプリは、1キャラのみのアプリになっているが、複数キャラクターを入れる場合は、そのキャラクター分の画像とボイスが必要(組み込みについては2~3人日工数増)。

1. アプリアイコン
アイコン画像 : 144x144 ただし、ストア登録時に512x512が必要なので、そのサイズで作っておいた方が良い。

2.ウィジェット
背景画像 : 時間帯変化するのなら、その変化する回数分必要。夜は寝てる画像で、朝は起きた画像にするなど。変化しない場合は1種類。
時間画像 : 0~9の画像、AM、PM、: の13種類の画像。
選択画像 : ウィジェットを実際に配置した時の画像。ウィジェットサイズと同じ大きさ。
※画像のサイズは、ウィジェットをどの程度の大きさにするかに依存。ひな形アプリは2x1マスのサイズ。

3.アラーム画面
背景画像 : 1080x1920 か、小さい画像を並べたり引き延ばす形で表示。
キャラクター画像 : 1080x1920 に収まるサイズで任意。

4. 通知領域
アラーム通知画像 : 通知領域に表示される画像 72x72。標準の時計画像でよければ、標準のものを使用。ひな形アプリは標準の画像を使用。

5.ボイス
時報ボイス : 0~23時の各時間のボイス。時間だけを言うものと、何か一言添える版の2種類。
ロック解除ボイス : 任意の個数。ランダムで再生される。
アラーム音声 : 任意の個数。ユーザー選択。選択時に表示するセリフ文言も必要。

6.他に必要なもの
パッケージに使用するドメイン名
アプリ名
アプリについて…で表示する文言。アプリ説明やHPへのリンクを入れたものなど。
ヘルプ文言。ヘルプ画面で表示する文言。ADV風表示になっている。
ひな形アプリから文言変更する場合は、その分の文言。
色変更などがある場合は、その色。

工数について
ほぼ単純な差し替えの場合は、1人日(確認と差し替えを考慮すると2人日程度) + テスト。
機能追加や変更がある場合は、実工数見積もりが必要。

投稿者 Takenori : 23:41

2015年06月09日

RenderScript で画像の拡大-ニアレストネイバー

RenderScript で forEach する時、入力と出力指定するので、サイズが違う時どうするのかわからなかったが、入力を指定しなければいけるようだ。
つまり、uchar4 function(uint32_t x, uint32_t y) 的な定義をすれば、出力側のサイズ回呼ばれる様子。
単純にニアレストネイバーで2倍拡大する場合なら、以下のような RenderScript のソースコードで出来る。

#pragma version(1)
#pragma rs java_package_name(com.kaede_software.renderscripttest)
#include "rs_core.rsh"
rs_allocation in;
uchar4 __attribute__((kernel)) scalex2(uint32_t x, uint32_t y) {
return rsGetElementAt_uchar4( in, x/2, y/2 );
}

補間が必要なら、rsGetElementAt_uchar4 で複数画素取得すればできそう。
範囲外アクセスしないように min/max で以下のようにクリッピングする。
uint32_t x1 = min((int32_t)x+1, width-1);
uint32_t x2 = max((int32_t)x-1, 0);
width は外部から。

Java 側のソースコードは以下。

BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
Bitmap src = BitmapFactory.decodeResource(getResources(), R.drawable.bitmap, options );
RenderScript rs = RenderScript.create(this);
Allocation input = Allocation.createFromBitmap(rs, src);
int w2 = src.getWidth()*2;
int h2 = src.getHeight()*2;
Type.Builder rgbaType = new Type.Builder(rs, Element.RGBA_8888(rs)).setX(w2).setY(h2);
Allocation out = Allocation.createTyped(rs, rgbaType.create());
ScriptC_scale script = new ScriptC_scale(rs);
script.set_in(input);
script.forEach_scalex2( out );
Bitmap bmpout = Bitmap.createBitmap(w2, h2, Bitmap.Config.ARGB_8888);
out.copyTo(bmpout);

投稿者 Takenori : 16:44

2015年06月16日

RenderScript + Java で waifu2x を実装した

2次元超解像拡大(waifu2x)

作っていて躓いたところなど――

Android で標準の org.json.JSONObject 使って、モデル読もうと思ったら例外で落ちる。
5MB強あるけど、大きい JSON ファイルは処理できないのだろうか。
仕方がないので、JsonPullParser を使って読み込むようにするも 8秒も JSON の読み込みにかかる。
最初に MessagePack 使うことも考えたが、色々と検索すると Android での読み込みは JSON の方が早いとベンチマークがあったので、まずは JSON で読むことにしたのだけれど。
これだとどっちにしても遅いだろうから、独自バイナリ化して読むことにした。
要素数、float配列のような形で決め打ちでバイナリ吐いて読む。
読み込むファイルは3パターン決め打ちなので、汎用性は考えない。
すると読み込み速度は 1秒強~2秒辺りまで早くなり、実用的に。

大きめの画像を拡大しようとしたらアプリが例外もなく突然落ちる。
RenderScript の Allocation のサイズを合計してログに吐いて 1.15GB 確保した次に落ちた。
Allocation で null が返るわけでもなく、例外も出ず突然落ちるので、事前にチェックしてはじくことにした。
確保する前に毎回メモリの空きを確認してもいいけど、事前チェックの方が処理走る前に知れていいだろうと言うことで。
開発に使っている端末は Nexus5 でメモリ容量は 2GB。
スマフォではあまり大きな画像は拡大できそうにない。
メモリ容量が大きくて 64bitの端末だとしても、support library 使ってターゲット API レベルを 18 にすると so は 32bit のバイナリが吐かれているようなので、4GB辺りまでしか使えなさそう。
API レベル 21 にして 64bit 吐くようにすると Java の方が対応していないようでエラーになる。
4GB フルに使えても 1024x1024 の拡大までなので、実際にはこれより少し少ない辺りが現状サイズ限界か。
Android 5.0 以降専用にして 64bit に対応すれば 64bit 端末ではもっと行けるけど、まだそんなにメモリ積んだ端末ないだろうからあまり意味もなさそう。
処理速度を無視するのなら、途中の結果を逐一ファイルに書き出して処理すると言うことは出来るけど、処理時間は絶望的なことになるだろうな。

スマフォだと処理時間はちょっとしたことで大きく変わる。
プログレスバーを出していると、処理時間が倍くらいになる。
本体が熱くなるとクロック落とされるので、熱くなってくると処理時間が遅くなる。
RenderScript で GPU が使われるケースは少ないようで、多くは CPU での処理になるっぽい。
色々と制限付きで記述すると GPU で動くこともあるようだけど、全てそのように書くのは難しい。
OpenGL ES 3.0 使ってシェーダーで走らせれば GPU での処理になるだろうけど……

RenderScript での実装自体は楽。
RenderScript が分かれば、waifu2x は極めて適した処理なので楽に書けた。

後は上がってきた不具合等をじわじわ修正していく。

投稿者 Takenori : 16:42

2015年06月18日

Android で waifu2x 分割処理などに対応

2次元超解像拡大(waifu2x)

メモリ制限から 512x512 辺りが拡大の限度となっていたが、メモリに入りきりそうにない場合は分割すると良いと言うアドバイスを受け、分割して境界をオーバーラップさせる形で処理するようにした。
これにより読み込んだ画像と拡大(orノイズ除去)後の画像サイズ4枚分、分割処理用のメモリ4MB以上があれば処理できるように。
これでかなり大きな画像まで処理できるようになったはず。
ただ、処理時間が大変なことになるけど。
分割サイズは、512、256、128、64 の内メモリに乗る最大サイズで行う形。

アプリ切り替えなどで処理が終了してしまっていたのを、プログレスダイアログのキャンセルボタンが押されるまで継続するようにした。
処理中は端末の発熱に注意する必要がある。

アルファチャンネルも引き継ぐようにした。
ただ、拡大の場合は境界部分はあまり綺麗ではない。
処理前に境界色を透明部分へ数ピクセル引き延ばす処理をすればそこそこ綺麗になると思うが、アルファはとりあえず入れておいた感じ。

プログレスダイアログの進捗更新を1%以上変化がある場合のみ呼び出すように修正した。
プログレスダイアログの更新処理には意外と CPU パワー持って行かれていて処理速度が半分以下になったりするようなので、更新頻度を少なくするのはある程度効果ある。
後、広告表示にも CPU パワーを持って行かれている感じだったので、バナー広告からインタースティシャル広告をたまに出す形に変更した。


他に互換性の問題などを修正した。
4.0 で ProgressDialog の show 後に setMax を呼ぶと落ちていたので修正。
例外吐いて落ちるのでわかりやすい互換性問題。

4.0 や 4.1 の場合 ScriptIntrinsicConvolve3x3 で Element の型が float(F32) だと RSIllegalArgumentException 例外で落ちる。
互換性の観点から support library の使用が推奨されているけど、support library 使っていても落ちる。
support library ではなく、組み込みの機能が使用されるのだろうか?
android.support.v8.renderscript.ScriptIntrinsic 系のクラスは同じような互換性の問題があるかもしれない。
同じ処理を RenderScript で書き直したものを使うと互換性の問題は回避できる。
事前に自前実装のものと速度比較して、ScriptIntrinsicConvolve3x3 の方が少し速かったので使っていたが、RSIllegalArgumentException 例外が出る環境では、自前実装のものを使用する形にした。

投稿者 Takenori : 01:37

2016年10月13日

Android Studio 2.2 + cmake で上位ディレクトリを C++ の基準とする

デフォルトでは app/CMakeLists.txt と配置され、ソースコードは app/src/main/cpp に置く形になる。
C++ のソースコードが Android 固有であればいいが、マルチプラットフォームなどの場合はあまり好ましくない。
Android Studio のプロジェクトルートを android などのフォルダに置き、その上位ディレクトリがルートとなるような構成の方が見通しが良い。

app/build.gradle で CMakeLists.txt の位置を指定してやれば、上位ディレクトリでも問題なくビルドできる。
例えば以下のような形。

android {
  externalNativeBuild {
    cmake {
      path "../../CMakeLists.txt"
    }
  }
}

C++ のソースコードなども問題なく Android Studio のツリーに表示される。
cmake のキャッシュを消さないとうまくいかないこともあるかもしれないので、build.gradle 修正後 app/.externalNativeBuild を消した方がいいかもしれない。

投稿者 Takenori : 23:15

2016年11月30日

Android のフォントを C++ から利用する

Java から使用するのは Typeface クラスを用いればすぐできるが、C++ (NDK) から利用しようとするとあまり情報がないので、調べた内容をある程度まとめておく。

フォントファイルは /system/fonts/ ディレクトリに入っている。
使用するのは大体 TrueType/OpenType だろうから、このフォルダ内の拡張子が .ttf/.ttc/.otf のファイルを列挙すればよい。
.ttf TrueType
.ttc TrueTypeフォントを複数まとめたもの
.otf OpenType
といった感じ。
列挙は opendir で開いて DT_REG のファイルの拡張子で比較すれば済む。

日本語フォントはバージョンによって異なり――
Android 3.x以前 DroidSansJapanese.ttf(Droid Sans Japanese)
Android 4.0~5.x MTLmr3m.ttf(MotoyaLMaru), MTLc3m.ttf(MotoyaLCedar)
Android 6.0以降 NotoSansJP-Regular.otf(Noto Sans JP)
となる。括弧内は FreeType で開いた時に得られるファミリー名。
もちろん今後変更になる可能性は十分ある。

上記フォントファイルの内、端末内に存在するフォントファイルを使ってもいいが、Java で利用した時に使われるフォントと同じものを使おうとすると /system/etc/fonts.xml や /system/etc/system_fonts.xml を読み込む必要がある。
また、日本語フォントの場合、バージョンによっては /system/etc/fallback_fonts-ja.xml や /system/etc/fallback_fonts.xml などのファイルも読み込む必要がある。
fonts.xml のフォーマットはバージョンによって違う。
最新のものはかなりわかりやすい形になっている。

このファイルを読み込むソースコードは Java なら StackOverFlow の How to change fontFamily of TextView in Android の中ほどにある FontListParser を使えばできる
ここの FontListParser は、元々 Android のソースコードと同名のクラスを改造したもののようだ。
Android の Typeface クラスが FontListParser クラスを使用して上記 XML ファイルを読み込んでいるので、これを使えば使用されるフォントがわかる。
もちろんバージョンにって切り替える必要があるし、将来変更される可能性もある。

以上が調べたフォントに関する情報だが、バージョンによって違ったり将来的に変更の懸念があるので、使用する文字に削ったフォントフォイルを自前で持っておくのが安心。
Note からはそうそう変わらないんじゃないかと思うが、あっさり変えてくることも十分考えられる。

投稿者 Takenori : 22:45

 
Total : Today : Yesterday :