Android 機種互換性問題対応顛末記

はじめに

ちょっと前に、スティーブ・ジョブスが、Android と iPhone の関係を、Google が言うように、オープン 対 クローズド なのではなく、分断 対 統合 なんだと言っていた。

ジョブズ、Google Androidとの競争を語る:オープン対クローズドではなく分断 vs 統合

分断と統合とは、Android は複数OSバージョン × 各種端末 と環境が「分断」されているのに対し、iPhone は、現バージョンと一つ前のバージョンのOS × Apple 1社 に「統合」されていると。

要するに Android はバラバラで統一されていなくて開発しにくいし不便なんだよと。

まぁジョブスが、例としてあげた、TweetDeck の開発チームがそんなことないと反論してたりもするんですけど。

ジョブズに援用されたアプリ開発者、「Android分断化の悪夢」を否定

自分も、どちらかと言えば、オープン 対 クローズド 、もっと具体的に言えば、開発の敷居 低い 対 高いという意味で、Android 派。

開発の敷居が高いとは、言語が、Objective C 云々ということでは無くて、開発に Mac が必要だったり(買えない)、アプリ公開に年間1万円もかかったり、公開に審査があったりという意味で。

Android は、PC (Windows、Linux) OK、アプリ公開初回のみ 2,500円、公開に審査無しと非常に「ゆるい」

ではあるのだけれど、、、

自作の手書きメモアプリを最近電器屋さんに増えてきた Android 端末のホットモックで、Android マーケットからインストールして試してみた(よい子はそんなことしちゃいけません)ら、まぁうごかないうごかない。

 

まだ、練習段階的なアプリなので、そんな特殊なことをしているつもりは無いのだが・・・うーむ。

 

とりあえず、どんな風に動かないかというと、

機種 動かない具合
HTC Desire HD カメラで強制終了
SHARP GALAPAGOS そもそもメモがとれない
Dell Streak 線の太さが異常に細い
SHARP IS03 そもそもメモがとれない
REGZA Phone T-01C 線の太さが変わらない

 

開発時には、HTC Desire(X06HT)で動作確認しているのだが、その他機種では、こんな具合に全滅でびっくりしました。

個人で実機をこんなにそろえる訳にもいかないし・・・メジャーな機種ぐらいサポートしたいし・・・ 悩ましい。

日本アンドロイドの会のメーリングリストでも、『Android Market アプリに「対応機種指定」を』 とトピックに上がっているが、何らかのシステム的な対応があるとよいなぁと思う。

 

で、そんな中、何とか、電器屋で発生したエラーログを採取したり、友人にIS03実機デバッグさせてもらったりと対応したなかで、気づいた点をメモ。

 

カメラで強制終了について

カメラのプレビューでもはまったが、機種によっては写真自体の縮小がうまく機能していないようだ。

そのあたりは、調整しながらデバッグする必要があるので、電器屋でデバッグするには無理がある。現時点では目をつぶる。

強制終了が発生したタイミングで情報を送信すると、Android Market のデベロッパーコンソール でスタックトレースが確認できる様になる。

android_market01

オートフォーカス失敗

カメラプレビューで、画面をタップするとオートフォーカスするように実装していたのだが、オートフォーカス対応でない機種の場合、RuntimeException が発生するようだ(当たり前か・・・)

  1. java.lang.RuntimeException: autoFocus failed

  2. at android.hardware.Camera.native_autoFocus(Native Method)

  3. at android.hardware.Camera.autoFocus(Camera.java:440)

  4. at info.typea.shujiroid.core.CameraActivity$CameraControlView.onTouchEvent(CameraActivity.java:214)

  5. at android.view.View.dispatchTouchEvent(View.java:3778)

  6. at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:958)

  7. at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:958)

  8. at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1716)

  9. at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1124)

  10. at android.app.Activity.dispatchTouchEvent(Activity.java:2125)

  11. at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1700)

  12. at android.view.ViewRoot.handleMessage(ViewRoot.java:1802)

  13. at android.os.Handler.dispatchMessage(Handler.java:99)

  14. at android.os.Looper.loop(Looper.java:143)

  15. at android.app.ActivityThread.main(ActivityThread.java:5068)

  16. at java.lang.reflect.Method.invokeNative(Native Method)

  17. at java.lang.reflect.Method.invoke(Method.java:521)

  18. at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)

  19. at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)

  20. at dalvik.system.NativeStart.main(Native Method)

