Choreographer.java revision 968588573cf516fd8cf33c133d06f06c67ca1785
196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown/* 296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * Copyright (C) 2011 The Android Open Source Project 396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * 496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * Licensed under the Apache License, Version 2.0 (the "License"); 596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * you may not use this file except in compliance with the License. 696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * You may obtain a copy of the License at 796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * 896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * http://www.apache.org/licenses/LICENSE-2.0 996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * 1096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * Unless required by applicable law or agreed to in writing, software 1196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * distributed under the License is distributed on an "AS IS" BASIS, 1296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * See the License for the specific language governing permissions and 1496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * limitations under the License. 1596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown */ 1696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 1796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brownpackage android.view; 1896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 1996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brownimport com.android.internal.util.ArrayUtils; 2096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 2196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brownimport android.os.Handler; 2296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brownimport android.os.Looper; 2396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brownimport android.os.Message; 2496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brownimport android.os.SystemClock; 2596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brownimport android.os.SystemProperties; 2696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brownimport android.util.Log; 2796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 2896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown/** 298bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn * Coordinates animations and drawing for UI on a particular thread. 3087d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown * 3187d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown * This object is thread-safe. Other threads can add and remove listeners 3287d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown * or schedule work to occur at a later time on the UI thread. 3387d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown * 3458aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown * Ensuring thread-safety is a little tricky because the {@link DisplayEventReceiver} 3558aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown * can only be accessed from the UI thread so operations that touch the event receiver 3658aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown * are posted to the UI thread if needed. 3758aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown * 3896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * @hide 3996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown */ 40968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brownpublic final class Choreographer { 4196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown private static final String TAG = "Choreographer"; 4296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown private static final boolean DEBUG = false; 4396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 4458aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown // Amount of time in ms to wait before actually disposing of the display event 4558aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown // receiver after all listeners have been removed. 4658aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown private static final long DISPOSE_RECEIVER_DELAY = 200; 4758aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown 4896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown // The default amount of time in ms between animation frames. 4996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown // When vsync is not enabled, we want to have some idea of how long we should 5096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown // wait before posting the next animation message. It is important that the 5196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown // default value be less than the true inter-frame delay on all devices to avoid 5296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown // situations where we might skip frames by waiting too long (we must compensate 5396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown // for jitter and hardware variations). Regardless of this value, the animation 5496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown // and display loop is ultimately rate-limited by how fast new graphics buffers can 5596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown // be dequeued. 5696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown private static final long DEFAULT_FRAME_DELAY = 10; 5796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 5896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown // The number of milliseconds between animation frames. 5987d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown private static volatile long sFrameDelay = DEFAULT_FRAME_DELAY; 6096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 6196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown // Thread local storage for the choreographer. 6296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown private static final ThreadLocal<Choreographer> sThreadInstance = 6396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown new ThreadLocal<Choreographer>() { 6496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown @Override 6596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown protected Choreographer initialValue() { 6696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown Looper looper = Looper.myLooper(); 6796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown if (looper == null) { 6896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown throw new IllegalStateException("The current thread must have a looper!"); 6996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 7096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown return new Choreographer(looper); 7196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 7296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown }; 7396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 7496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown // System property to enable/disable vsync for animations and drawing. 7596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown // Enabled by default. 7696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown private static final boolean USE_VSYNC = SystemProperties.getBoolean( 7796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown "debug.choreographer.vsync", true); 7896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 7996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown // System property to enable/disable the use of the vsync / animation timer 8096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown // for drawing rather than drawing immediately. 8190a3c5f51ddf0f98769a258ed224f2ec5b645d0eJeff Brown // Temporarily disabled by default because postponing performTraversals() violates 8290a3c5f51ddf0f98769a258ed224f2ec5b645d0eJeff Brown // assumptions about traversals happening in-order relative to other posted messages. 8390a3c5f51ddf0f98769a258ed224f2ec5b645d0eJeff Brown // Bug: 5721047 8496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown private static final boolean USE_ANIMATION_TIMER_FOR_DRAW = SystemProperties.getBoolean( 8590a3c5f51ddf0f98769a258ed224f2ec5b645d0eJeff Brown "debug.choreographer.animdraw", false); 8696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 8796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown private static final int MSG_DO_ANIMATION = 0; 8896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown private static final int MSG_DO_DRAW = 1; 8958aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown private static final int MSG_DO_SCHEDULE_VSYNC = 2; 9058aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown private static final int MSG_DO_DISPOSE_RECEIVER = 3; 9196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 9287d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown private final Object mLock = new Object(); 9387d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown 9496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown private final Looper mLooper; 95968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown private final FrameHandler mHandler; 96968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 97968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown private Callback mCallbackPool; 9896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 9996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown private OnAnimateListener[] mOnAnimateListeners; 10096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown private OnDrawListener[] mOnDrawListeners; 10196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 102968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown private Callback mOnAnimateCallbacks; 103968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown private Callback mOnDrawCallbacks; 104968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 10596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown private boolean mAnimationScheduled; 10696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown private boolean mDrawScheduled; 10758aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown private boolean mFrameDisplayEventReceiverNeeded; 10896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown private FrameDisplayEventReceiver mFrameDisplayEventReceiver; 10996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown private long mLastAnimationTime; 11096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown private long mLastDrawTime; 11196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 11296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown private Choreographer(Looper looper) { 11396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown mLooper = looper; 114968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mHandler = new FrameHandler(looper); 11596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown mLastAnimationTime = Long.MIN_VALUE; 11696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown mLastDrawTime = Long.MIN_VALUE; 11796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 11896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 11996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown /** 1208bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn * Gets the choreographer for the calling thread. Must be called from 1218bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn * a thread that already has a {@link android.os.Looper} associated with it. 12296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * 12396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * @return The choreographer for this thread. 12496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * @throws IllegalStateException if the thread does not have a looper. 12596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown */ 12696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown public static Choreographer getInstance() { 12796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown return sThreadInstance.get(); 12896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 12996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 13096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown /** 13196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * The amount of time, in milliseconds, between each frame of the animation. This is a 13296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * requested time that the animation will attempt to honor, but the actual delay between 13396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * frames may be different, depending on system load and capabilities. This is a static 13496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * function because the same delay will be applied to all animations, since they are all 13596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * run off of a single timing loop. 13696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * 13796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * The frame delay may be ignored when the animation system uses an external timing 13896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * source, such as the display refresh rate (vsync), to govern animations. 13996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * 14096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * @return the requested time between frames, in milliseconds 14196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown */ 14296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown public static long getFrameDelay() { 14396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown return sFrameDelay; 14496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 14596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 14696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown /** 14796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * The amount of time, in milliseconds, between each frame of the animation. This is a 14896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * requested time that the animation will attempt to honor, but the actual delay between 14996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * frames may be different, depending on system load and capabilities. This is a static 15096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * function because the same delay will be applied to all animations, since they are all 15196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * run off of a single timing loop. 15296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * 15396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * The frame delay may be ignored when the animation system uses an external timing 15496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * source, such as the display refresh rate (vsync), to govern animations. 15596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * 15696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * @param frameDelay the requested time between frames, in milliseconds 15796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown */ 15896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown public static void setFrameDelay(long frameDelay) { 15996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown sFrameDelay = frameDelay; 16096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 16196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 16296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown /** 16396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * Schedules animation (and drawing) to occur on the next frame synchronization boundary. 16496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown */ 16596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown public void scheduleAnimation() { 16687d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown synchronized (mLock) { 167968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown scheduleAnimationLocked(false); 16887d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown } 16987d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown } 17087d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown 171968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown private void scheduleAnimationLocked(boolean force) { 172968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown if (!mAnimationScheduled 173968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown && (force || mOnAnimateListeners != null || mOnAnimateCallbacks != null)) { 17496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown mAnimationScheduled = true; 17596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown if (USE_VSYNC) { 17696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown if (DEBUG) { 17796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown Log.d(TAG, "Scheduling vsync for animation."); 17896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 17958aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown 18058aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown // If running on the Looper thread, then schedule the vsync immediately, 18158aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown // otherwise post a message to schedule the vsync from the UI thread 18258aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown // as soon as possible. 18358aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown if (!mFrameDisplayEventReceiverNeeded) { 18458aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown mFrameDisplayEventReceiverNeeded = true; 18558aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown if (mFrameDisplayEventReceiver != null) { 186968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mHandler.removeMessages(MSG_DO_DISPOSE_RECEIVER); 18758aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown } 18858aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown } 18958aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown if (isRunningOnLooperThreadLocked()) { 19058aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown doScheduleVsyncLocked(); 19158aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown } else { 192968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mHandler.sendMessageAtFrontOfQueue( 193968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC)); 19496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 19596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } else { 19696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown final long now = SystemClock.uptimeMillis(); 19796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown final long nextAnimationTime = Math.max(mLastAnimationTime + sFrameDelay, now); 19896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown if (DEBUG) { 19996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown Log.d(TAG, "Scheduling animation in " + (nextAnimationTime - now) + " ms."); 20096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 201968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mHandler.sendEmptyMessageAtTime(MSG_DO_ANIMATION, nextAnimationTime); 20296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 20396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 20496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 20596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 20696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown /** 20787d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown * Returns true if {@link #scheduleAnimation()} has been called but 2088bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn * {@link OnAnimateListener#onAnimate() OnAnimateListener.onAnimate()} has 2098bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn * not yet been called. 2108bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn */ 2118bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn public boolean isAnimationScheduled() { 21287d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown synchronized (mLock) { 21387d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown return mAnimationScheduled; 21487d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown } 2158bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn } 2168bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn 2178bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn /** 21896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * Schedules drawing to occur on the next frame synchronization boundary. 21996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * Must be called on the UI thread. 22096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown */ 22196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown public void scheduleDraw() { 22287d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown synchronized (mLock) { 223968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown scheduleDrawLocked(); 224968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 225968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 226968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 227968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown private void scheduleDrawLocked() { 228968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown if (!mDrawScheduled 229968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown && (mOnDrawListeners != null || mOnDrawCallbacks != null)) { 230968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mDrawScheduled = true; 231968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown if (USE_ANIMATION_TIMER_FOR_DRAW) { 232968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown scheduleAnimationLocked(true); 233968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } else { 234968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown if (DEBUG) { 235968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown Log.d(TAG, "Scheduling draw immediately."); 23696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 237968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mHandler.sendEmptyMessage(MSG_DO_DRAW); 23896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 23996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 24096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 24196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 2428bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn /** 24387d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown * Returns true if {@link #scheduleDraw()} has been called but 2448bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn * {@link OnDrawListener#onDraw() OnDrawListener.onDraw()} has 2458bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn * not yet been called. 2468bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn */ 2478bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn public boolean isDrawScheduled() { 24887d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown synchronized (mLock) { 24987d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown return mDrawScheduled; 25087d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown } 2518bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn } 2528bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn 253968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown /** 254968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * Adds an animation listener. 255968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * 256968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * @param listener The listener to add. 257968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown */ 258968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown public void addOnAnimateListener(OnAnimateListener listener) { 259968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown if (listener == null) { 260968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown throw new IllegalArgumentException("listener must not be null"); 261968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 262968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 263968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown if (DEBUG) { 264968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown Log.d(TAG, "Adding onAnimate listener: " + listener); 265968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 266968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 267968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown synchronized (mLock) { 268968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mOnAnimateListeners = ArrayUtils.appendElement(OnAnimateListener.class, 269968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mOnAnimateListeners, listener); 270968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 271968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 272968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 273968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown /** 274968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * Removes an animation listener. 275968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * 276968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * @param listener The listener to remove. 277968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown */ 278968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown public void removeOnAnimateListener(OnAnimateListener listener) { 279968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown if (listener == null) { 280968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown throw new IllegalArgumentException("listener must not be null"); 281968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 282968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 283968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown if (DEBUG) { 284968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown Log.d(TAG, "Removing onAnimate listener: " + listener); 285968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 286968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 287968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown synchronized (mLock) { 288968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mOnAnimateListeners = ArrayUtils.removeElement(OnAnimateListener.class, 289968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mOnAnimateListeners, listener); 290968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown stopTimingLoopIfNoListenersOrCallbacksLocked(); 291968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 292968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 293968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 294968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown /** 295968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * Adds a draw listener. 296968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * 297968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * @param listener The listener to add. 298968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown */ 299968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown public void addOnDrawListener(OnDrawListener listener) { 300968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown if (listener == null) { 301968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown throw new IllegalArgumentException("listener must not be null"); 302968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 303968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 304968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown if (DEBUG) { 305968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown Log.d(TAG, "Adding onDraw listener: " + listener); 306968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 307968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 308968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown synchronized (mLock) { 309968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mOnDrawListeners = ArrayUtils.appendElement(OnDrawListener.class, 310968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mOnDrawListeners, listener); 311968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 312968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 313968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 314968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown /** 315968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * Removes a draw listener. 316968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * Must be called on the UI thread. 317968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * 318968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * @param listener The listener to remove. 319968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown */ 320968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown public void removeOnDrawListener(OnDrawListener listener) { 321968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown if (listener == null) { 322968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown throw new IllegalArgumentException("listener must not be null"); 323968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 324968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 325968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown if (DEBUG) { 326968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown Log.d(TAG, "Removing onDraw listener: " + listener); 327968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 328968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 329968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown synchronized (mLock) { 330968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mOnDrawListeners = ArrayUtils.removeElement(OnDrawListener.class, 331968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mOnDrawListeners, listener); 332968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown stopTimingLoopIfNoListenersOrCallbacksLocked(); 333968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 334968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 335968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 336968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 337968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown /** 338968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * Posts a callback to run on the next animation cycle and schedules an animation cycle. 339968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * The callback only runs once and then is automatically removed. 340968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * 341968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * @param runnable The callback to run during the next animation cycle. 342968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * 343968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * @see #removeOnAnimateCallback 344968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown */ 345968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown public void postOnAnimateCallback(Runnable runnable) { 346968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown if (runnable == null) { 347968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown throw new IllegalArgumentException("runnable must not be null"); 348968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 349968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown synchronized (mLock) { 350968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mOnAnimateCallbacks = addCallbackLocked(mOnAnimateCallbacks, runnable); 351968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown scheduleAnimationLocked(false); 352968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 353968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 354968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 355968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown /** 356968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * Removes an animation callback. 357968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * Does nothing if the specified animation callback has not been posted or has already 358968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * been removed. 359968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * 360968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * @param runnable The animation callback to remove. 361968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * 362968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * @see #postOnAnimateCallback 363968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown */ 364968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown public void removeOnAnimateCallback(Runnable runnable) { 365968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown if (runnable == null) { 366968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown throw new IllegalArgumentException("runnable must not be null"); 367968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 368968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown synchronized (mLock) { 369968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mOnAnimateCallbacks = removeCallbackLocked(mOnAnimateCallbacks, runnable); 370968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown stopTimingLoopIfNoListenersOrCallbacksLocked(); 371968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 372968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 373968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 374968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown /** 375968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * Posts a callback to run on the next draw cycle and schedules a draw cycle. 376968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * The callback only runs once and then is automatically removed. 377968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * 378968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * @param runnable The callback to run during the next draw cycle. 379968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * 380968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * @see #removeOnDrawCallback 381968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown */ 382968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown public void postOnDrawCallback(Runnable runnable) { 383968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown if (runnable == null) { 384968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown throw new IllegalArgumentException("runnable must not be null"); 385968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 386968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown synchronized (mLock) { 387968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mOnDrawCallbacks = addCallbackLocked(mOnDrawCallbacks, runnable); 388968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown scheduleDrawLocked(); 389968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 390968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 391968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 392968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown /** 393968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * Removes a draw callback. 394968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * Does nothing if the specified draw callback has not been posted or has already 395968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * been removed. 396968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * 397968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * @param runnable The draw callback to remove. 398968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * 399968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown * @see #postOnDrawCallback 400968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown */ 401968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown public void removeOnDrawCallback(Runnable runnable) { 402968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown if (runnable == null) { 403968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown throw new IllegalArgumentException("runnable must not be null"); 404968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 405968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown synchronized (mLock) { 406968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mOnDrawCallbacks = removeCallbackLocked(mOnDrawCallbacks, runnable); 407968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown stopTimingLoopIfNoListenersOrCallbacksLocked(); 40896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 40996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 41096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 411968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown void doAnimation() { 41287d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown doAnimationInner(); 41387d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown 41487d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown if (USE_ANIMATION_TIMER_FOR_DRAW) { 41587d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown doDraw(); 41687d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown } 41787d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown } 41887d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown 419968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown void doAnimationInner() { 42087d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown final long start; 42187d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown final OnAnimateListener[] listeners; 422968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown final Callback callbacks; 42387d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown synchronized (mLock) { 42487d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown if (!mAnimationScheduled) { 42587d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown return; // no work to do 42687d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown } 42796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown mAnimationScheduled = false; 42896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 42987d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown start = SystemClock.uptimeMillis(); 43096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown if (DEBUG) { 43196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown Log.d(TAG, "Performing animation: " + Math.max(0, start - mLastAnimationTime) 43296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown + " ms have elapsed since previous animation."); 43396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 43496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown mLastAnimationTime = start; 43596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 43687d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown listeners = mOnAnimateListeners; 437968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown callbacks = mOnAnimateCallbacks; 438968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mOnAnimateCallbacks = null; 43987d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown } 44096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 44187d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown if (listeners != null) { 44287d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown for (int i = 0; i < listeners.length; i++) { 44387d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown listeners[i].onAnimate(); 44496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 44596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 44696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 447968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown if (callbacks != null) { 448968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown runCallbacks(callbacks); 449968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown synchronized (mLock) { 450968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown recycleCallbacksLocked(callbacks); 451968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 452968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 453968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 45487d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown if (DEBUG) { 45587d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown Log.d(TAG, "Animation took " + (SystemClock.uptimeMillis() - start) + " ms."); 45696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 45796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 45896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 459968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown void doDraw() { 46087d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown final long start; 46187d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown final OnDrawListener[] listeners; 462968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown final Callback callbacks; 46387d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown synchronized (mLock) { 46487d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown if (!mDrawScheduled) { 46587d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown return; // no work to do 46687d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown } 46796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown mDrawScheduled = false; 46896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 46987d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown start = SystemClock.uptimeMillis(); 47096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown if (DEBUG) { 47196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown Log.d(TAG, "Performing draw: " + Math.max(0, start - mLastDrawTime) 47296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown + " ms have elapsed since previous draw."); 47396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 47496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown mLastDrawTime = start; 47596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 47687d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown listeners = mOnDrawListeners; 477968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown callbacks = mOnDrawCallbacks; 478968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mOnDrawCallbacks = null; 47987d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown } 48096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 48187d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown if (listeners != null) { 48287d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown for (int i = 0; i < listeners.length; i++) { 48387d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown listeners[i].onDraw(); 48496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 48596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 48687d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown 487968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown if (callbacks != null) { 488968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown runCallbacks(callbacks); 489968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown synchronized (mLock) { 490968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown recycleCallbacksLocked(callbacks); 491968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 492968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 493968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 49487d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown if (DEBUG) { 49587d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown Log.d(TAG, "Draw took " + (SystemClock.uptimeMillis() - start) + " ms."); 49687d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown } 49796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 49896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 499968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown void doScheduleVsync() { 50058aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown synchronized (mLock) { 50158aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown doScheduleVsyncLocked(); 50258aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown } 50358aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown } 50458aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown 50558aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown private void doScheduleVsyncLocked() { 50658aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown if (mFrameDisplayEventReceiverNeeded && mAnimationScheduled) { 50758aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown if (mFrameDisplayEventReceiver == null) { 50858aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown mFrameDisplayEventReceiver = new FrameDisplayEventReceiver(mLooper); 50958aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown } 51058aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown mFrameDisplayEventReceiver.scheduleVsync(); 51158aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown } 51258aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown } 51358aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown 514968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown void doDisposeReceiver() { 51558aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown synchronized (mLock) { 51658aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown if (!mFrameDisplayEventReceiverNeeded && mFrameDisplayEventReceiver != null) { 51758aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown mFrameDisplayEventReceiver.dispose(); 51858aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown mFrameDisplayEventReceiver = null; 51958aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown } 52058aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown } 52158aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown } 52258aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown 523968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown private void stopTimingLoopIfNoListenersOrCallbacksLocked() { 524968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown if (mOnAnimateListeners == null && mOnDrawListeners == null 525968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown && mOnAnimateCallbacks == null && mOnDrawCallbacks == null) { 52696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown if (DEBUG) { 52796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown Log.d(TAG, "Stopping timing loop."); 52896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 52996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 53096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown if (mAnimationScheduled) { 53196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown mAnimationScheduled = false; 53258aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown if (USE_VSYNC) { 533968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mHandler.removeMessages(MSG_DO_SCHEDULE_VSYNC); 53458aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown } else { 535968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mHandler.removeMessages(MSG_DO_ANIMATION); 53696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 53796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 53896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 53996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown if (mDrawScheduled) { 54096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown mDrawScheduled = false; 54196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown if (!USE_ANIMATION_TIMER_FOR_DRAW) { 542968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mHandler.removeMessages(MSG_DO_DRAW); 54396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 54496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 54596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 54658aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown // Post a message to dispose the display event receiver if we haven't needed 54758aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown // it again after a certain amount of time has elapsed. Another reason to 54858aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown // defer disposal is that it is possible for use to attempt to dispose the 54958aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown // receiver while handling a vsync event that it dispatched, which might 55058aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown // cause a few problems... 55158aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown if (mFrameDisplayEventReceiverNeeded) { 55258aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown mFrameDisplayEventReceiverNeeded = false; 55358aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown if (mFrameDisplayEventReceiver != null) { 554968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mHandler.sendEmptyMessageDelayed(MSG_DO_DISPOSE_RECEIVER, 555968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown DISPOSE_RECEIVER_DELAY); 55658aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown } 55796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 55896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 55996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 56096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 56158aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown private boolean isRunningOnLooperThreadLocked() { 56258aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown return Looper.myLooper() == mLooper; 56358aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown } 56458aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown 565968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown private Callback addCallbackLocked(Callback head, Runnable runnable) { 566968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown Callback callback = obtainCallbackLocked(runnable); 567968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown if (head == null) { 568968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown return callback; 569968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 570968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown Callback tail = head; 571968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown while (tail.next != null) { 572968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown tail = tail.next; 573968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 574968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown tail.next = callback; 575968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown return head; 576968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 577968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 578968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown private Callback removeCallbackLocked(Callback head, Runnable runnable) { 579968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown Callback predecessor = null; 580968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown for (Callback callback = head; callback != null;) { 581968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown final Callback next = callback.next; 582968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown if (callback.runnable == runnable) { 583968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown if (predecessor != null) { 584968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown predecessor.next = next; 585968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } else { 586968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown head = next; 587968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 588968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown recycleCallbackLocked(callback); 589968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } else { 590968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown predecessor = callback; 591968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 592968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown callback = next; 593968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 594968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown return head; 595968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 596968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 597968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown private void runCallbacks(Callback head) { 598968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown while (head != null) { 599968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown head.runnable.run(); 600968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown head = head.next; 601968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 602968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 603968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 604968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown private void recycleCallbacksLocked(Callback head) { 605968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown while (head != null) { 606968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown final Callback next = head.next; 607968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown recycleCallbackLocked(head); 608968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown head = next; 609968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 610968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 611968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 612968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown private Callback obtainCallbackLocked(Runnable runnable) { 613968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown Callback callback = mCallbackPool; 614968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown if (callback == null) { 615968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown callback = new Callback(); 616968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } else { 617968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mCallbackPool = callback.next; 618968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown callback.next = null; 619968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 620968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown callback.runnable = runnable; 621968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown return callback; 622968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 623968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 624968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown private void recycleCallbackLocked(Callback callback) { 625968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown callback.runnable = null; 626968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown callback.next = mCallbackPool; 627968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown mCallbackPool = callback; 628968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 629968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 63096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown /** 63196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * Listens for animation frame timing events. 63296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown */ 63396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown public static interface OnAnimateListener { 63496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown /** 63596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * Called to animate properties before drawing the frame. 63696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown */ 63796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown public void onAnimate(); 63896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 63996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 64096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown /** 64196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * Listens for draw frame timing events. 64296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown */ 64396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown public static interface OnDrawListener { 64496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown /** 64596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * Called to draw the frame. 64696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown */ 64796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown public void onDraw(); 64896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 64996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 650968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown private final class FrameHandler extends Handler { 651968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown public FrameHandler(Looper looper) { 652968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown super(looper); 653968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 654968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 655968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown @Override 656968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown public void handleMessage(Message msg) { 657968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown switch (msg.what) { 658968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown case MSG_DO_ANIMATION: 659968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown doAnimation(); 660968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown break; 661968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown case MSG_DO_DRAW: 662968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown doDraw(); 663968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown break; 664968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown case MSG_DO_SCHEDULE_VSYNC: 665968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown doScheduleVsync(); 666968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown break; 667968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown case MSG_DO_DISPOSE_RECEIVER: 668968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown doDisposeReceiver(); 669968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown break; 670968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 671968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 672968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 673968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 67496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown private final class FrameDisplayEventReceiver extends DisplayEventReceiver { 67596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown public FrameDisplayEventReceiver(Looper looper) { 67696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown super(looper); 67796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 67896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown 67996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown @Override 68096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown public void onVsync(long timestampNanos, int frame) { 68196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown doAnimation(); 68296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 68396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown } 684968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown 685968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown private static final class Callback { 686968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown public Callback next; 687968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown public Runnable runnable; 688968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown } 68996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown} 690