RSS

カテゴリー別アーカイブ: Android

[Android] Contextの持ち方 (2)

 前回の日記で「ContextはApplicationを渡すのが基本(ドヤァ」なんてことを書いたが、それはちょっと言い過ぎで、やはり状況に応じたContextインスタンスを持つべきではある。
 つまり、Applicationじゃ困ることもあるということだ。

 例えば、三大Contextインスタンス(Activity、Service、Application…の3つと言えるだろう)の中でDialogを起動できるのはActivityだけである。DialogはWindowManagerやらを呼ぶためにコンストラクタでContextを要求する仕様になっているが、そこにServiceインスタンスを代入しても例外吐いて落ちるし、Applicationでもダメである。
 正直この仕様は納得がいかない。Dialogを起動出来るのがActivityならば最初から引数型をActivityクラスにするべきではないのか? まぁ、Dialogで利用するのはContextのメソッドであってActivity特有のメソッドは使わない、というなら確かにContextでいいのかもしれないが、だとすればなぜ他のContextで落ちるのか(Contextを要求してるのにContextを継承したクラスのオブジェクトを渡して落ちるとか、コンパイラさんの立場が無いのではないか。型安全はどこへいった。)。ちょっとよく調べてないのでわからないが、たとえActivityの派生型以外から起動出来る仕組みにしているのだとしても、DialogOwnerInterfaceでも定義して、そいつを実装したクラスのインスタンスを渡させるべきではないのか。
 まぁ私はプログラムの勉強を初めて日も浅いので、なにか見落としてる都合があるのかもしれないが…、実際紛らわしい仕様であることは確かだ。

 また他にも、こちらのサイトで挙げられているようにBroadcastReceiverなどをregistするときにApplicationを渡してしまうとメモリリークの原因になってしまう、ということもある。
  http://d.hatena.ne.jp/IchiRoku/20101230/1293717201
 
 Contextは「そのContext自身の生存範囲と利用側の都合が一致しているかどうか」を常に意識して扱わなければならない、ということだろう。アプリケーション全体で静的に持ちたい情報(複数画面で共有される情報など)はApplicationContextで。アクティビティと運命を共にすべき(完全にアクティビティ子飼いのビューなど)ならばActivityContextで。
 しかしそれを実践しようにもContextという抽象的な型でやりとりするのは紛らわしく思うのだが。

    public class Hoge {
        
        Application mApp;
        Activity mActivity;
        
        public Hoge(Activity activity){
            mActivity = activity;
            mApp = activity.getApplication();
        }
        
    }

 もういっそのこと、このような潔い持ち方をしたほうがいいんじゃなかろうか。
 ちょっと納得がいかないことが多いので継続調査!

 
コメントする

投稿者: : 2011/07/02 投稿先 Android, プログラム, Java

 

タグ: , , ,

[Android] Contextの持ち方~ApplicationContext

 Android開発では、Context型のオブジェクトを使い回すことがやたら多い。
 問題はActivity(もしくはService)の中で利用できるContext型のオブジェクトはActivity(Service)のインスタンス自身と、getApplicationContext() で取得できるApplicationのインスタンスの二種類があるということだ。

        // 例えばActivity内でトーストを表示するとき、以下2通りのContextを渡せる。
        Toast.makeText(this, "Activity", Toast.LENGTH_SHORT).show();
        Toast.makeText(getApplicationContext(), "Application", Toast.LENGTH_SHORT).show();

 このあたりについては各所でまとめられている。例えばこちらのブログなんかにうまくまとまっている。
  http://individualmemo.blog104.fc2.com/blog-entry-41.html
 また、こちらのサイトではわかりやすくActivityやApplicationの違いや役割が図解されている。
  http://magpad.jugem.jp/?eid=197

 で、どちらかというと「Applicationのほうを使いましょう」というのが基本だ。特に独自クラスなんかで半永久的にContextを保持しておくような場合はなおさらだ。先のサイトでも言及されているが、ActivityやServiceは常にkillされ破棄される可能性があり、変に保持しっぱなしにするとメモリーリークの原因になる他、いつのまにか実体がいなくなって例外を吐いたりなどなど。

 よって、独自クラスでContextを持つ必要があるときは大体getApplicationContext()の戻り値を渡すようにする。が、しかし、そういうルールの下で開発を進めていくと、ついつい「contextの実体はほとんどApplicationContextだ」と思い込んでしまう。実際問題として、たまーにActivityが渡されてくる場合もあるので注意が必要である。
 例えば、独自で実装した拡張ViewクラスをレイアウトXMLに定義した場合、独自Viewクラスのコンストラクタで渡されるcontextはsetContentViewしたActivityインスタンスだったりする。どういうことかというと、

public class RedButton extends Button {

    // XMLでlayout定義してsetContentViewでinflateしてもらうにはこのコンストラクタが必要くさい
    public RedButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        super.setBackgroundColor(0xFFFF0000); //デフォルトで赤くする
    }

}

 こんな感じで新たに定義した独自ボタンクラスをレイアウトXMLで配置してアクティビティで呼び出した場合の上記contextの実体は呼び出したActivityオブジェクトが渡される。勿論、ActivityにセットしたViewなのだからこのRedButtonのインスタンスがActivityへの参照を保持しておくのは問題無い(アクティビティが死ねばボタンも消えるだろう)。しかしこのRedButtonクラスの中でクラス変数に代入…、いやそこまでじゃなくとも、別クラスに渡してしまうと厄介である。RedButtonのインスタンスとともに破棄されるインスタンス変数に渡すのなら問題ないのだが、Activityのライフサイクルを越えて保持される危険のあるオブジェクトに渡すのは危険である。
 Contextの実体に注意していればそのような渡し方はしないだろうが、普段からcontextにApplicationContextを使うようにして「context イコール ApplicationContextだ」という認識にはまってしまった場合、「ActivityのインスタンスをApplicationContextのつもりで保持してしまう」ということをうっかりやってしまう可能性がある。

 ということで、contextを貰ってきてそれを使い回す可能性があるのであれば、なるべくその実体がなんであるか明示して保持しておくようにしたい。例えば上記RedButtonクラスではActivityが渡って来るとわかっているのだから、

