FileDocCategorySizeDatePackage
ContiguousFIFOAggregator.javaAPI DocAndroid 5.1 API12128Thu Mar 12 22:22:50 GMT 2015com.android.bitmap

ContiguousFIFOAggregator

public class ContiguousFIFOAggregator extends Object
An implementation of a task aggregator that executes tasks in the order that they are expected . All tasks that is given to {@link #execute(Object, Runnable)} must correspond to a key. This key must have been previously declared with {@link #expect(Object, Callback)}. The task will be scheduled to run when its corresponding key becomes the first expected key amongst the other keys in this aggregator.

If on {@link #execute(Object, Runnable)} the key is not expected, the task will be executed immediately as an edge case.

A characteristic scenario is as follows:

  1. Expect keys A, B, C, and Z, in that order. Key A is now the first expected key.
  2. Execute task 2 for key B. The first expected key is A, which has no task associated with it, so we store task 2.
  3. Execute task 4 for key Z. We store task 4.
  4. Execute task 1 for key A. We run task 1 because its key A is the first expected, then we remove key A from the list of keys that we expect.
  5. This causes key B to be the first expected key, and we see that we have previously stored task 2 for that key. We run task 2 and remove key B.
  6. Key C is now the first expected key, but it has no task, so nothing happens. Task 4, which was previously stored, cannot run until its corresponding key Z becomes the first expected key. This can happen if a task comes in for key C or if forget is called on key C.

ContiguousFIFOAggregator is not thread safe.

Fields Summary
private final Queue
mExpected
private final android.util.SparseArray
mTasks
private static final String
TAG
private static final boolean
DEBUG
Constructors Summary
public ContiguousFIFOAggregator()
Create a new ContiguousFIFOAggregator.

The nature of the prioritization means that the number of stored keys and tasks may grow unbounded. This may happen, for instance, if the top priority key is never given a task to {@link #execute(Object, Runnable)}. However, in practice, if you are generating tasks in response to UI elements appearing on the screen, you will only have a bounded set of keys. UI elements that scroll off the screen will call {@link #forget(Object)} while new elements will call {@link #expect(Object, Callback)}. This means that the expected number of keys and tasks is the maximum number of UI elements that you expect to show on the screen at any time.


                                                                                                                           
      
        mExpected = new ArrayDeque<T>();
        mTasks = new SparseArray<Value>();
    
Methods Summary
private booleancontains(T key)

        return mTasks.get(key.hashCode()) != null;
    
public voidexecute(T key, java.lang.Runnable task)
Attempt to execute a task corresponding to a previously declared key. If the key is the first expected key, the task will be executed immediately. Otherwise, the task is stored until its key becomes the first expected key. Execution of a task results in the removal of that key, which potentially allows another key to now become the first expected key, and may cause one or more other tasks to be executed.

If the key is not expected, the task will be executed immediately as an edge case.

param
key the key previously declared in {@link #expect(Object, Callback)}.
param
task the task to execute or store, depending on its corresponding key.

        Trace.beginSection("pool execute");
        final int hash = key.hashCode();
        final Value value = mTasks.get(hash);
        if (value == null || task == null) {
            if (task != null) {
                task.run();
            }
            Trace.endSection();
            return;
        }
        value.task = task;
        if (DEBUG) {
            Log.d(TAG, String.format("ContiguousFIFOAggregator ++ tasks: %s", prettyPrint()));
        }
        maybeExecuteNow();
        Trace.endSection();
    
public voidexpect(T key, com.android.bitmap.ContiguousFIFOAggregator$Callback callback)
Declare a key to be expected in the future. The order in which you expect keys is very important. Keys that are declared first are guaranteed to have their tasks run first. You must call either {@link #forget(Object)} or {@link #execute(Object, Runnable)} with this key in the future, or you will leak the key. If you call expect with a previously expected key, the key will be placed at the back of the expected queue and its callback will be replaced with this one.

param
key the key to expect a task for. Use the same key when setting the task later with {@link #execute (Object, Runnable)}.
param
callback the callback to notify when the key becomes the first expected key, or null.

        if (key == null) {
            throw new IllegalArgumentException("Do not use null keys.");
        }

        Trace.beginSection("pool expect");
        final int hash = key.hashCode();
        if (contains(key)) {
            mExpected.remove(key);
            mTasks.remove(hash);
        }
        final boolean isFirst = mExpected.isEmpty();
        mExpected.offer(key);
        mTasks.put(hash, new Value(callback, null));
        if (DEBUG) {
            Log.d(TAG, String.format("ContiguousFIFOAggregator >> tasks: %s", prettyPrint()));
        }

        if (isFirst) {
            onFirstExpectedChanged(key);
        }
        Trace.endSection();
    
public voidforget(T key)
Remove a previously declared key that we no longer expect to execute a task for. This potentially allows another key to now become the first expected key, and so this may trigger one or more tasks to be executed.

param
key the key previously declared in {@link #expect(Object, Callback)}.

        if (key == null) {
            throw new IllegalArgumentException("Do not use null keys.");
        }

        if (!contains(key)) {
            return;
        }

        Trace.beginSection("pool forget");
        final boolean removedFirst = key.equals(mExpected.peek());
        mExpected.remove(key);
        mTasks.delete(key.hashCode());
        if (DEBUG) {
            Log.d(TAG, String.format("ContiguousFIFOAggregator  < tasks: %s", prettyPrint()));
        }

        final T second;
        if (removedFirst && (second = mExpected.peek()) != null) {
            // We removed the first key. The second key is now first.
            onFirstExpectedChanged(second);
        }

        maybeExecuteNow();
        Trace.endSection();
    
private voidmaybeExecuteNow()
Triggered by {@link #execute(Object, Runnable)} and {@link #forget(Object)}. The keys or tasks have changed, which may cause one or more tasks to be executed. This method will continue to execute tasks associated with the first expected key to the last expected key, stopping when it finds that the first expected key has not yet been assigned a task.

        T first;
        int count = 0;
        while (!mExpected.isEmpty()) {
            Trace.beginSection("pool maybeExecuteNow loop");
            first = mExpected.peek();
            if (count > 0) {
                // When count == 0, the key is already first.
                onFirstExpectedChanged(first);
            }

            final int hash = first.hashCode();
            final Value value = mTasks.get(hash);
            if (value.task == null) {
                Trace.endSection();
                break;
            }

            mExpected.poll();
            mTasks.delete(hash);
            if (DEBUG) {
                Log.d(TAG, String.format("ContiguousFIFOAggregator  - tasks: %s", prettyPrint()));
            }
            value.task.run();
            count++;
            Trace.endSection();
        }
    
private voidonFirstExpectedChanged(T key)
This method should only be called once for any key.

param
key The key that has become the new first expected key.

        final int hash = key.hashCode();
        final Value value = mTasks.get(hash);
        if (value == null) {
            return;
        }
        final Callback<T> callback = value.callback;
        if (callback == null) {
            return;
        }
        if (DEBUG) {
            Log.d(TAG, String.format("ContiguousFIFOAggregator    first: %d", hash));
        }
        callback.onBecomeFirstExpected(key);
    
private java.lang.StringprettyPrint()
Get a pretty string representing the internal data.

return
a String for the internal data.

        if (mExpected.isEmpty()) {
            return "{}";
        }

        StringBuilder buffer = new StringBuilder(mExpected.size() * 28);
        buffer.append('{");
        Iterator<T> it = mExpected.iterator();
        while (it.hasNext()) {
            final T key = it.next();
            final int hash = key.hashCode();
            buffer.append(hash);
            buffer.append('=");
            final Value value = mTasks.get(hash);
            buffer.append(value);
            if (it.hasNext()) {
                buffer.append(", ");
            }
        }
        buffer.append('}");
        if (mExpected.size() != mTasks.size()) {
            buffer.append(" error");
        }
        return buffer.toString();