Androidアプリで簡易なthrottle処理を実装する……の、続き

前回の投稿ですが、独立したクラスにして、他のActivityからでも参照できるようにしたほうがいいかと思いました。こちらの方がより汎用性・可読性の意味で使いやすいのではないでしょうか。

[MainActivity.java]

    /** throttle処理で間引く間隔 */
    private static final int THROTTLE_INTERVAL = 60 * 1 * 1000;

    /** throttle処理クラス */
    private Throttle throttle;
    ・
    ・
    ・
    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);

        // 初期化
        throttle = new Throttle(THROTTLE_INTERVAL);
    }
    ・
    ・
    ・
    // コード上の任意の場所で
    {
        //throttle処理を入れて間引く
        throttle.setThrottle(new Throttle.ThrottleEventListener() {
            @Override
            public void onActuate(){
                //実行したい処理
                ・
                ・
                ・
            }
        });
    }

[Throttle.java]

/**
 * Javaでthrottle処理を行う
 */
public class Throttle {

    /**
     * throttle処理用のハンドラ
     */
    private Handler throttleHandler;

    /**
     * throttle処理を実行した最後の時間
     */
    private long throttleLastTimeMillis;

    /**
     * 処理を間引く間隔(ミリ秒)
     */
    long throtteleInterval;

    /**
     * リスナ
     */
    private ThrottleEventListener throttleEventListener;

    //--------------------------------------------------------------
    // コンストラクタ
    //--------------------------------------------------------------
    public Throttle(long throtteleInterval) {
        //初期化
        throttleHandler = new Handler();
        throttleLastTimeMillis = 0;

        //間引く間隔を定義する
        this.throtteleInterval = throtteleInterval;
    }

    //--------------------------------------------------------------
    // メソッド
    //--------------------------------------------------------------
    public void setThrottle(ThrottleEventListener throttleEventListener) {
        //イベントを通知するリスナーセット
        this.throttleEventListener = throttleEventListener;

        long currentTimeMillis = System.currentTimeMillis();

        // 最後に実行した時間から間引きたい時間経過していたら実行
        if ((throttleLastTimeMillis + throtteleInterval) <= currentTimeMillis) {
            throttleLastTimeMillis = currentTimeMillis;

            //処理実行
            throttleEventListener.onActuate();
        } else {
            //重複して呼び出さないようにキャンセルするのを忘れずに
            throttleHandler.removeCallbacks(throttleTask);
            //間引いた場合は、間隔明けに実行する
            throttleHandler.postDelayed(throttleTask, (throttleLastTimeMillis + throtteleInterval) - currentTimeMillis);
        }
    }

    //--------------------------------------------------------------
    // タスク
    //--------------------------------------------------------------
    private final Runnable throttleTask = new Runnable() {
        @Override
        public void run() {
        throttleHandler.removeCallbacks(throttleTask);

        throttleLastTimeMillis = System.currentTimeMillis();

        //処理実行
        throttleEventListener.onActuate();
        }
    };

    //--------------------------------------------------------------
    // イベントリスナ
    //--------------------------------------------------------------
    public interface ThrottleEventListener {
        /**
         * 通知
         */
        void onActuate();
    }
}

独立したクラスでほとんどの処理が完結しているので、Activity側では実質、間引き時間だけを管理すれば良いという状態になり非常に楽です。
当たり前ですが、Throttleクラスをnewするのは1回だけ(onCreate内でだけ)にしてくださいね。じゃないと、newした分だけタスクが走ることになり、処理が重複してしまいます。

カテゴリー: Android, Java, プログラム

Androidアプリで簡易なthrottle処理を実装する

明けましておめでとうございます。今年もよろしくお願いします。
ずいぶん久しぶりの記事更新です。今年はもっと投稿できるようにしたいですね。(果たして何本投稿できるかな……)
 
Androidアプリで、とあるイベントが走るたびにAPI通信を行っていて動作を圧迫していたようなので、適度に処理を間引く必要が出てきました。
そこで、Javaで簡易なthrottle処理を実装してみました。
Handlerを上手く使えばそれっぽく動くのが完成しました。
 
[宣言]

    /** throttle処理で間引く間隔 */
    private static final int THROTTLE_INTERVAL = 60 * 1 * 1000;

    /** throttle処理用のハンドラ */
    private Handler throttleHandler;

    /** throttle処理を実行した最後の時間 */
    private long throttleLastTimeMillis;

 