public class RedButton extends Button {

    private Context mAppContext;
    
    // XMLでlayout定義してsetContentViewでinflateしてもらうにはこのコンストラクタが必要くさい
    public RedButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        super.setBackgroundColor(0xFFFF0000); //デフォルトで赤くする
        mAppContext = context.getApplicationContext();
    }

}

最初からこうしておけば後から追記する際もきちんと区別出来るだろう。貰ってきたcontextの実体がApplicationContextでもcontext.getApplicationContext()の戻り値は同じ(はず)なので、contextを保持したい場合は必ずこのメソッドを呼んでおくことで保証出来る。

 まぁこういったトラブル回避策は人それぞれ好みがあるのでなんともいえないけども。


追記:2011/7/1
 なんでもかんでもApplication渡せばいいってもんじゃないことを思い出したので、そのことについて次書きます。

 
1件のコメント

投稿者: : 2011/06/29 投稿先 Android, プログラム, Java

 

タグ: , , , ,

[Android] テクスチャ用正方形画像を合成 (2)

以下の記事の続きである。今回は問題の整理だけでソース無し。
 [Java] ある整数よりも大きい最小の2のべき乗を計算
 [Android] テクスチャ用正方形画像を合成 (1)

 前回の記事で、テクスチャ用の正方形画像を合成することが出来た。しかし正方形の一辺の大きさが2のべき乗であるという制約上、どうしても無駄な領域が生じてしまう。そこで、なるべくうまく領域を使うために1つの大きな正方形領域にテクスチャ化したい画像を並べたものを合成することが望ましいだろう。これはグラフィックメモリの効率的にも、また「テクスチャを作成する処理は重い」という理由からもやっておくべき処理だ。

 ということで特定の正方形の中に、長方形をなるべく効率よく敷き詰めるアルゴリズムを考えるわけだけど、
   「任意の大きさの多数の長方形を正方形の中になるべく密に充填する問題