これは、camera.autoFocus(this); を try ~ catch で囲む対応。例外発生時に、Toast を表示しておくようにした。

パラメーター設定失敗

  1. java.lang.RuntimeException: setParameters failed
  2. at android.hardware.Camera.native_setParameters(Native Method)
  3. at android.hardware.Camera.setParameters(Camera.java:657)
  4. at info.typea.shujiroid.core.CameraActivity$CameraControlView.surfaceChanged(CameraActivity.java:264)
  5. at android.view.SurfaceView.updateWindow(SurfaceView.java:538)
  6. at android.view.SurfaceView.dispatchDraw(SurfaceView.java:339)
  7. at android.view.ViewGroup.drawChild(ViewGroup.java:1638)
  8. at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1367)
  9. at android.view.ViewGroup.drawChild(ViewGroup.java:1638)
  10. at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1367)
  11. at android.view.ViewGroup.drawChild(ViewGroup.java:1638)
  12. at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1367)
  13. at android.view.View.draw(View.java:6743)
  14. at android.widget.FrameLayout.draw(FrameLayout.java:352)
  15. at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:1906)
  16. at android.view.ViewRoot.draw(ViewRoot.java:1411)
  17. at android.view.ViewRoot.performTraversals(ViewRoot.java:1167)
  18. at android.view.ViewRoot.handleMessage(ViewRoot.java:1731)
  19. at android.os.Handler.dispatchMessage(Handler.java:99)
  20. at android.os.Looper.loop(Looper.java:123)
  21. at android.app.ActivityThread.main(ActivityThread.java:4627)
  22. at java.lang.reflect.Method.invokeNative(Native Method)
  23. at java.lang.reflect.Method.invoke(Method.java:521)
  24. at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:876)
  25. at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:634)
  26. at dalvik.system.NativeStart.main(Native Method)

カメラの setParameters() で例外が発生している。不正なパラメータを設定したようだ。

もとのソースは、

  1. Camera.Parameters p = camera.getParameters();
  2. p.setRotation(90);
  3. p.setFlashMode(currentFlashMode);
  4. camera.setParameters(p);

のように、カメラから、Parameters を取得し、変更した後、再度セットし直していたのだが、これだと同時に複数の値を setParameters() するため、一つでも不正な値があると、すべての設定失敗してしまう。

もう少しましな書き方(事前チェックのような)が出来ないものかと思いつつ、以下の様に、一つずつ設定し、失敗したら Toast を表示しながら、パラメータをセットしていく方式に修正した。例外をキャッチしたブロックで、再度getParameters() を呼ぶと、不正な値がセットされていないパラメータが取得出来るため、失敗した値以外はきちんと設定される。

  1. Camera.Parameters p = camera.getParameters();
  2.  
  3. try {
  4. p.setRotation(90);
  5. camera.setParameters(p);
  6. } catch (Exception e) {
  7. Context context = getApplicationContext();
  8. (Toast.makeText(context,
  9. context.getString(R.string.msg_err_rotate_fail),
  10. Toast.LENGTH_SHORT)).show();
  11.  
  12. p = camera.getParameters();
  13. }
  14.  
  15. try {
  16. p.setFlashMode(currentFlashMode);
  17. camera.setParameters(p);
  18. } catch (Exception e) {
  19. Context context = getApplicationContext();
  20. (Toast.makeText(context,
  21. context.getString(R.string.msg_err_flash_fail),
  22. Toast.LENGTH_SHORT)).show();
  23.  
  24. p = camera.getParameters();
  25. }

IS03 で実機デバッグ

SHARP の機種は、そもそもメモ自体がとれなくて、原因はわからないわ、Android マーケットのコメントで、「IS03 で動きません」と、最低評価されるわ、だったので、友人に実機でバッグさせてもらった。

MotionEvent.getHistorySize() が 0 ?

