2011年7月24日日曜日

Cameraの機種依存 (1)

自作Androidアプリから、Camera撮影をする場合は、2つの方法があります。
1.Intentで端末のカメラアプリを呼び出す
2.SurfaceViewを使って、カメラスルー画面を直接呼び出す。
どちらもWebをちょっと調べると、サンプルコードが出てきます。

が。

Camera機能は、機種依存動作が強すぎるようで、結構はまりどころです。

やろうとしていたのは、カメラで写真をとって、FaceDetectorで顔認識をするアプリ。
まず、Intentでカメラアプリを呼び出す方法。

カメラにIntentを投げて、onActivityResult()で、結果を受け取る、というのが大体の流れです。
この、結果を受けとる、ところがクセモノで、機種によって受け取れ方が違います。

1.intent.getData()でUriを取得する。HTC Desire/Xperia acroなんかはこれっぽい。
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
    if (resultCode == RESULT_CANCELED)
    {
        return;
    }

    Uri uri = data.getData();
    getImage(uri);
}

2. 事前に、IntentのputExtraで書き出し先のUriを指定して、Intentを発行する。SHARP製のAndroidはこの動作。
public void launchCamera()
{
  // Create your own image Uri
  ...
  Intent intent = new Intent();
  intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
  intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);
  startActivityForResult(intent, 2);
}

protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
  if (resultCode == RESULT_CANCELED)
  {
    if (mImageUri == null)
    //delete file at mImageUri
    
     return;
  }
  getImage(mImageUri);
}

このmImageUriに、画像が書きだされてますので、onActivityResult()が呼ばれれば、mImageUriからそのまま取得できます。

3. 1も2もしてくれない・・・Galaxy Sがこのパターン
えっと・・・これが一番困りました。
intent.getData()はnullだし、2をやると、違うUriに書きだしてくるし、、、

結局、画像の中で最新の画像を取得するコードを書いています。
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
  if (resultCode == RESULT_CANCELED)
  {
    return;
  }

  Uri uri = data.getData();
  if (uri == null)
  {
    ContentResolver cr = getContentResolver();
    Cursor c = MediaStore.Images.Media.query(cr,
    MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
      null,null,
      MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC");

    c.moveToFirst();
    String id =c.getString(c.getColumnIndexOrThrow(BaseColumns._ID);

    Uri uri = Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + id);

    getImage(uri);
  } else {
    getImage(uri);
  }
}

もちろん、このコードは、最新の時刻の画像=今とった画像、ではないパターンで問題が出ます。
撮影直後に別のアプリが画像書き出してた、とか。タイムスタンプが未来の画像が入っちゃってる、とか。
エラーチェックを厳しくすればいいのでしょうが、、、

一応、1,2,3ともに共存可能なので、3は最終手段として使う予定です。

なお、Galaxyの場合、以下のような動作もあり。
・撮影が終わってもカメラアプリが終了しないという問題(仕様?)
・カメラ呼び出し元のActivityが終了させられてしまう

前者は、作ってるアプリの仕様上、撮影後はすぐに別画面に遷移させる必要があるので、
別画面を起動するIntentを投げてカメラを終了(中断)させています。

後者が厄介で、Activity::onDestroy()が呼ばれた上、GCまでされちゃいます。
カメラから呼び出し元Activityに戻ると、onCreate()からやり直しです。
つまり変数が全て初期化されます。スッゲ泣ける。

実は、SurfaceViewを使う方法でも、Galaxy Sは大変なんですが、、、それは次回にします。



Intentional Disciplemaking: Cultivating Spiritual Maturity in the Local Church
楽天ブックス
INTENTIONAL DISCIPLEMAKING Ron Bennett NAV PR2001

楽天市場 by Intentional Disciplemaking: Cultivating Spiritual Maturity in the Local Church の詳しい情報を見る / ウェブリブログ商品ポータル

2011年7月4日月曜日

モノづくりと、ソフトウェアと、インターネット

いまさら、かもしれないけど。
実は、ハードとしての「モノづくり」という価値は、とっくに破壊されているのではないか。
モノづくり、モノづくり、と、声高に重要視されるけど。
その「モノづくり」と、「ソフトウェア」とは、わけて考えなきゃいけないでしょう、と。

そのハードとしてのモノづくり、その本質は?というと、

「同じものを、大量に量産して、いろんな箇所にばらまける」能力が、その本質であると思う。アイディア、品質含め。

例えば、何か、こういうハードあれば便利かも?と思って、
アキバで基板とパーツ買ってきて、できた!となる。なったとしよう。
ここまでは、それなりの知識があれば、出来るでしょう。

じゃあ、それを、量産できるか、同じものをばらまけるか?というと・・・
・・・えっと、設計書つくって、パーツを、必要なだけ発注して、
それを組み立てる工程は、工場は・・・。
儲けが出るためには、ウン万台売らなきゃいけないから、ウン万台つくって,うんたらかんたら、、、

気が遠くなります。この時点で、素人には、到底無理だなと。リスクもでかい。
パーツ発注して売れなければ、大赤字。