…って、ちょっとどころじゃなく難しい。こういう問題を専門的にやってる分野もあるだろうから、多分うまい解は既に考えられているんじゃないかと思うが、軽くググった程度では目的に沿うような解を見つけることが出来なかった。

 そもそもなぜこのアルゴリズムが必要になるのか?そして既存のグラフィックス屋さんはどう解決しているのか?

 後者については、通常、3Dグラフィックスのテクスチャは「あらかじめ用意したものを使う」ので問題にならない(のだと思っている)。正方形になるにしろ長方形になるにしろ、ひとつなぎの大きな画像領域の中に、使用するイメージを並べたものを用意し、それをテクスチャとして読み込み、テクスチャ座標を指定してポリゴンにマッピングしていくのが普通だ。3Dに限らず効率的に画像を処理する場合は割と常識的なテクニックで、ゲーム作成をしたり、RPGツクールのようなゲームをやったことある人は、どのような画像リソースを用意するのか想像出来ると思う。

 では今回なぜこの手法を必要としているのかというと、「アプリケーションのユーザーが用意した画像を読み込めるようにする為」だ。まぁこれが本当に3Dポリゴンに貼り付けるテクスチャ画像を要求するのであれば、最初からテクスチャ用の画像を用意してもらうのがスジだろうが、OpenGLを用いた2Dライクなユーザーインターフェイスの作成を目的とした場合、例えばユーザーに用意させたい画像というのは「オリジナルのボタン画像」とか「オリジナルのアイコン画像」とかそういう類のものになる。オリジナルのアイコンを読み込ませるのに、わざわざテクスチャマッピングを意識した画像を用意させるわけにもいくまい。
 そういうわけで、ユーザーが用意した複数の(例えばアイコン)画像をテクスチャ化することになるわけだが、なんとかしてテクスチャ化に適した画像に加工しなきゃだめだよねという話なのであった。ああ道のりは長い。
 しかしそこで本当に任意の画像を自由に読み込ませる必要があるだろうか?アプリケーションの制約というものがあるはずだ。例えば「オリジナルのアイコン」を読み込ませるとして、2000px*2000pxのような巨大画像や、100px*4pxのような細長い画像を許容するものだろうか?多分、アイコンとして適した画像のサイズがあるはず。そう考えると、「指定したサイズの画像」もしくは「指定したサイズ以内の画像」しか使えませんよ、という制約があって良い。というか多くのアプリケーションがそういう方式を採用している。

 というわけで、画像合成の方針としては
  「同じ大きさの多数の長方形画像をタイル状に並べたなるべく小さな正方形画像を合成する
というものを目指すことにする。イメージとしては以下のような感じ。

 アプリケーションの制約としては、同じサイズの画像を用意させることにしてもいいが、「用意した画像は全て長辺が100pxまで縮小されます」みたいなことも可能だろう。前回の記事で紹介したラッピング合成の手法を使えば、ラッピングして同じサイズの画像にした上で並べればよい(そうすると無駄は増えてしまうが)。
 こんなところを妥協点として、画像合成アルゴリズムについて進めてみようと思う。

 
1件のコメント

投稿者: : 2011/06/11 投稿先 Android, プログラム, Java

 

タグ: , , , , ,