[初期化]

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 変数を初期化する。
        throttleHandler = new Handler();
        throttleLastTimeMillis = 0;
    }

 
[throttle呼び出し]

       //throttle処理を入れて間引く
        long currentTimeMillis = System.currentTimeMillis();

        // 最後に実行した時間から間引きたい時間経過していたら実行
        if ((throttleLastTimeMillis + THROTTLE_INTERVAL) <= currentTimeMillis) {
            throttleLastTimeMillis = currentTimeMillis;

            //実行したい処理
            ・
            ・
            ・
        } else {
            //重複して呼び出さないようにキャンセルするのを忘れずに
            throttleHandler.removeCallbacks(throttleTask);
            //間引いた場合は、間隔明けに実行する
            throttleHandler.postDelayed(throttleTask, (throttleLastTimeMillis + THROTTLE_INTERVAL) - currentTimeMillis);
        }

 
[タスク定義]

    /** throttle処理タスク */
    private final Runnable throttleTask = new Runnable() {
        @Override
        public void run() {
            throttleHandler.removeCallbacks(throttleTask);

            throttleLastTimeMillis = System.currentTimeMillis();

            //実行したい処理
            ・
            ・
            ・
        }
    };

初回のthrottle呼び出しはそのまま処理されますが、次の呼び出しがTHROTTLE_INTERVALミリ秒分の間隔があいていなければ、HandlerにpostDelayedがセットされます。これは、THROTTLE_INTERVALミリ秒以内であれば何度呼び出されても同じくpostDelayedがセットされるだけです。
一点だけ気を付けるとしたら、throttleLastTimeMillisがprivateな変数なので、ライフサイクル的に破棄されることがあると思うので、それでも大丈夫なように、内部の処理に留意ください。

カテゴリー: Android, Java, プログラム

In-App BillingでIllegalArgumentExceptionでおちる

お久しぶりです。職場が変わり、働き方がだいぶ違ったのでなかなか記事を書く時間がありませんでした。
ちゃんと生きていますので、ゆっくりマイペースで投稿していきます。

アプリ内アイテムで課金機能を実装しました。
それで、購入ボタンを押したときに一部のAndroid5.0以降の端末で、

Service Intent must be explicit: Intent { act=com.android.vending.billing.InAppBillingService.BIND }

とログが出て落ちてしまう場合がありました。

これは、決済モジュールへのコネクションを作成するときの、バインドのやり方に原因があります。

this.bindService(new Intent("com.android.vending.billing.InAppBillingService.BIND"), this.serviceConnection, Context.BIND_AUTO_CREATE);

このようにバインドしていませんか?
この場合、下記のようなバインドのやり方に変更すると落ちなくなります。

Intent intent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
intent.setPackage("com.android.vending");
this.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
カテゴリー: Android, Java, プログラム

株式会社プラスアールを退職します

退職のごあいさつ

わたくし谷口隆太は、この度2015年2月28日をもって株式会社プラスアールを退職いたします。

退職自体は2月末日ですが、最終出社日は1月29日なので、いったんプラスアールでの勤務は終わりになります。

2011年1月25日からプラスアールに入社して、それから4年間ほど在籍させていただきました。

エンジニアとして働き始めて2年目から6年目という、おそらく最も成長する時期に、ITヘルスケア領域というポテンシャルのある分野の最前線で働くことができ、自分の大きな糧となったと思います。

プラスアールでの4年間を振り返るなら、大きく分けて4つのタームがあったと思います。

 

最初の半年:キャッチアップ期

プラスアールに来て最初の仕事は、Mixi/GREE/Mobageにて配信されていたソーシャルアプリの企画・開発・運営でした。

WEB系のスキルに初めて触れ、迷惑かけてばかりの日々でした。しかし、その中でビジネスのいろはから、エンジニアとしてのふるまい、IT系の知識まで、多くのことを吸収でき、目線が一段高くなったような気がします。

2年目まで:スキルアップ期

それから半年後には、新規ソーシャルアプリの企画・開発・運営をひとりで任されました。

特に、とあるアプリでは、チーム作りから開発環境の整備、企画、シナリオづくり、プログラム開発、リリース、ユーザー対応まで、ひとつのアプリが始まって終わるところまでほぼ全てに関わり、事業そのものに関わることの意味を知りました。