だから、メーカーというのは、重要な位置にいて、資本を持った、その企業にしか出来ない、となっていたわけです。

じゃあ、ソフトウェアの話ならば?

「大量に量産」は、コピー一つで済んじゃいます。もちろん、タダ。微々たる電気代だけ。
「ばらまく」も、同じこと。ネットがあれば、サーバにアップロードすれば、誰でも取っていけます、イコールばらまけます。
「大量に・・・」も。アクセス過負荷になる量が集中しても。スケーラブルなサーバがあれば、
いまどき簡単に(ちょっとのお金を払えば)、容量はすぐ増やせちゃいます。
最初から、負荷を考える必要がない。足りなければ、すぐ追加できる。
��これがクラウドを支える技術だったり。

ソフトウェアの世界を、「モノづくり」の考え方のまま、考えてしまっている人が多いかなと。
ソフトウェアと、インターネットは、「モノづくり」の本質を壊しちゃってるわけです。
「モノづくり」の難しかったところが、最初からないわけです。

だれでも、いつでも、「オレがすごいと思ったもの」を「世界に量産してばらまける」わけです。
だから、「ハードとしてのモノづくり」と、「ソフトウェアのモノづくり」を同じように考えてはいけない。

そうなると、ソフトの世界では、「アイディア」が重要になる・・・わけではなく。
「アイディア」を、「誰よりも早く」公開できることが、重要になるのかなと。
このあたりは、いろんな人が言っているので、これ以上書かないですが。
アイディアとスピードの勝負なら、「下手な鉄砲も数撃ちゃ当たる」方式を、考えるべきなのかな。
考え尽くしてる前に、作って、公開しちゃえ~、みたいな。

これも、いままでの「モノづくり」では出来ないやり方なんだったりしますが。
これはそのうち、気が向いたらつぶやこう。

2011年7月2日土曜日

アプリ終了:Homeキー押しと、Backキー押し

小ネタ。
��大ネタ書いたことないけど

普通にAndroidアプリ使ってると、アプリを終了したい時に、以下どっちかをします。
・Homeキーを押して待受へ。
・Backキーを押して、アプリ起動前画面へ。

使ってる限りは、どっちも終了しているように思えますが。

実は、両者は明確に動作が違います。

・Homeキー押しは、「中断」です。
・Backキー押しは、「終了」です。

Homeキーを押したときは、アプリが終了するわけではなくて、
Homeアプリ(待受)が、「割り込みで起動している」という動作になります。

これは、知っておいたほうがいいです。

実際、適当なアプリ作って、onDestroy()にブレーク貼っておくと、
前者はonDestroy()が呼ばれず、後者は呼ばれます。

例えば、アプリ内から起動するサービスが、アプリ終了後も操作して欲しい場合(*1)、
Homeキー押しだと、Activityが中断、つまり、存在したままになるので、
Activity→Serviceの参照が残るので、Serviceはそのまま存在し続けます。
これがBackキー押しの場合、Activityは消えますので、Activity->Serviceの参照関係が消えるため、
ServiceまでGarbase Correctionされることがあります。

永続的に起動しときたいServiceは、Intent経由で起動しておくと、ちゃんと永続化してくれますが、
デバッグ時は、ちょっと注意しておいたほうがいいですね。

(*1) ここに書いたけど、
実は、こういう時以外、Serviceって使うべきじゃないんだけどね

Contextについて

ActivityのContext。よく使うと思います。
Intent経由で、他のActivityを起動するときなど、Intentに、Contextを指定するときは、大体以下のコードを書きます。

Intent(this, nextActivity.class)

これは、ActivityがContextのサブクラスであるためです。

でもって、モジュール的に、ActivityのサブクラスではないClassが、(Intent起動など)Contextを使う場合、
Activityから渡してやります。

でも、これが、どうやら、クセモノらしい。

そもそも、Contextとしてthisを指定するということは、Contextのライフサイクルが
Activityのライフサイクルと一致していないといけないわけです。

一致していない場合、なんらかのExceptionが発生する。
・・・のなら、いいのですが(デバッグしてればわかるから)、実は普通にメモリリークするらしい。

つまり、thisを指定して使われているContextが、Activity終了しても、他のモジュールから
参照されちゃってる場合、
「そのActivityが、ガベージコレクションされない」状態になっちゃうみたい。
��サンプルコード載せたいけど、試したコードがどっかいっちゃった・・・

なので、Activityのライフサイクル≠Context(を使用しているClass)のライフサイクルの場合、
Activity:getApplicationContext()を使ったほうがいいみたい。
こちらは、自分のつくっているアプリケーションが、Activityとは別に持っているContextを取得します。
(別じゃないパターンもあるみたいだけど、これは問題にならない)
アプリケーションが生きてる場合は、Contextを使用しても問題ない、ってことです。

実は、この問題は、こっそりここに書いてあります

http://developer.android.com/reference/android/content/Context.html#getApplicationContext()

うーん、このメソッドを見る人は、ここで書いたこと、わかってると思うんですけどね、、、(w