ACTION_MOVE イベントが発生した場合、イベントが発生する間隔よりも、座標を取得指定間隔の方が短いため、前回のイベントから、今回のイベントまでの間の軌跡が1つ以上格納されている。

線をなめらかに表示するために、ACTION_MOVEイベントが起きた時点の座標だけでなく、その軌跡を利用していたのだが、どうも IS03 (SHARPの機種全般?) では、この値が常に 0 となってしまうように見受けられた。

  1. public boolean onTouchEvent(MotionEvent event) {
  2. switch(event.getAction()) {
  3. :
  4. case MotionEvent.ACTION_MOVE:
  5. int n = event.getHistorySize();
  6. for (int i=0; i < n; i++) {
  7. line.add(event.getHistoricalX(i),
  8. event.getHistoricalY(i),
  9. event.getHistoricalPressure(i),
  10. event.getHistoricalSize(i));
  11. }
  12. :

なので、最初、上記の様に、履歴を線で結ぶコードだったのだが、履歴が存在しないため、」画面を指でなぞっても、line オブジェクトに座標が設定されていなかった。

  1. public boolean onTouchEvent(MotionEvent event) {
  2. switch(event.getAction()) {
  3. :
  4. case MotionEvent.ACTION_MOVE:
  5. int n = event.getHistorySize();
  6. if (n == 0) {
  7. line.add(event.getX(),
  8. event.getY(),
  9. event.getPressure(),
  10. 1);
  11. } else {
  12. for (int i=0; i < n; i++) {
  13. if (this.isPressureEmurate) {
  14. pressureEmu += emudiff;
  15. }
  16. line.add(event.getHistoricalX(i),
  17. event.getHistoricalY(i),
  18. event.getHistoricalPressure(i),
  19. event.getHistoricalSize(i));
  20. }
  21. }
  22. :

よって、getHistorySize() が得られない(履歴が無い)場合、直接座標情報を設定するように修正。

MotionEvent.getPressure() が 固定値?

この値により、線の太さが変わるように実装してあるのだが、画面をどう押しても、線の太さが変わらない(REGZA Phone も同様な挙動)。IS03でログに値を出力してみたところ、0.39 ・・・ とどうやら固定値が取得されるようだ。対応していないのかしら?

これに関しては、アプリの設定を追加し、疑似筆圧として、ACTION_MOVE中、MotionEvent.getPressure() の戻り値から値を徐々に減じていくという逃げを打つ対応。

Dell Streak の線の太さ対応

最後に、Dell Streak この機種は、線の太い細いは感知している(MotionEvent.getPressure() の値は固定値ではなさそう)のだが、やたら線が細くなってしまう。SDKのリファレンスを見ると、MotionEvent.getPressure()  は、 0 から 1 の間の値を返すとのことなのだが、取る値の幅が小さい(?)のだろう。

これもアプリに、感度設定を追加し、MotionEvent.getPressure()  で取得した値を増幅することを可能にして逃げた。

まとめ

とりあえず、以上で、カメラ撮影した画像の縮小、画面にフィットさせる機能がおかしい件には目をつぶりつつ、他の不具合には一応対応したつもり。。。

Android アプリ というかモバイル端末用の アプリだと、センサーとかカメラとかの使うところが面白いと思うし、醍醐味だと思うのだが、結構互換性がない(稚拙なプログラミングというか経験不足も多々あると思うが)なぁという印象と懸念を覚えた。

まぁ、SDK のリファレンスに、例えば、0 ~ 1 の間の値を返すとしか書いていないので、固定値 0.3 を返そうが、筆圧によって、値が変わろうが、仕様の範囲ではあるのだろうけれど。

このあたりの互換性のなさが、Android 普及の足枷にならないことを願うとともに、なにかしかの解決策があるとよいなぁと感じました。以上。

Follow me!

Android 機種互換性問題対応顛末記” に対して1件のコメントがあります。

  1. jesusisinus より:

    確かに、iPhone の敷居の高さは感じます(苦笑)
    Apple Store を経由しないと、自作ソフトを自分の実機にすらインストール出来ないのも考えてみるとおかしな話です。

jesusisinus へ返信する コメントをキャンセル

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です