2年目から3年目:社内にノウハウを蓄積する

部署というには大げさですが、それまで社内になかったAndroidアプリ部門を任されることになり、現在約30本ほどリリースされるまで1人で開発していました。今私のスキルとして最も大きな強みは、このAndroidアプリ開発です。

3年目から現在:より生産性を高める

3年目からは、「いかに早く完成させ、いかにバグを少なくするか」ということを常にテーマとして忘れず開発していました。結果、自分でも「このクオリティのアプリを1週間で完成できるエンジニアはなかなかいないだろう」と自信をもって言えるレベルの成果物を安定して開発できるようになりました。

 

そして現在

なぜ転職することになったかというと、より大きな場で自分の力を試したくなったからです。

今まで私は中小企業でしか働いたことがなかったため、多数のメンバーと大規模なサービスを開発するという経験がありませんでした。ほとんどのアプリを、1人でフルスタック的に開発してきたのです。

しかし、今後のキャリアパスを考えたときに、大規模開発を経験することなく、ディレクターやプロデューサーのような、さらに一歩進んだ人材になれるだろうか、と不安になりました。

業務内容や人間関係に不満はありませんでしたが、自分の今後のことを考えて、転職という結論に達しました。

 

これからについて

すでに次の転職先は決まっています。次は教育業界にて働くつもりです。

教育分野もヘルスケアと同じく、社会的意義が高く、まだITが浸透していないブルーな市場です。そこでもチャレンジをし続けて、より成長していきたいと考えています。

 

次の会社に入社するまで、しばらくはリフレッシュや趣味アプリを作ったり、学習をしていこうかと思います。食事や飲み会などお声をかけるかもしれませんが、そのときはなにとぞお願いします。もちろん、誘っていただければ喜んで参加いたします。

 

それでは!

4年の間、たくさんの人と関わり、学ばせて頂きました

この場を借りて、お世話になった方々に心よりお礼を申し上げます。ありがとうございました!!

これからもよろしくお願いします!!

 

谷口隆太

カテゴリー: 雑記

ファイル名からfindViewByIdするIDを取得する

普通、レイアウトXMLに配置したコンポーネントのIDを取得するときや、画像のリソースIDを取得するときは、

ImageView iv = (ImageView) findViewById(R.id.iv);
Drawable image = getResources().getDrawable(R.drawable.image);

//画像をImageViewにセット
iv.setImageDrawable(image);

のように、idで取得してくるのが普通だと思います。

しかし、ここで例えば以下のような実装をするとします。
——————–
IDがiv1,iv2,iv3,・・・iv50と50個のImageViewに、
image1,image2,image3・・・image50とファイル名がつけられた50個のpng画像を貼りつける
——————–

その場合、下記のように50個分のソースを書くのは大変です。

ImageView iv1 = (ImageView) findViewById(R.id.iv1);
ImageView iv2 = (ImageView) findViewById(R.id.iv2);
ImageView iv3 = (ImageView) findViewById(R.id.iv3);
.
.
.
ImageView iv50 = (ImageView) findViewById(R.id.iv50);

Drawable image1 = getResources().getDrawable(R.drawable.image1);
Drawable image2 = getResources().getDrawable(R.drawable.image2);
Drawable image3 = getResources().getDrawable(R.drawable.image3);
.
.
.
Drawable image50 = getResources().getDrawable(R.drawable.image50);

//画像をImageViewにセット
iv1.setImageDrawable(image1);
iv2.setImageDrawable(image2);
iv3.setImageDrawable(image3);
.
.
.
iv50.setImageDrawable(image50);

なので、ファイル名からIDを取得しましょう!
基本的な書式は、「getResources().getIdentifier(“ファイル名”, “id”, getPackageName());」です。

for (int i = 0; i < 50; i++) {
	//ImageViewのIDを文字列から取得する
	int viewId = getResources().getIdentifier("iv" + (i + 1), "id", getPackageName());
	ImageView iv = (ImageView)findViewById(viewId);

	//文字列から画像のdrawableのIDを取得する
	int imageId = getResources().getIdentifier("image" + (i + 1), "drawable", getPackageName());

	//画像をImageViewにセットする
	iv.setImageResource(imageId);	
}

リソースIDを取得するときは、第二引数を”id”から”drawable”に変更してください

カテゴリー: Android, Java, プログラム