2012年1月11日水曜日

AndroidのHandlerクラスの使い勝手を良くする

Android の Handler クラスの使い勝手が悪いので、改善してみる。

UI 関連の処理をおこないたい場合、(Handler クラスのデフォルトコンストラクタを使うと)「UI スレッド内で Handler インスタンスを作成し、Runnable インスタンスを別途作成して当該 Handler インスタンスの post メソッドに渡す」というコーディングが必要で、そのままコーディングすると次のようになるが、

// UI スレッド
//
// Handler インスタンスを作る。別スレッドで使うというユースケースが
// 大半なので、final を付けることが多くなる。
final Handler handler = new Handler();

// UI スレッドではない別のスレッド
//
// UI に関連する処理をスケジュールする。
handler.post(new Runnable() {
    public void run() {
        // UI スレッドで実行する処理を記述する。
    }
});

面倒なので、「(1) 常に UI スレッドに紐付けられ、(2) 自クラスで Runnable インターフェースを実装する」Handler クラスのサブクラスを次のように実装してみる。

public class UiHandler extends Handler implements Runnable
{
    public UiHandler()
    {
        // Looper.getMainLooper() で UI スレッドの Looper を取得する。
        super(Looper.getMainLooper());
    }

    public UiHandler(Handler.Callback callback)
    {
        // Looper.getMainLooper() で UI スレッドの Looper を取得する。
        super(Looper.getMainLooper(), callback);
    }

    public boolean post()
    {
        // 自分で Runnable インターフェースを実装しているので、
        // post メソッドに this を渡すことができる。
        return post(this);
    }

    public boolean postAtFrontOfQueue()
    {
        return postAtFrontOfQueue(this);
    }

    public boolean postAtTime(Object token, long uptimeMillis)
    {
        return postAtTime(this, token, uptimeMillis);
    }

    public boolean postAtTime(long uptimeMillis)
    {
        return postAtTime(this, uptimeMillis);
    }

    public boolean postDelayed(long delayMillis)
    {
        return postDelayed(this, delayMillis);
    }

    public void run()
    {
        // UI スレッドで実行する処理を記述する。
        // サブクラスでオーバーライドすべき。
    }
}

これにより、「任意のスレッド内で」(←面倒削減ポイント)当該サブクラスのインスタンスを作成できる上、Handler と Runnable を別々に管理する必要もなくなり、下記のようなコーディングが可能となる。

new UiHandler() {
    public void run() {
        // UI スレッドで実行する処理を記述する。
    }
}.post();