Reuse display lists at the java level.

Objects are invalidated and reset instead of being nulled out
and recreated. This avoids creating small amounts of garbage for
the display list and canvas objects.

Change-Id: I464fac7ea8944c19ad6d03f13a95d9017e3f4262
diff --git a/api/current.xml b/api/current.xml
index d4e6b05..fe5ea76 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -257911,7 +257911,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="arg0" type="T">
+<parameter name="t" type="T">
 </parameter>
 </method>
 </interface>
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java
index 636f547..8931450 100644
--- a/core/java/android/animation/LayoutTransition.java
+++ b/core/java/android/animation/LayoutTransition.java
@@ -73,19 +73,19 @@
 
     /**
      * A flag indicating the animation that runs on those items that are changing
-     * due to a new item disappearing from the container.
+     * due to an item disappearing from the container.
      */
     public static final int CHANGE_DISAPPEARING = 1;
 
     /**
-     * A flag indicating the animation that runs on those items that are changing
-     * due to a new item appearing in the container.
+     * A flag indicating the animation that runs on those items that are appearing
+     * in the container.
      */
     public static final int APPEARING = 2;
 
     /**
-     * A flag indicating the animation that runs on those items that are changing
-     * due to a new item appearing in the container.
+     * A flag indicating the animation that runs on those items that are disappearing
+     * from the container.
      */
     public static final int DISAPPEARING = 3;
 
diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java
index e7c2231..959fae4 100644
--- a/core/java/android/view/DisplayList.java
+++ b/core/java/android/view/DisplayList.java
@@ -46,4 +46,19 @@
      * @see android.view.HardwareCanvas#drawDisplayList(DisplayList) 
      */
     abstract boolean isReady();
+
+    /**
+     * Invalidates the display list, indicating that it should be repopulated
+     * with new drawing commands prior to being used again. Calling this method
+     * causes calls to {@link #isValid()} to return <code>false</code>.
+     */
+    abstract void invalidate();
+
+    /**
+     * Returns whether the display list is currently usable. If this returns false,
+     * the display list should be re-recorded prior to replaying it.
+     *
+     * @return boolean true if the display list is able to be replayed, false otherwise.
+     */
+    abstract boolean isValid();
 }
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index f0bb40d..67b22fa 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -80,6 +80,10 @@
     protected GLES20Canvas(boolean record, boolean translucent) {
         mOpaque = !translucent;
 
+        setupRenderer(record);
+    }
+
+    protected void setupRenderer(boolean record) {
         if (record) {
             mRenderer = nCreateDisplayListRenderer();
         } else {
@@ -89,7 +93,11 @@
         if (mRenderer == 0) {
             throw new IllegalStateException("Could not create GLES20Canvas renderer");
         } else {
-            mFinalizer = new CanvasFinalizer(mRenderer);
+            if (mFinalizer == null) {
+                mFinalizer = new CanvasFinalizer(mRenderer);
+            } else {
+                mFinalizer.replaceNativeObject(mRenderer);
+            }
         }
     }
 
@@ -99,15 +107,22 @@
     private static native void nDestroyRenderer(int renderer);
 
     private static class CanvasFinalizer {
-        final int mRenderer;
+        int mRenderer;
 
         CanvasFinalizer(int renderer) {
             mRenderer = renderer;
         }
 
+        void replaceNativeObject(int newRenderer) {
+            if (mRenderer != 0) {
+                nDestroyRenderer(mRenderer);
+            }
+            mRenderer = newRenderer;
+        }
+
         @Override
         protected void finalize() throws Throwable {
-            nDestroyRenderer(mRenderer);
+            replaceNativeObject(0);
         }
     }
 
diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java
index adf08ea..bd18fdd 100644
--- a/core/java/android/view/GLES20DisplayList.java
+++ b/core/java/android/view/GLES20DisplayList.java
@@ -24,6 +24,7 @@
 
     private boolean mStarted = false;
     private boolean mRecorded = false;
+    private boolean mValid = false;
 
     int mNativeDisplayList;
 
@@ -38,21 +39,42 @@
             throw new IllegalStateException("Recording has already started");
         }
 
-        mCanvas = new GLES20RecordingCanvas(true);
+        if (mCanvas != null) {
+            ((GLES20RecordingCanvas) mCanvas).reset();
+        } else {
+            mCanvas = new GLES20RecordingCanvas(true);
+        }
         mStarted = true;
         mRecorded = false;
+        mValid = true;
 
         return mCanvas;
     }
 
     @Override
+    void invalidate() {
+        mStarted = false;
+        mRecorded = false;
+        mValid = false;
+    }
+
+    @Override
+    boolean isValid() {
+        return mValid;
+    }
+
+    @Override
     void end() {
         if (mCanvas != null) {
             mStarted = false;
             mRecorded = true;
 
             mNativeDisplayList = mCanvas.getDisplayList();
-            mFinalizer = new DisplayListFinalizer(mNativeDisplayList);
+            if (mFinalizer == null) {
+                mFinalizer = new DisplayListFinalizer(mNativeDisplayList);
+            } else {
+                mFinalizer.replaceNativeObject(mNativeDisplayList);
+            }
         }
     }
 
@@ -68,9 +90,16 @@
             mNativeDisplayList = nativeDisplayList;
         }
 
+        void replaceNativeObject(int newNativeDisplayList) {
+            if (mNativeDisplayList != 0) {
+                GLES20Canvas.destroyDisplayList(mNativeDisplayList);
+            }
+            mNativeDisplayList = newNativeDisplayList;
+        }
+
         @Override
         protected void finalize() throws Throwable {
-            GLES20Canvas.destroyDisplayList(mNativeDisplayList);
+            replaceNativeObject(0);
         }
     }
 }
diff --git a/core/java/android/view/GLES20RecordingCanvas.java b/core/java/android/view/GLES20RecordingCanvas.java
index bd14286..7f86d99 100644
--- a/core/java/android/view/GLES20RecordingCanvas.java
+++ b/core/java/android/view/GLES20RecordingCanvas.java
@@ -51,6 +51,11 @@
         }
     }
 
+    void reset() {
+        mBitmaps.clear();
+        setupRenderer(true);
+    }
+
     @Override
     public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,
             Paint paint) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 48f9e81..3cacbe3 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7783,9 +7783,12 @@
         }
 
         if ((mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED &&
-                ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || mDisplayList == null)) {
+                ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 ||
+                        mDisplayList == null || !mDisplayList.isValid())) {
 
-            mDisplayList = mAttachInfo.mHardwareRenderer.createDisplayList();
+            if (mDisplayList == null) {
+                mDisplayList = mAttachInfo.mHardwareRenderer.createDisplayList();
+            }
 
             final HardwareCanvas canvas = mDisplayList.start();
             try {
@@ -7886,7 +7889,7 @@
             mUnscaledDrawingCache = null;
         }
         if (mDisplayList != null) {
-            mDisplayList = null;
+            mDisplayList.invalidate();
         }
     }