[Android] テクスチャ用正方形画像を合成 (1)

 今回も比較的基本的な話。

 5月26日の記事で書いたように、OpenGL ESでは原則的に非正方形テクスチャは使えず、正方形でも一辺の長さが2のべき乗である必要がある。じゃあテクスチャ用画像を合成で作成してしまおうという話だった。前回で2のべき乗に関する計算をするメソッドを作成したので、今回はそれを使って実際に画像合成のサンプルを作成してみる。

 今回使ってみる画像はコイツ。
 

 実際にプロパティでも見ていただくとわかるが、この画像のサイズは240px×149px。なので256px×256pxの正方形にくるんでやれば良いことになる。画像の合成を行うコードはこんな感じになる。


    public Bitmap makeTexturableBmp(Resources res, int resId){
        
        //画像サイズに応じてビットマップ作成
        Bitmap org = BitmapFactory.decodeResource(res, resId);
        Bitmap ret = null;
        if(org!=null){
            int w = org.getWidth();
            int h = org.getHeight();
            if(w==h && MathUtil.isPowerOfTwo(w)){
                //正方形で一辺が2のべき乗ピクセルならそのまま使う
                ret = org;
            } else {
                //ダメなら2のべき乗正方形で覆ったビットマップを作成する
                //w*hの画像を覆える最小の2のべき乗正方形を算出
                int len = MathUtil.padToPowerOfTwo(w,h);  
                Bitmap wrapper = Bitmap.createBitmap(len, len, Bitmap.Config.ARGB_8888);
                Canvas offScreen = new Canvas(wrapper);
                offScreen.drawColor(0x99990000); // ← 確認のために色を塗る
                offScreen.drawBitmap(org, 0f, 0f, null);
                ret = wrapper;
                org.recycle(); //ちゃんと消しておく
            }
        }
        
        return ret;
        
    }

 MathUtilというクラスは5月26日の記事で載せた静的メソッドを集めたクラスである。条件に合う正方形ビットマップを作成し、Canvasオブジェクトをオフスクリーンとして合成を行って返すだけ。面倒くさいのは条件分岐のところなのだが、MathUtilを作っておいたのでさくっと書けた。あとはこのメソッドにリソースを食わせてあげればいい。

        ImageView img = (ImageView) findViewById(R.id.img);
        img.setImageBitmap(makeTexturableBmp(getResources(),R.drawable.qbsample));

 今回はアクティビティのonCreateで適当に描画して確かめる。実機(Xperia arc)の実行結果SSを撮ってみた。

 ちゃんと256の正方形でくるまれた画像になっている。実際は色を塗らずに作成し、このビットマップをまるまるテクスチャとして登録した後、クリッピングすればよい。テクスチャ用に作ったのにテクスチャとして確認してないのは、OpenGLのコードを書くと長くなってしまうので。

 さて、ここまでだらだら書いておいてナンだが、ぶっちゃけこの方法はあまり効率がよくない
 無駄が多いのは明らかだろう。例えばQB画像の横幅が257pxだっただけで、ラッパーの正方形は512px四方になってしまうのである。空白の部分にメモリを食うのは無駄だ(多分、そんな都合良い最適化とか行われないだろうし?)。ただでさえメモリの少ないAndroidでそんな無駄を許していいだろうか、まぁ状況次第だろうか。
 そうなるともう少し広大な正方形を用意して、複数の画像をなるべく隙間無くマージしよう、という話になる。しかしこれは難問だ。今回のようにあらかじめサイズが分かっている画像リソースをマージするならうまいパッキング方法を自分で考えられるが、例えば、任意に指定された画像ファイルをマージするとなると……数学的な難問になってくる。
 まぁ本当に任意のサイズの長方形を正方形にパッキングするアルゴリズムというのはあまりに大変なので、そのへんを上手く避けたソリューションを考えなければならない。次回はそんな記事を書く…かもしれない。

 
1件のコメント

投稿者: : 2011/05/31 投稿先 Android, プログラム, Java

 

タグ:

[Java] ある整数よりも大きい最小の2のべき乗を計算

 Android開発をしてて必要になったのだが、Androidに限らず通用するアルゴリズムなのでJavaと書いてみた。

 相変わらずやや長い前置き。
 さて、タイトルだけ読んでも「なんのこっちゃ」「まるでプログラム初級教本の課題のようだ」と思うだろうけど、これが必要になる場面がある。OpenGLで任意のビットマップイメージからテクスチャを作成する際に必要になるのだ。私が扱ったことのある3DCGライブラリがOpenGL ESだけなので、それを前提に書くが、他の3DCGライブラリでもテクスチャ回りは似たようなものだろうと思う。

 OpenGL ESでは生成できるテクスチャのサイズと形状に関して制約がある。正方形、かつ、一辺の長さが2のべき乗(ピクセル?)というものらしい。逆にこの制約を守ってないテクスチャを「非正方形テクスチャ」と言うらしい。基本的に非正方形テクスチャはサポートされない。実際、非正方形の画像をテクスチャ化しても致命的なエラーは起きないのだが、ハードウェア依存性が出たり、崩れるテクスチャがあったり(全てではない)、どうも不安定になってしまうことが分かった。
 しかしテクスチャとして貼り付けたい画像は非正方形であることのほうが多い。これに対して通常は「テクスチャは正方形で作成し、テクスチャマッピングの際に貼り付けるテクスチャ座標を長方形で指定する」という手法をとるようだ。つまり適当な2のべき乗サイズの正方形画像を用意して、そのなかにすっぽり目的の画像を合成したものをテクスチャとして読み込んでしまえば良い、テクスチャに貼り付ける際は必要部分だけ切り取る、という話。が、手動でそんなすっぽり正方形な画像を合成するのは面倒くさいので、テクスチャ読み込み処理の中で自動で画像合成してしまうのが望ましい。

 …という流れで、タイトルの処理が必要になる。テクスチャ用イメージ作成のために、目的画像をラップ出来る正方形の一辺の長さを算出する必要がある、というわけだ。例えば、40px×92pxの画像をテクスチャ化するには128px×128px(128=2^7)の正方形が必要になる。
 さてまず必要になるのは整数が2のべき乗がチェックする関数。

    /**
     * xが2のべき乗かどうか調べる関数
     * @param x
     * @return 2のべき乗ならtrue。0はfalse。
     */
    public static boolean isPowerOfTwo(int x){
        return x > 0 && (x & (x - 1)) == 0;
    }

偶に見かけるアルゴリズムだとは思う。ループなんてせずにビットマスク演算して一発判定である。
 で、あとは画像の縦と横のうち長い方よりも長さ以上の2のべき乗を探してやればよい。

    /**
     * 整数列のうち最大の整数を返す。
     * @param args 整数列
     * @return
     */
    public static int max(int... args){
        int max = 0;
        for(int i=0; i<args.length;i++){
            max = Math.max(max, args[i]);
        }
        return max;
    }
    
    /**
     * 整数列のうち最大の整数よりも大きい、最小の2のべき乗値を返す関数
     * 引数のうちの最大の整数が0以下の場合は0を返す。
     * 万が一32bit整数で表現可能範囲外になった場合も0を返す。
     * @param args 整数列
     * @return
     */
    public static int padToPowerOfTwo(int... args){
        int max = max(args);
        if(max<=0){
            return 0;
        }
        int ret = 0;
        if(!isPowerOfTwo(max)){
            //単純な実装(パフォーマンスはそこそこを想定)
            // isPowerOfTwo関数によりshift=0,1は見る必要なし
            for(int shift=2; shift<31; shift++){ // 32bit符号付整数なので30回シフト=2^31が限界
                ret = 1 << shift;                // 1を左シフトして2のべき乗を生成
                if(ret > max){
                    return ret;
                }
            }
        } else {
            ret = max;
        }
        return ret;
    }

 max関数はついでに実装した。今回は縦と横の2値だけなのでいらないんだけどw
 2のべき乗値の作成はループごとにかけ算して作成していっても良いのだけど、Javaのコンパイラがビットシフトとして変換してくれるか謎だったので、敢えて生で左シフト演算して作成。なんかもっと高速に動くアルゴリズムがありそうだが、そんな繰り返し使う予定でもないので、まぁこんなもんだろうと思う。(本当はisPowerOfTwoのようにうまくビット演算数回して導出できないか(計算量O(1)になるようなアルゴリズムを)探してみたのだが、ちょっと思いつかなかった)

 実際に、Androidで画像をラップする処理はまた今度。

 なんというか、頭の体操的な感じのものだが、毎回自作してたら面倒くさい類のものでもあるのでやる気のあるうちに確立しておこうと思って記事にした。こういうアルゴリズムとか3DCGの話って情報系の人は授業で習ったりするのかな。そう思うと情報系の授業も受けとけばよかったかなと思いつつ、べつに独学で問題ないかとも思いつつ。絶賛勉強中です。

 
