Loading core/java/android/view/GLRenderer.java +62 −8 Original line number Diff line number Diff line Loading @@ -635,11 +635,11 @@ public class GLRenderer extends HardwareRenderer { GLRenderer(boolean translucent) { mTranslucent = translucent; loadSystemProperties(null); loadSystemProperties(); } @Override boolean loadSystemProperties(Surface surface) { boolean loadSystemProperties() { boolean value; boolean changed = false; Loading Loading @@ -1101,11 +1101,6 @@ public class GLRenderer extends HardwareRenderer { return mHeight; } @Override HardwareCanvas getCanvas() { return mCanvas; } @Override void setName(String name) { mName = name; Loading @@ -1128,6 +1123,66 @@ public class GLRenderer extends HardwareRenderer { } } @Override void drawDisplayList(DisplayList displayList, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty) { if (canDraw()) { if (!hasDirtyRegions()) { dirty = null; } // We are already on the correct thread final int surfaceState = checkRenderContextUnsafe(); if (surfaceState != SURFACE_STATE_ERROR) { HardwareCanvas canvas = mCanvas; if (mProfileEnabled) { mProfileLock.lock(); } dirty = beginFrame(canvas, dirty, surfaceState); int saveCount = 0; int status = DisplayList.STATUS_DONE; long start = getSystemTime(); try { status = prepareFrame(dirty); saveCount = canvas.save(); callbacks.onHardwarePreDraw(canvas); status |= drawDisplayList(attachInfo, canvas, displayList, status); } catch (Exception e) { Log.e(LOG_TAG, "An error has occurred while drawing:", e); } finally { callbacks.onHardwarePostDraw(canvas); canvas.restoreToCount(saveCount); mDrawDelta = getSystemTime() - start; if (mDrawDelta > 0) { mFrameCount++; debugOverdraw(attachInfo, dirty, canvas, displayList); debugDirtyRegions(dirty, canvas); drawProfileData(attachInfo); } } onPostDraw(); swapBuffers(status); if (mProfileEnabled) { mProfileLock.unlock(); } attachInfo.mIgnoreDirtyState = false; } } } @Override void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty) { Loading @@ -1144,7 +1199,6 @@ public class GLRenderer extends HardwareRenderer { final int surfaceState = checkRenderContextUnsafe(); if (surfaceState != SURFACE_STATE_ERROR) { HardwareCanvas canvas = mCanvas; attachInfo.mHardwareCanvas = canvas; if (mProfileEnabled) { mProfileLock.lock(); Loading core/java/android/view/HardwareRenderer.java +22 −10 Original line number Diff line number Diff line Loading @@ -188,6 +188,9 @@ public abstract class HardwareRenderer { */ public static boolean sSystemRendererDisabled = false; /** @hide */ public static boolean sUseRenderThread = false; private boolean mEnabled; private boolean mRequested = true; Loading Loading @@ -305,13 +308,6 @@ public abstract class HardwareRenderer { */ abstract int getHeight(); /** * Gets the current canvas associated with this HardwareRenderer. * * @return the current HardwareCanvas */ abstract HardwareCanvas getCanvas(); /** * Outputs extra debugging information in the specified file descriptor. * @param pw Loading @@ -335,7 +331,7 @@ public abstract class HardwareRenderer { * * @return True if a property has changed. */ abstract boolean loadSystemProperties(Surface surface); abstract boolean loadSystemProperties(); /** * Sets the directory to use as a persistent storage for hardware rendering Loading Loading @@ -411,6 +407,18 @@ public abstract class HardwareRenderer { abstract void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty); /** * Temporary hook to draw a display list directly, only used if sUseRenderThread * is true. * * @param displayList The display list to draw * @param attachInfo AttachInfo tied to the specified view. * @param callbacks Callbacks invoked when drawing happens. * @param dirty The dirty rectangle to update, can be null. */ abstract void drawDisplayList(DisplayList displayList, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty); /** * Creates a new hardware layer. A hardware layer built by calling this * method will be treated as a texture layer, instead of as a render target. Loading Loading @@ -517,10 +525,14 @@ public abstract class HardwareRenderer { * @return A hardware renderer backed by OpenGL. */ static HardwareRenderer create(boolean translucent) { HardwareRenderer renderer = null; if (GLES20Canvas.isAvailable()) { return new GLRenderer(translucent); renderer = new GLRenderer(translucent); } if (renderer != null && sUseRenderThread) { renderer = new ThreadedRenderer((GLRenderer)renderer); } return null; return renderer; } /** Loading core/java/android/view/ThreadedRenderer.java 0 → 100644 +317 −0 Original line number Diff line number Diff line /* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.view; import android.graphics.Rect; import android.graphics.SurfaceTexture; import android.os.Looper; import android.os.SystemClock; import android.os.Trace; import android.util.Log; import android.view.Surface.OutOfResourcesException; import android.view.View.AttachInfo; import java.io.PrintWriter; import java.lang.reflect.Method; import java.util.HashMap; /** * Hardware renderer that proxies the rendering to a render thread. Most calls * are synchronous, however a few such as draw() are posted async. The display list * is shared between the two threads and is guarded by a top level lock. * * The UI thread can block on the RenderThread, but RenderThread must never * block on the UI thread. * * Note that although currently the EGL context & surfaces are created & managed * by the render thread, the goal is to move that into a shared structure that can * be managed by both threads. EGLSurface creation & deletion should ideally be * done on the UI thread and not the RenderThread to avoid stalling the * RenderThread with surface buffer allocation. * * @hide */ public class ThreadedRenderer extends HardwareRenderer { private static final String LOGTAG = "ThreadedRenderer"; @SuppressWarnings("serial") static HashMap<String, Method> sMethodLut = new HashMap<String, Method>() {{ Method[] methods = HardwareRenderer.class.getDeclaredMethods(); for (Method m : methods) { put(m.getName(), m); } }}; static boolean sNeedsInit = true; private HardwareRenderer mRemoteRenderer; private int mWidth, mHeight; private RTJob mPreviousDraw; ThreadedRenderer(GLRenderer backingRenderer) { mRemoteRenderer = backingRenderer; setEnabled(true); if (sNeedsInit) { sNeedsInit = false; postToRenderThread(new Runnable() { @Override public void run() { // Hack to allow GLRenderer to create a handler to post the EGL // destruction to, although it'll never run Looper.prepare(); } }); } } @Override void destroy(boolean full) { run("destroy", full); } @Override boolean initialize(Surface surface) throws OutOfResourcesException { return (Boolean) run("initialize", surface); } @Override void updateSurface(Surface surface) throws OutOfResourcesException { post("updateSurface", surface); } @Override void destroyLayers(View view) { throw new NoSuchMethodError(); } @Override void destroyHardwareResources(View view) { run("destroyHardwareResources", view); } @Override void invalidate(Surface surface) { post("invalidate", surface); } @Override boolean validate() { // TODO Remove users of this API return false; } @Override boolean safelyRun(Runnable action) { return (Boolean) run("safelyRun", action); } @Override void setup(int width, int height) { mWidth = width; mHeight = height; post("setup", width, height); } @Override int getWidth() { return mWidth; } @Override int getHeight() { return mHeight; } @Override void dumpGfxInfo(PrintWriter pw) { // TODO Auto-generated method stub } @Override long getFrameCount() { // TODO Auto-generated method stub return 0; } @Override boolean loadSystemProperties() { return (Boolean) run("loadSystemProperties"); } @Override void pushLayerUpdate(HardwareLayer layer) { throw new NoSuchMethodError(); } @Override void cancelLayerUpdate(HardwareLayer layer) { throw new NoSuchMethodError(); } @Override void flushLayerUpdates() { throw new NoSuchMethodError(); } @Override void drawDisplayList(DisplayList displayList, AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty) { throw new NoSuchMethodError(); } /** * TODO: Remove * Temporary hack to allow RenderThreadTest prototype app to trigger * replaying a DisplayList after modifying the displaylist properties * * @hide */ public void repeatLastDraw() { if (mPreviousDraw == null) { throw new IllegalStateException("There isn't a previous draw"); } synchronized (mPreviousDraw) { mPreviousDraw.completed = false; } mPreviousDraw.args[3] = null; postToRenderThread(mPreviousDraw); } @Override void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty) { requireCompletion(mPreviousDraw); attachInfo.mIgnoreDirtyState = true; attachInfo.mDrawingTime = SystemClock.uptimeMillis(); view.mPrivateFlags |= View.PFLAG_DRAWN; view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED) == View.PFLAG_INVALIDATED; view.mPrivateFlags &= ~View.PFLAG_INVALIDATED; Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList"); DisplayList displayList = view.getDisplayList(); Trace.traceEnd(Trace.TRACE_TAG_VIEW); view.mRecreateDisplayList = false; mPreviousDraw = post("drawDisplayList", displayList, attachInfo, callbacks, dirty); } @Override HardwareLayer createHardwareLayer(boolean isOpaque) { throw new NoSuchMethodError(); } @Override HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) { throw new NoSuchMethodError(); } @Override SurfaceTexture createSurfaceTexture(HardwareLayer layer) { throw new NoSuchMethodError(); } @Override void setSurfaceTexture(HardwareLayer layer, SurfaceTexture surfaceTexture) { throw new NoSuchMethodError(); } @Override void detachFunctor(int functor) { throw new NoSuchMethodError(); } @Override boolean attachFunctor(AttachInfo attachInfo, int functor) { throw new NoSuchMethodError(); } @Override void setName(String name) { post("setName", name); } private static void requireCompletion(RTJob job) { if (job != null) { synchronized (job) { if (!job.completed) { try { job.wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } } } } } private RTJob post(String method, Object... args) { RTJob job = new RTJob(); job.method = sMethodLut.get(method); job.args = args; job.target = mRemoteRenderer; if (job.method == null) { throw new NullPointerException("Couldn't find method: " + method); } postToRenderThread(job); return job; } private Object run(String method, Object... args) { RTJob job = new RTJob(); job.method = sMethodLut.get(method); job.args = args; job.target = mRemoteRenderer; if (job.method == null) { throw new NullPointerException("Couldn't find method: " + method); } synchronized (job) { postToRenderThread(job); try { job.wait(); return job.ret; } catch (InterruptedException e) { throw new RuntimeException(e); } } } static class RTJob implements Runnable { Method method; Object[] args; Object target; Object ret; boolean completed = false; @Override public void run() { try { ret = method.invoke(target, args); synchronized (this) { completed = true; notify(); } } catch (Exception e) { Log.e(LOGTAG, "Failed to invoke: " + method.getName(), e); } } } /** @hide */ public static native void postToRenderThread(Runnable runnable); } core/java/android/view/View.java +7 −2 Original line number Diff line number Diff line Loading @@ -11234,6 +11234,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return null; } /** * @hide */ public HardwareRenderer getHardwareRenderer() { return mAttachInfo != null ? mAttachInfo.mHardwareRenderer : null; } /** * <p>Causes the Runnable to be added to the message queue. * The runnable will be run on the user interface thread.</p> Loading Loading @@ -18869,8 +18876,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final Callbacks mRootCallbacks; HardwareCanvas mHardwareCanvas; IWindowId mIWindowId; WindowId mWindowId; core/java/android/view/ViewRootImpl.java +115 −109 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/android/view/GLRenderer.java +62 −8 Original line number Diff line number Diff line Loading @@ -635,11 +635,11 @@ public class GLRenderer extends HardwareRenderer { GLRenderer(boolean translucent) { mTranslucent = translucent; loadSystemProperties(null); loadSystemProperties(); } @Override boolean loadSystemProperties(Surface surface) { boolean loadSystemProperties() { boolean value; boolean changed = false; Loading Loading @@ -1101,11 +1101,6 @@ public class GLRenderer extends HardwareRenderer { return mHeight; } @Override HardwareCanvas getCanvas() { return mCanvas; } @Override void setName(String name) { mName = name; Loading @@ -1128,6 +1123,66 @@ public class GLRenderer extends HardwareRenderer { } } @Override void drawDisplayList(DisplayList displayList, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty) { if (canDraw()) { if (!hasDirtyRegions()) { dirty = null; } // We are already on the correct thread final int surfaceState = checkRenderContextUnsafe(); if (surfaceState != SURFACE_STATE_ERROR) { HardwareCanvas canvas = mCanvas; if (mProfileEnabled) { mProfileLock.lock(); } dirty = beginFrame(canvas, dirty, surfaceState); int saveCount = 0; int status = DisplayList.STATUS_DONE; long start = getSystemTime(); try { status = prepareFrame(dirty); saveCount = canvas.save(); callbacks.onHardwarePreDraw(canvas); status |= drawDisplayList(attachInfo, canvas, displayList, status); } catch (Exception e) { Log.e(LOG_TAG, "An error has occurred while drawing:", e); } finally { callbacks.onHardwarePostDraw(canvas); canvas.restoreToCount(saveCount); mDrawDelta = getSystemTime() - start; if (mDrawDelta > 0) { mFrameCount++; debugOverdraw(attachInfo, dirty, canvas, displayList); debugDirtyRegions(dirty, canvas); drawProfileData(attachInfo); } } onPostDraw(); swapBuffers(status); if (mProfileEnabled) { mProfileLock.unlock(); } attachInfo.mIgnoreDirtyState = false; } } } @Override void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty) { Loading @@ -1144,7 +1199,6 @@ public class GLRenderer extends HardwareRenderer { final int surfaceState = checkRenderContextUnsafe(); if (surfaceState != SURFACE_STATE_ERROR) { HardwareCanvas canvas = mCanvas; attachInfo.mHardwareCanvas = canvas; if (mProfileEnabled) { mProfileLock.lock(); Loading
core/java/android/view/HardwareRenderer.java +22 −10 Original line number Diff line number Diff line Loading @@ -188,6 +188,9 @@ public abstract class HardwareRenderer { */ public static boolean sSystemRendererDisabled = false; /** @hide */ public static boolean sUseRenderThread = false; private boolean mEnabled; private boolean mRequested = true; Loading Loading @@ -305,13 +308,6 @@ public abstract class HardwareRenderer { */ abstract int getHeight(); /** * Gets the current canvas associated with this HardwareRenderer. * * @return the current HardwareCanvas */ abstract HardwareCanvas getCanvas(); /** * Outputs extra debugging information in the specified file descriptor. * @param pw Loading @@ -335,7 +331,7 @@ public abstract class HardwareRenderer { * * @return True if a property has changed. */ abstract boolean loadSystemProperties(Surface surface); abstract boolean loadSystemProperties(); /** * Sets the directory to use as a persistent storage for hardware rendering Loading Loading @@ -411,6 +407,18 @@ public abstract class HardwareRenderer { abstract void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty); /** * Temporary hook to draw a display list directly, only used if sUseRenderThread * is true. * * @param displayList The display list to draw * @param attachInfo AttachInfo tied to the specified view. * @param callbacks Callbacks invoked when drawing happens. * @param dirty The dirty rectangle to update, can be null. */ abstract void drawDisplayList(DisplayList displayList, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty); /** * Creates a new hardware layer. A hardware layer built by calling this * method will be treated as a texture layer, instead of as a render target. Loading Loading @@ -517,10 +525,14 @@ public abstract class HardwareRenderer { * @return A hardware renderer backed by OpenGL. */ static HardwareRenderer create(boolean translucent) { HardwareRenderer renderer = null; if (GLES20Canvas.isAvailable()) { return new GLRenderer(translucent); renderer = new GLRenderer(translucent); } if (renderer != null && sUseRenderThread) { renderer = new ThreadedRenderer((GLRenderer)renderer); } return null; return renderer; } /** Loading
core/java/android/view/ThreadedRenderer.java 0 → 100644 +317 −0 Original line number Diff line number Diff line /* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.view; import android.graphics.Rect; import android.graphics.SurfaceTexture; import android.os.Looper; import android.os.SystemClock; import android.os.Trace; import android.util.Log; import android.view.Surface.OutOfResourcesException; import android.view.View.AttachInfo; import java.io.PrintWriter; import java.lang.reflect.Method; import java.util.HashMap; /** * Hardware renderer that proxies the rendering to a render thread. Most calls * are synchronous, however a few such as draw() are posted async. The display list * is shared between the two threads and is guarded by a top level lock. * * The UI thread can block on the RenderThread, but RenderThread must never * block on the UI thread. * * Note that although currently the EGL context & surfaces are created & managed * by the render thread, the goal is to move that into a shared structure that can * be managed by both threads. EGLSurface creation & deletion should ideally be * done on the UI thread and not the RenderThread to avoid stalling the * RenderThread with surface buffer allocation. * * @hide */ public class ThreadedRenderer extends HardwareRenderer { private static final String LOGTAG = "ThreadedRenderer"; @SuppressWarnings("serial") static HashMap<String, Method> sMethodLut = new HashMap<String, Method>() {{ Method[] methods = HardwareRenderer.class.getDeclaredMethods(); for (Method m : methods) { put(m.getName(), m); } }}; static boolean sNeedsInit = true; private HardwareRenderer mRemoteRenderer; private int mWidth, mHeight; private RTJob mPreviousDraw; ThreadedRenderer(GLRenderer backingRenderer) { mRemoteRenderer = backingRenderer; setEnabled(true); if (sNeedsInit) { sNeedsInit = false; postToRenderThread(new Runnable() { @Override public void run() { // Hack to allow GLRenderer to create a handler to post the EGL // destruction to, although it'll never run Looper.prepare(); } }); } } @Override void destroy(boolean full) { run("destroy", full); } @Override boolean initialize(Surface surface) throws OutOfResourcesException { return (Boolean) run("initialize", surface); } @Override void updateSurface(Surface surface) throws OutOfResourcesException { post("updateSurface", surface); } @Override void destroyLayers(View view) { throw new NoSuchMethodError(); } @Override void destroyHardwareResources(View view) { run("destroyHardwareResources", view); } @Override void invalidate(Surface surface) { post("invalidate", surface); } @Override boolean validate() { // TODO Remove users of this API return false; } @Override boolean safelyRun(Runnable action) { return (Boolean) run("safelyRun", action); } @Override void setup(int width, int height) { mWidth = width; mHeight = height; post("setup", width, height); } @Override int getWidth() { return mWidth; } @Override int getHeight() { return mHeight; } @Override void dumpGfxInfo(PrintWriter pw) { // TODO Auto-generated method stub } @Override long getFrameCount() { // TODO Auto-generated method stub return 0; } @Override boolean loadSystemProperties() { return (Boolean) run("loadSystemProperties"); } @Override void pushLayerUpdate(HardwareLayer layer) { throw new NoSuchMethodError(); } @Override void cancelLayerUpdate(HardwareLayer layer) { throw new NoSuchMethodError(); } @Override void flushLayerUpdates() { throw new NoSuchMethodError(); } @Override void drawDisplayList(DisplayList displayList, AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty) { throw new NoSuchMethodError(); } /** * TODO: Remove * Temporary hack to allow RenderThreadTest prototype app to trigger * replaying a DisplayList after modifying the displaylist properties * * @hide */ public void repeatLastDraw() { if (mPreviousDraw == null) { throw new IllegalStateException("There isn't a previous draw"); } synchronized (mPreviousDraw) { mPreviousDraw.completed = false; } mPreviousDraw.args[3] = null; postToRenderThread(mPreviousDraw); } @Override void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty) { requireCompletion(mPreviousDraw); attachInfo.mIgnoreDirtyState = true; attachInfo.mDrawingTime = SystemClock.uptimeMillis(); view.mPrivateFlags |= View.PFLAG_DRAWN; view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED) == View.PFLAG_INVALIDATED; view.mPrivateFlags &= ~View.PFLAG_INVALIDATED; Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList"); DisplayList displayList = view.getDisplayList(); Trace.traceEnd(Trace.TRACE_TAG_VIEW); view.mRecreateDisplayList = false; mPreviousDraw = post("drawDisplayList", displayList, attachInfo, callbacks, dirty); } @Override HardwareLayer createHardwareLayer(boolean isOpaque) { throw new NoSuchMethodError(); } @Override HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) { throw new NoSuchMethodError(); } @Override SurfaceTexture createSurfaceTexture(HardwareLayer layer) { throw new NoSuchMethodError(); } @Override void setSurfaceTexture(HardwareLayer layer, SurfaceTexture surfaceTexture) { throw new NoSuchMethodError(); } @Override void detachFunctor(int functor) { throw new NoSuchMethodError(); } @Override boolean attachFunctor(AttachInfo attachInfo, int functor) { throw new NoSuchMethodError(); } @Override void setName(String name) { post("setName", name); } private static void requireCompletion(RTJob job) { if (job != null) { synchronized (job) { if (!job.completed) { try { job.wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } } } } } private RTJob post(String method, Object... args) { RTJob job = new RTJob(); job.method = sMethodLut.get(method); job.args = args; job.target = mRemoteRenderer; if (job.method == null) { throw new NullPointerException("Couldn't find method: " + method); } postToRenderThread(job); return job; } private Object run(String method, Object... args) { RTJob job = new RTJob(); job.method = sMethodLut.get(method); job.args = args; job.target = mRemoteRenderer; if (job.method == null) { throw new NullPointerException("Couldn't find method: " + method); } synchronized (job) { postToRenderThread(job); try { job.wait(); return job.ret; } catch (InterruptedException e) { throw new RuntimeException(e); } } } static class RTJob implements Runnable { Method method; Object[] args; Object target; Object ret; boolean completed = false; @Override public void run() { try { ret = method.invoke(target, args); synchronized (this) { completed = true; notify(); } } catch (Exception e) { Log.e(LOGTAG, "Failed to invoke: " + method.getName(), e); } } } /** @hide */ public static native void postToRenderThread(Runnable runnable); }
core/java/android/view/View.java +7 −2 Original line number Diff line number Diff line Loading @@ -11234,6 +11234,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return null; } /** * @hide */ public HardwareRenderer getHardwareRenderer() { return mAttachInfo != null ? mAttachInfo.mHardwareRenderer : null; } /** * <p>Causes the Runnable to be added to the message queue. * The runnable will be run on the user interface thread.</p> Loading Loading @@ -18869,8 +18876,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final Callbacks mRootCallbacks; HardwareCanvas mHardwareCanvas; IWindowId mIWindowId; WindowId mWindowId;
core/java/android/view/ViewRootImpl.java +115 −109 File changed.Preview size limit exceeded, changes collapsed. Show changes