Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit afed0351 authored by Ned Burns's avatar Ned Burns
Browse files

Implement message handling in ExecutorImpl

Previously, we were posting runnables to the queue but never doing
anything when their messages came due.

 - Implements message handling (in onHandleMessage).
 - Inlines HandlerExecutor superclass. It was only offering one, very
   simple, method and the required super() call was awkward.
 - Changes ExecutorImpl to take in a Looper instead of a Handler; we
   construct the Handler for ourselves.
 - Changes ExecutorImpl to be a singleton per-looper (since we're
   constructing a new Handler each time).
 - Changes our token to be an instance of ExecutionToken. This serves as
   wrapper around the Runnable that we need to run and itself serves as
   cancellation token that can be returned to the caller of
   executeDelayed() etc.i

Test: manual
Change-Id: Ia986d48c675a794c6f16dca6a82cb8cbd4010741
parent b4632bca
Loading
Loading
Loading
Loading
+10 −5
Original line number Diff line number Diff line
@@ -83,17 +83,19 @@ public abstract class ConcurrencyModule {
     * Provide a Background-Thread Executor by default.
     */
    @Provides
    @Singleton
    public static Executor provideExecutor(@Background Looper looper) {
        return new ExecutorImpl(new Handler(looper));
        return new ExecutorImpl(looper);
    }

    /**
     * Provide a Background-Thread Executor.
     */
    @Provides
    @Singleton
    @Background
    public static Executor provideBackgroundExecutor(@Background Looper looper) {
        return new ExecutorImpl(new Handler(looper));
        return new ExecutorImpl(looper);
    }

    /**
@@ -109,26 +111,29 @@ public abstract class ConcurrencyModule {
     * Provide a Background-Thread Executor by default.
     */
    @Provides
    @Singleton
    public static DelayableExecutor provideDelayableExecutor(@Background Looper looper) {
        return new ExecutorImpl(new Handler(looper));
        return new ExecutorImpl(looper);
    }

    /**
     * Provide a Background-Thread Executor.
     */
    @Provides
    @Singleton
    @Background
    public static DelayableExecutor provideBackgroundDelayableExecutor(@Background Looper looper) {
        return new ExecutorImpl(new Handler(looper));
        return new ExecutorImpl(looper);
    }

    /**
     * Provide a Main-Thread Executor.
     */
    @Provides
    @Singleton
    @Main
    public static DelayableExecutor provideMainDelayableExecutor(@Main Looper looper) {
        return new ExecutorImpl(new Handler(looper));
        return new ExecutorImpl(looper);
    }

    /**
+43 −11
Original line number Diff line number Diff line
@@ -17,37 +17,69 @@
package com.android.systemui.util.concurrency;

import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.Message;

import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * Implementations of {@link DelayableExecutor} for SystemUI.
 */
public class ExecutorImpl extends HandlerExecutor implements DelayableExecutor {
public class ExecutorImpl implements DelayableExecutor {
    private final Handler mHandler;

    public ExecutorImpl(Handler handler) {
        super(handler);
        mHandler = handler;
    ExecutorImpl(Looper looper) {
        mHandler = new Handler(looper, this::onHandleMessage);
    }

    @Override
    public void execute(Runnable command) {
        if (!mHandler.post(command)) {
            throw new RejectedExecutionException(mHandler + " is shutting down");
        }
    }

    @Override
    public Runnable executeDelayed(Runnable r, long delay, TimeUnit unit) {
        Object token = new Object();
        Message m = mHandler.obtainMessage(0, token);
        ExecutionToken token = new ExecutionToken(r);
        Message m = mHandler.obtainMessage(MSG_EXECUTE_RUNNABLE, token);
        mHandler.sendMessageDelayed(m, unit.toMillis(delay));

        return () -> mHandler.removeCallbacksAndMessages(token);
        return token;
    }

    @Override
    public Runnable executeAtTime(Runnable r, long uptimeMillis, TimeUnit unit) {
        Object token = new Object();
        Message m = mHandler.obtainMessage(0, token);
        ExecutionToken token = new ExecutionToken(r);
        Message m = mHandler.obtainMessage(MSG_EXECUTE_RUNNABLE, token);
        mHandler.sendMessageAtTime(m, unit.toMillis(uptimeMillis));

        return () -> mHandler.removeCallbacksAndMessages(token);
        return token;
    }

    private boolean onHandleMessage(Message msg) {
        if (msg.what == MSG_EXECUTE_RUNNABLE) {
            ExecutionToken token = (ExecutionToken) msg.obj;
            token.runnable.run();
        } else {
            throw new IllegalStateException("Unrecognized message: " + msg.what);
        }
        return true;
    }

    private class ExecutionToken implements Runnable {
        public final Runnable runnable;

        private ExecutionToken(Runnable runnable) {
            this.runnable = runnable;
        }

        @Override
        public void run() {
            mHandler.removeCallbacksAndMessages(this);
        }
    }

    private static final int MSG_EXECUTE_RUNNABLE = 0;
}