2件のコメント

投稿者: : 2011/05/26 投稿先 Android, プログラム, Java

 

タグ: , , ,

Androidで周期的な処理をするクラスを作ってみた

 また唐突だがAndroidアプリ開発の記事を書く。

 アプリのユーザビリティを考えると、マルチスレッド処理は必須だ。
 Android SDKでマルチスレッド処理を書く場合、いろいろな方法がある。どんな方法があるかはググってもらった方が早いだろう。一応、一通り便利に処理するための枠組みは用意されている。まぁ、「スレッドの存在は意識してもらうけど管理はライブラリに任せてくれたまえ」くらいな感じ。いいところだと思う。

 別スレッド処理が必要な典型例としては「バックグラウンドで重い処理」と「周期的な処理」が考えられる。
 AndroidではUIコンポーネント(画面上のボタンなど)はメインスレッド(UIスレッド)からしかアクセスできないという仕様があるので、別スレッドからUIコンポーネントを操作するにはUIスレッドとスレッド間通信みたいなことをする必要が出てくる。例えば、「バックグラウンドで重いファイルをダウンロードしながらその進捗率をプログレスバーで表示する」という処理をするには、ワーカースレッドでダウンロード監視を行いつつ、状況をUIスレッドに通知してプログレスバーを進ませるという記述をしなければならない。実はそういった処理に関してはAsyncTaskという便利なクラスが用意されており、比較的簡単に、特別なことを考えなくても実装出来るようになっている。
 さて、今回やりたかったのは「OpenGL描画のFPSをTextViewで定期的に表示する」という、後者(周期的な処理)のパターン。この実装もAsyncTaskで出来るかな、と思ったのだが、どうもよくよく調べてみるとAsyncTaskは明らかに「使い捨て一度きり!」な作りになっており、ちょっと周期的処理に使うには無理がある。ということで、ちゃんとAndroid的なマルチスレッド処理を書かなければならない。

 最初は自前でThreadを作って回そうかと思ったが、幸いなことにAndroid SDKにはjava.util.Timerクラスとjava.util.TimerTaskクラスが含まれているのでこれを利用することにする。あとはUIスレッドと通信するためのandroid.os.Handlerクラス(こいつについてはthrow Lifeさんの「AndroidのHandlerとは何か?」が大変参考になる)。

 はてさて、前置きが長くなったが、どうせ周期部分の処理なんて同じパターンになるだろうので後でも使い回せそうなクラスを作ってみたという話である。以下、クラス定義をベタ貼り。

/**
 * 周期的に何か処理を走らせるためのクラス。
 * 周期カウントはこのクラスのインスタンスで作るスレッドで行われる。
 * invokersMethod()の中の処理はこのインスタンスを作成したスレッドで行われる。
 * 継承して使ってください。
 */
public abstract class AbstractPeriodicTask {

    private long period;
    private boolean isDaemon;
    private boolean isCancelled = true;
    private Timer timer;
    private TimerTask timerTask;
    private Handler handler;
    
    /**
     * periodミリ秒の周期で動かす
     * @param period 周期ミリ秒
     * @param isDaemon 定期処理を行うスレッドをデーモンスレッドで作成するかどうか(false=ユーザースレッド)
     */
    public AbstractPeriodicTask(long period, boolean isDaemon) {
        handler = new Handler();
        this.period = period;
        this.isDaemon = isDaemon;
    }
    
