Development


[android] Camera Previewが1.6だと動くのに2.1だとエラーになる 2

カメラを使うアプリをAndroid 1.6で動確してリリースしたら、「動かねー」とかいわれて評価が悲惨なことになった (^o^)/

Android 1.6と2.1で、Camera.Parameters.setPreviewSize()を使用した場合の動きが変わってるみたい。
1.6だとサポートしていないサイズを指定しても動くけど、2.1だとCamera.setParameters()実行時にRuntimeExceptionが発生する。
公式サンプルのやり方だと、端末によっては落ちるので注意しましょ。

2.0以降をターゲットにしているなら、getSupportedPreviewSizes()で使えるサイズを取るのが無難ですね。
1.6ならリフレクションでgetSupportedPreviewSizes()を使っても良いんだけど、サイズ指定無しでも問題なさそうです。


[android] 別プロジェクトでAIDLをつかってみる

別プロジェクトでのAIDLの使い方についてのコメントあったので、サンプルを公開してみる。
やることはプロジェクトが同じでも別でも変わりません。

プログラム自体は以前書いたのと同じなので省略。

サービス提供側のAIDLファイルを、サービス利用側にコピーして使ってます。
本当は生ファイルを渡すのではなく、JARとかにした方がいい気がします。

InterAppsAIDL.zip


[android] メモリをたくさん使いたければダイレクトバッファを使えば良い

写真を加工するアプリを作ってたらメモリ不足が多発した (^o^)/

Androidではアプリが使えるヒープ領域が少ないため、気をつけないとすぐにメモリ不足になる。
ヒープが少ないならヒープ外を使えばいいんじゃね、ってことでダイレクトバッファを使えばメモリをたくさん使えます。

package jp.xfutures.android.sample;

import java.nio.ByteBuffer;

import android.app.Activity;
import android.os.Bundle;

public class MemoryLimitCheckActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // これはメモリ不足で落ちる
        ByteBuffer buf1 = ByteBuffer.allocate(20 * 1024 * 1024);

        // こっちなら大丈夫
        ByteBuffer buf2 = ByteBuffer.allocateDirect(20 * 1024 * 1024);
    }
}

たくさん使えるからといって使いすぎには注意です。


[android] AIDLによるプロセス間通信 – Callback編 9

前回に続いてAIDLのお話です。今回はCallbackしてみます。

流れとしては、

  1. ActivityでServiceをBind
  2. ServiceのCallback登録Interfaceをコール
  3. Serviceに実装したCallback登録Interfaceで、受け取ったCallback情報をリストに登録
  4. Callbackリストを参照して登録されているCallback Interfaceをコール
  5. Activityに実装したCallback Interfaceでなんかする

みたいな感じです。

図にするとこんな感じ。

2009072001
(さらに…)


[android] AIDLによるプロセス間通信 8

タイトルにあるとおり、Activity – Service間の通信のお話です。

andoridでは、簡単にActivityからServiceのメソッドを実行することができます。

  1. AIDLファイルにIPCのインターフェイスを記述する
  2. Serviceにインターフェイスを実装する
  3. ActivityからServiceにBindし、インターフェイスを叩く

たったこれだけです。

サンプルとして、テキストボックスに入力された値をServiceで加算するプログラムを作ってみます。
こんな感じ。
2009071812

(さらに…)


[android] Serviceプロセスの永続性は保証されていない?

androidはユーザがアプリプロセスの終了を意識しない作りとなっています。
戻るボタンやホームボタンを押すとホーム画面に戻り、不要となったアプリプロセスはOSがテキトーなタイミングで殺します。
OSに殺されたくないときはServiceを作ってあげれば良いのですが、OSが勝手にServiceプロセスを再起動することがあるっぽいです。

Serviceでタイマーを仕掛けて周期的に処理を行っていたのですが、この周期的な処理が途切れてしまう現象が発生しました。
DDMSでプロセスをチェックしてみると、なんとタイマースレッドが死んでました ?(^o^)/

onCreate(), onStart(), onDestroy()にログを仕掛けて放置してみると、何も操作していないのにonCreate()がコールされてる・・・。
しかも、元のServiceプロセスのonDestroy()や新しいServiceプロセスのonStart()はなしです。
android:process属性を設定して別プロセスにしてみましたが、あえなくお亡くなりに。

まとめると以下のような感じ。

  1. ServiceプロセスはOSが再起動することがある。
  2. Serviceプロセスの再起動時には、新しいプロセスのonCreate()のみが呼ばれる。
  3. Serviceプロセスを別プロセスとしても再起動が発生する。

ちなみに、音楽プレイヤーとかのガリガリ動く物は再起動されることはないと思います。
長時間アイドル状態となっているから殺されるのでしょう。


[android] AppWidget onClick時にIDを渡す

前回はViewのonClickリスナにPendingIntentを仕掛けることによって、AppWidgetのクリックイベントを拾うことができました。

複数のWidgetを配置している場合には、どのWidgetがクリックされたかを判断する必要があります。
Intent.setData()を使用してActivityにAppWidgetIDを渡してあげればOKです。

public class SampleWidgetProvider extends AppWidgetProvider {
  public static final Uri CONTENT_URI = Uri.parse("content://jp.xfutures.android.sample");

  public void onUpdate(
      Context context,
      AppWidgetManager appWidgetManager,
      int[] appWidgetIds
  ){
    for(int id : appWidgetIds){
      // 起動するActivityのIntentを作成する
      Intent intent = new Intent(context, SampleWidgetActivity.class);

      // AppWidget IDを設定する
      Uri uri = ContentUris.withAppendedId(CONTENT_URI, id);
      intent.setData(uri);

      // PendingIntentを取得する
      PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);

      // onClickリスナを設定する
      RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.main);
      views.setOnClickPendingIntent(R.id.TextView01, pendingIntent);

      // AppWidgetを更新する
      appWidgetManager.updateAppWidget(id, views);
    }
  }
}

受けるとる時はIntent.getData()ですね。

public class SampleWidgetActivity extends Activity {
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.onclick);

    // AppWidget IDを取得する
    Uri uri = getIntent().getData();
    int id =  (int)ContentUris.parseId(uri);

    // AppWidget IDをTextViewに設定する
    TextView view = (TextView)findViewById(R.id.TextViewID);
    view.setText(String.format("WidgetID is %d.", id));
  }
}

AndroidSample2009062101.zip