    /**
     * periodミリ秒の周期で動かす。タイマースレッドはユーザースレッドで作成される
     * @param period 周期ミリ秒
     */
    public AbstractPeriodicTask(long period){
        this(period,false);
    }
    
    /**
     * 周期タスクの実行を開始する
     */
    public void execute(){
        
        if(!isCancelled){
            //isCancelledがfalse(=実行中)ならばこのメソッドは実行しない
            return;
        }
        
        //timerをキャンセルした場合、timer,timerTaskは破棄されるので都度作り直す
        timerTask = new TimerTask(){
            @Override
            public void run() {
                preInvokersMethod();
                handler.post(new Runnable(){
                    @Override
                    public void run() {
                        invokersMethod();
                    }
                });
                postInvokersMethod();
            }
        };
        timer = new Timer(isDaemon);
        timer.scheduleAtFixedRate(timerTask, period, period);
        
    }
    
    /**
     * 周期タスクの実行をキャンセルする
     */
    public void cancel(){
        if(timer==null || timerTask==null){
            return;
        }
        timer.cancel();
        timer = null;
        isCancelled = true;
    }
    
    /**
     * 本インスタンスを作成したスレッド(例えばUIスレッド)で処理させるメソッド
     */
    abstract protected void invokersMethod();
    
    /**
     * タイマースレッドで処理させるメソッドでinvokersMethodの直前に呼ばれる。
     */
    protected void preInvokersMethod(){
    }
    
    /**
     * タイマースレッドで処理させるメソッドでinvokersMethodの直後に呼ばれる。
     */
    protected void postInvokersMethod(){
    }
    
}

 AsyncTaskの作りをなんとなく参考にして作ったが、大したことはしてません。TimerTaskを回す部分と、Handlerを作成して処理を元スレッドに依頼する部分をやるだけのクラス…。なんだかこうやって貼り付けると長いコードに感じてしまう。一応、中断も考慮してみたけども、これで本当に大丈夫だろうか…?まぁ問題があれば修正エントリを書けばいいか。

 使用例として、1秒ごとにカウンターを進めるアプリを試作。こんな感じで使える。


/**
 * 周期的にカウントを進めるクラス
 */
public class PeriodicCounter extends AbstractPeriodicTask {

    TextView countView;
    private int count;
    
    public PeriodicCounter(long period, boolean isDaemon, TextView countView) {
        super(period, isDaemon);
        this.countView = countView;
    }

    @Override
    protected void invokersMethod() {
        count++;
        countView.setText(Integer.toString(count));
    }

}

/**
 * 言わずもがなMainActivityクラス
 */
public class PeriodicExp extends Activity {
    
    private TextView countDisplay;
    private PeriodicCounter counter;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        //フルスクリーン表示
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
        //タイトルバー非表示
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        
        setContentView(R.layout.main);
        countDisplay = (TextView) findViewById(R.id.countDisplay);
        
        //カウンターを周期1000ms、カウント表示をcountDisplayで生成
        counter = new PeriodicCounter(1000,true, countDisplay);

    }

    @Override
    protected void onResume() {
        super.onResume();
        if(counter!=null){
            counter.execute();
        }
    }

    @Override
    protected void onPause() {
        if(counter!=null){
            counter.cancel();
        }
        super.onPause();
    }
  
}

 
 PeriodicTaskを継承したPeriodicCounterクラスを定義。そのインスタンスで周期カウント→都度UIスレッドでカウントを描画、というだけのアプリ。長くなったのでmain.xmlは書かなくていいだろう(countDisplayというIDのTextViewがあるだけ)。

 とりあえずこれで周期的に何かやる、みたいのは楽になったと思う。
 ちょっとこれで別に試したいこともでてきたけど、それはまた別件で。


(2011/05/25) ソース修正。
PeriodicTaskクラスをAbstract化してみた。→AbstractPeriodicTaskクラス。
これで典型的なTemplateMethodパターンっぽくなった?

 
3件のコメント

投稿者: : 2011/05/18 投稿先 Android, プログラム

 

タグ: , , , ,