Loading api/current.txt +9 −6 Original line number Diff line number Diff line Loading @@ -14035,6 +14035,7 @@ package android.os { method public void dispatchMessage(android.os.Message); method public final void dump(android.util.Printer, java.lang.String); method public final android.os.Looper getLooper(); method public java.lang.String getMessageName(android.os.Message); method public void handleMessage(android.os.Message); method public final boolean hasMessages(int); method public final boolean hasMessages(int, java.lang.Object); Loading Loading @@ -14104,13 +14105,13 @@ package android.os { public class Looper { method public void dump(android.util.Printer, java.lang.String); method public static final synchronized android.os.Looper getMainLooper(); method public static synchronized android.os.Looper getMainLooper(); method public java.lang.Thread getThread(); method public static final void loop(); method public static final android.os.Looper myLooper(); method public static final android.os.MessageQueue myQueue(); method public static final void prepare(); method public static final void prepareMainLooper(); method public static void loop(); method public static android.os.Looper myLooper(); method public static android.os.MessageQueue myQueue(); method public static void prepare(); method public static void prepareMainLooper(); method public void quit(); method public void setMessageLogging(android.util.Printer); } Loading Loading @@ -22642,8 +22643,10 @@ package android.view { ctor public ViewDebug(); method public static void dumpCapturedView(java.lang.String, java.lang.Object); method public static void startHierarchyTracing(java.lang.String, android.view.View); method public static void startLooperProfiling(java.io.File); method public static void startRecyclerTracing(java.lang.String, android.view.View); method public static void stopHierarchyTracing(); method public static void stopLooperProfiling(); method public static void stopRecyclerTracing(); method public static void trace(android.view.View, android.view.ViewDebug.RecyclerTraceType, int...); method public static void trace(android.view.View, android.view.ViewDebug.HierarchyTraceType); core/java/android/os/Handler.java +15 −0 Original line number Diff line number Diff line Loading @@ -168,6 +168,21 @@ public class Handler { mCallback = callback; } /** * Returns a string representing the name of the specified message. * The default implementation will either return the class name of the * message callback if any, or the hexadecimal representation of the * message "what" field. * * @param message The message whose name is being queried */ public String getMessageName(Message message) { if (message.callback != null) { return message.callback.getClass().getName(); } return "0x" + Integer.toHexString(message.what); } /** * Returns a new {@link android.os.Message Message} from the global message pool. More efficient than * creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this). Loading core/java/android/os/Looper.java +37 −32 Original line number Diff line number Diff line Loading @@ -52,7 +52,6 @@ import android.util.PrefixPrinter; */ public class Looper { private static final String TAG = "Looper"; private static final boolean LOG_V = Log.isLoggable(TAG, Log.VERBOSE); // sThreadLocal.get() will return null unless you've called prepare(). private static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); Loading @@ -70,7 +69,7 @@ public class Looper { * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */ public static final void prepare() { public static void prepare() { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } Loading @@ -83,7 +82,7 @@ public class Looper { * is created by the Android environment, so you should never need * to call this function yourself. See also: {@link #prepare()} */ public static final void prepareMainLooper() { public static void prepareMainLooper() { prepare(); setMainLooper(myLooper()); myLooper().mQueue.mQuitAllowed = false; Loading @@ -95,7 +94,7 @@ public class Looper { /** Returns the application's main looper, which lives in the main thread of the application. */ public synchronized static final Looper getMainLooper() { public synchronized static Looper getMainLooper() { return mMainLooper; } Loading @@ -103,7 +102,7 @@ public class Looper { * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ public static final void loop() { public static void loop() { Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); Loading @@ -122,20 +121,36 @@ public class Looper { // No target is a magic identifier for the quit message. return; } if (me.mLogging != null) me.mLogging.println( ">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what ); long wallStart = 0; long threadStart = 0; // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); wallStart = System.currentTimeMillis(); threadStart = SystemClock.currentThreadTimeMillis(); } msg.target.dispatchMessage(msg); if (me.mLogging != null) me.mLogging.println( "<<<<< Finished to " + msg.target + " " + msg.callback); if (logging != null) { long wallTime = System.currentTimeMillis() - wallStart; long threadTime = SystemClock.currentThreadTimeMillis() - threadStart; logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); if (logging instanceof Profiler) { ((Profiler) logging).profile(msg, wallStart, wallTime, threadTime); } } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf("Looper", "Thread identity changed from 0x" Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " Loading @@ -151,7 +166,7 @@ public class Looper { * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static final Looper myLooper() { public static Looper myLooper() { return sThreadLocal.get(); } Loading @@ -173,7 +188,7 @@ public class Looper { * thread. This must be called from a thread running a Looper, or a * NullPointerException will be thrown. */ public static final MessageQueue myQueue() { public static MessageQueue myQueue() { return myLooper().mQueue; } Loading Loading @@ -225,23 +240,13 @@ public class Looper { } public String toString() { return "Looper{" + Integer.toHexString(System.identityHashCode(this)) + "}"; return "Looper{" + Integer.toHexString(System.identityHashCode(this)) + "}"; } static class HandlerException extends Exception { HandlerException(Message message, Throwable cause) { super(createMessage(cause), cause); } static String createMessage(Throwable cause) { String causeMsg = cause.getMessage(); if (causeMsg == null) { causeMsg = cause.toString(); } return causeMsg; } /** * @hide */ public static interface Profiler { void profile(Message message, long wallStart, long wallTime, long threadTime); } } core/java/android/view/ViewAncestor.java +56 −0 Original line number Diff line number Diff line Loading @@ -2208,6 +2208,62 @@ public final class ViewAncestor extends Handler implements ViewParent, public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 1022; public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT = 1023; @Override public String getMessageName(Message message) { switch (message.what) { case DO_TRAVERSAL: return "DO_TRAVERSAL"; case DIE: return "DIE"; case RESIZED: return "RESIZED"; case RESIZED_REPORT: return "RESIZED_REPORT"; case WINDOW_FOCUS_CHANGED: return "WINDOW_FOCUS_CHANGED"; case DISPATCH_KEY: return "DISPATCH_KEY"; case DISPATCH_POINTER: return "DISPATCH_POINTER"; case DISPATCH_TRACKBALL: return "DISPATCH_TRACKBALL"; case DISPATCH_APP_VISIBILITY: return "DISPATCH_APP_VISIBILITY"; case DISPATCH_GET_NEW_SURFACE: return "DISPATCH_GET_NEW_SURFACE"; case FINISHED_EVENT: return "FINISHED_EVENT"; case DISPATCH_KEY_FROM_IME: return "DISPATCH_KEY_FROM_IME"; case FINISH_INPUT_CONNECTION: return "FINISH_INPUT_CONNECTION"; case CHECK_FOCUS: return "CHECK_FOCUS"; case CLOSE_SYSTEM_DIALOGS: return "CLOSE_SYSTEM_DIALOGS"; case DISPATCH_DRAG_EVENT: return "DISPATCH_DRAG_EVENT"; case DISPATCH_DRAG_LOCATION_EVENT: return "DISPATCH_DRAG_LOCATION_EVENT"; case DISPATCH_SYSTEM_UI_VISIBILITY: return "DISPATCH_SYSTEM_UI_VISIBILITY"; case DISPATCH_GENERIC_MOTION: return "DISPATCH_GENERIC_MOTION"; case UPDATE_CONFIGURATION: return "UPDATE_CONFIGURATION"; case DO_PERFORM_ACCESSIBILITY_ACTION: return "DO_PERFORM_ACCESSIBILITY_ACTION"; case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID"; case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID"; case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT: return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT"; } return super.getMessageName(message); } @Override public void handleMessage(Message msg) { switch (msg.what) { Loading core/java/android/view/ViewDebug.java +143 −25 Original line number Diff line number Diff line Loading @@ -16,41 +16,45 @@ package android.view; import android.util.Log; import android.util.DisplayMetrics; import android.content.res.Resources; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Rect; import android.os.Environment; import android.os.Debug; import android.os.Environment; import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.util.DisplayMetrics; import android.util.Log; import android.util.Printer; import java.io.BufferedOutputStream; import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.File; import java.io.BufferedWriter; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.FileOutputStream; import java.io.DataOutputStream; import java.io.OutputStreamWriter; import java.io.BufferedOutputStream; import java.io.OutputStream; import java.util.List; import java.util.LinkedList; import java.util.ArrayList; import java.util.HashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.lang.annotation.Target; import java.io.OutputStreamWriter; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Various debugging/tracing tools related to {@link View} and the view hierarchy. Loading Loading @@ -105,13 +109,6 @@ public class ViewDebug { */ public static final boolean DEBUG_PROFILE_LAYOUT = false; /** * Profiles real fps (times between draws) and displays the result. * * @hide */ public static final boolean DEBUG_SHOW_FPS = false; /** * Enables detailed logging of drag/drop operations. * @hide Loading Loading @@ -396,6 +393,9 @@ public class ViewDebug { private static List<RecyclerTrace> sRecyclerTraces; private static String sRecyclerTracePrefix; private static final ThreadLocal<LooperProfiler> sLooperProfilerStorage = new ThreadLocal<LooperProfiler>(); /** * Returns the number of instanciated Views. * Loading @@ -418,6 +418,124 @@ public class ViewDebug { return Debug.countInstancesOfClass(ViewAncestor.class); } /** * Starts profiling the looper associated with the current thread. * You must call {@link #stopLooperProfiling} to end profiling * and obtain the traces. Both methods must be invoked on the * same thread. * * @param traceFile The path where to write the looper traces * * @see #stopLooperProfiling() */ public static void startLooperProfiling(File traceFile) { if (sLooperProfilerStorage.get() == null) { LooperProfiler profiler = new LooperProfiler(traceFile); sLooperProfilerStorage.set(profiler); Looper.myLooper().setMessageLogging(profiler); } } /** * Stops profiling the looper associated with the current thread. * * @see #startLooperProfiling(java.io.File) */ public static void stopLooperProfiling() { LooperProfiler profiler = sLooperProfilerStorage.get(); if (profiler != null) { sLooperProfilerStorage.remove(); Looper.myLooper().setMessageLogging(null); profiler.save(); } } private static class LooperProfiler implements Looper.Profiler, Printer { private static final int LOOPER_PROFILER_VERSION = 1; private static final String LOG_TAG = "LooperProfiler"; private final ArrayList<Entry> mTraces = new ArrayList<Entry>(512); private final File mTraceFile; public LooperProfiler(File traceFile) { mTraceFile = traceFile; } @Override public void println(String x) { // Ignore messages } @Override public void profile(Message message, long wallStart, long wallTime, long threadTime) { Entry entry = new Entry(); entry.messageId = message.what; entry.name = message.getTarget().getMessageName(message); entry.wallStart = wallStart; entry.wallTime = wallTime; entry.threadTime = threadTime; mTraces.add(entry); } void save() { // Don't block the UI thread new Thread(new Runnable() { @Override public void run() { saveTraces(); } }, "LooperProfiler[" + mTraceFile + "]").start(); } private void saveTraces() { FileOutputStream fos; try { fos = new FileOutputStream(mTraceFile); } catch (FileNotFoundException e) { Log.e(LOG_TAG, "Could not open trace file: " + mTraceFile); return; } DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fos)); try { out.writeInt(LOOPER_PROFILER_VERSION); out.writeInt(mTraces.size()); for (Entry entry : mTraces) { saveTrace(entry, out); } Log.d(LOG_TAG, "Looper traces ready: " + mTraceFile); } catch (IOException e) { Log.e(LOG_TAG, "Could not write trace file: ", e); } finally { try { out.close(); } catch (IOException e) { // Ignore } } } private void saveTrace(Entry entry, DataOutputStream out) throws IOException { out.writeInt(entry.messageId); out.writeUTF(entry.name); out.writeLong(entry.wallStart); out.writeLong(entry.wallTime); out.writeLong(entry.threadTime); } static class Entry { int messageId; String name; long wallStart; long wallTime; long threadTime; } } /** * Outputs a trace to the currently opened recycler traces. The trace records the type of * recycler action performed on the supplied view as well as a number of parameters. Loading Loading
api/current.txt +9 −6 Original line number Diff line number Diff line Loading @@ -14035,6 +14035,7 @@ package android.os { method public void dispatchMessage(android.os.Message); method public final void dump(android.util.Printer, java.lang.String); method public final android.os.Looper getLooper(); method public java.lang.String getMessageName(android.os.Message); method public void handleMessage(android.os.Message); method public final boolean hasMessages(int); method public final boolean hasMessages(int, java.lang.Object); Loading Loading @@ -14104,13 +14105,13 @@ package android.os { public class Looper { method public void dump(android.util.Printer, java.lang.String); method public static final synchronized android.os.Looper getMainLooper(); method public static synchronized android.os.Looper getMainLooper(); method public java.lang.Thread getThread(); method public static final void loop(); method public static final android.os.Looper myLooper(); method public static final android.os.MessageQueue myQueue(); method public static final void prepare(); method public static final void prepareMainLooper(); method public static void loop(); method public static android.os.Looper myLooper(); method public static android.os.MessageQueue myQueue(); method public static void prepare(); method public static void prepareMainLooper(); method public void quit(); method public void setMessageLogging(android.util.Printer); } Loading Loading @@ -22642,8 +22643,10 @@ package android.view { ctor public ViewDebug(); method public static void dumpCapturedView(java.lang.String, java.lang.Object); method public static void startHierarchyTracing(java.lang.String, android.view.View); method public static void startLooperProfiling(java.io.File); method public static void startRecyclerTracing(java.lang.String, android.view.View); method public static void stopHierarchyTracing(); method public static void stopLooperProfiling(); method public static void stopRecyclerTracing(); method public static void trace(android.view.View, android.view.ViewDebug.RecyclerTraceType, int...); method public static void trace(android.view.View, android.view.ViewDebug.HierarchyTraceType);
core/java/android/os/Handler.java +15 −0 Original line number Diff line number Diff line Loading @@ -168,6 +168,21 @@ public class Handler { mCallback = callback; } /** * Returns a string representing the name of the specified message. * The default implementation will either return the class name of the * message callback if any, or the hexadecimal representation of the * message "what" field. * * @param message The message whose name is being queried */ public String getMessageName(Message message) { if (message.callback != null) { return message.callback.getClass().getName(); } return "0x" + Integer.toHexString(message.what); } /** * Returns a new {@link android.os.Message Message} from the global message pool. More efficient than * creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this). Loading
core/java/android/os/Looper.java +37 −32 Original line number Diff line number Diff line Loading @@ -52,7 +52,6 @@ import android.util.PrefixPrinter; */ public class Looper { private static final String TAG = "Looper"; private static final boolean LOG_V = Log.isLoggable(TAG, Log.VERBOSE); // sThreadLocal.get() will return null unless you've called prepare(). private static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); Loading @@ -70,7 +69,7 @@ public class Looper { * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */ public static final void prepare() { public static void prepare() { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } Loading @@ -83,7 +82,7 @@ public class Looper { * is created by the Android environment, so you should never need * to call this function yourself. See also: {@link #prepare()} */ public static final void prepareMainLooper() { public static void prepareMainLooper() { prepare(); setMainLooper(myLooper()); myLooper().mQueue.mQuitAllowed = false; Loading @@ -95,7 +94,7 @@ public class Looper { /** Returns the application's main looper, which lives in the main thread of the application. */ public synchronized static final Looper getMainLooper() { public synchronized static Looper getMainLooper() { return mMainLooper; } Loading @@ -103,7 +102,7 @@ public class Looper { * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ public static final void loop() { public static void loop() { Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); Loading @@ -122,20 +121,36 @@ public class Looper { // No target is a magic identifier for the quit message. return; } if (me.mLogging != null) me.mLogging.println( ">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what ); long wallStart = 0; long threadStart = 0; // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); wallStart = System.currentTimeMillis(); threadStart = SystemClock.currentThreadTimeMillis(); } msg.target.dispatchMessage(msg); if (me.mLogging != null) me.mLogging.println( "<<<<< Finished to " + msg.target + " " + msg.callback); if (logging != null) { long wallTime = System.currentTimeMillis() - wallStart; long threadTime = SystemClock.currentThreadTimeMillis() - threadStart; logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); if (logging instanceof Profiler) { ((Profiler) logging).profile(msg, wallStart, wallTime, threadTime); } } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf("Looper", "Thread identity changed from 0x" Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " Loading @@ -151,7 +166,7 @@ public class Looper { * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static final Looper myLooper() { public static Looper myLooper() { return sThreadLocal.get(); } Loading @@ -173,7 +188,7 @@ public class Looper { * thread. This must be called from a thread running a Looper, or a * NullPointerException will be thrown. */ public static final MessageQueue myQueue() { public static MessageQueue myQueue() { return myLooper().mQueue; } Loading Loading @@ -225,23 +240,13 @@ public class Looper { } public String toString() { return "Looper{" + Integer.toHexString(System.identityHashCode(this)) + "}"; return "Looper{" + Integer.toHexString(System.identityHashCode(this)) + "}"; } static class HandlerException extends Exception { HandlerException(Message message, Throwable cause) { super(createMessage(cause), cause); } static String createMessage(Throwable cause) { String causeMsg = cause.getMessage(); if (causeMsg == null) { causeMsg = cause.toString(); } return causeMsg; } /** * @hide */ public static interface Profiler { void profile(Message message, long wallStart, long wallTime, long threadTime); } }
core/java/android/view/ViewAncestor.java +56 −0 Original line number Diff line number Diff line Loading @@ -2208,6 +2208,62 @@ public final class ViewAncestor extends Handler implements ViewParent, public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 1022; public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT = 1023; @Override public String getMessageName(Message message) { switch (message.what) { case DO_TRAVERSAL: return "DO_TRAVERSAL"; case DIE: return "DIE"; case RESIZED: return "RESIZED"; case RESIZED_REPORT: return "RESIZED_REPORT"; case WINDOW_FOCUS_CHANGED: return "WINDOW_FOCUS_CHANGED"; case DISPATCH_KEY: return "DISPATCH_KEY"; case DISPATCH_POINTER: return "DISPATCH_POINTER"; case DISPATCH_TRACKBALL: return "DISPATCH_TRACKBALL"; case DISPATCH_APP_VISIBILITY: return "DISPATCH_APP_VISIBILITY"; case DISPATCH_GET_NEW_SURFACE: return "DISPATCH_GET_NEW_SURFACE"; case FINISHED_EVENT: return "FINISHED_EVENT"; case DISPATCH_KEY_FROM_IME: return "DISPATCH_KEY_FROM_IME"; case FINISH_INPUT_CONNECTION: return "FINISH_INPUT_CONNECTION"; case CHECK_FOCUS: return "CHECK_FOCUS"; case CLOSE_SYSTEM_DIALOGS: return "CLOSE_SYSTEM_DIALOGS"; case DISPATCH_DRAG_EVENT: return "DISPATCH_DRAG_EVENT"; case DISPATCH_DRAG_LOCATION_EVENT: return "DISPATCH_DRAG_LOCATION_EVENT"; case DISPATCH_SYSTEM_UI_VISIBILITY: return "DISPATCH_SYSTEM_UI_VISIBILITY"; case DISPATCH_GENERIC_MOTION: return "DISPATCH_GENERIC_MOTION"; case UPDATE_CONFIGURATION: return "UPDATE_CONFIGURATION"; case DO_PERFORM_ACCESSIBILITY_ACTION: return "DO_PERFORM_ACCESSIBILITY_ACTION"; case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID"; case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID"; case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT: return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT"; } return super.getMessageName(message); } @Override public void handleMessage(Message msg) { switch (msg.what) { Loading
core/java/android/view/ViewDebug.java +143 −25 Original line number Diff line number Diff line Loading @@ -16,41 +16,45 @@ package android.view; import android.util.Log; import android.util.DisplayMetrics; import android.content.res.Resources; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Rect; import android.os.Environment; import android.os.Debug; import android.os.Environment; import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.util.DisplayMetrics; import android.util.Log; import android.util.Printer; import java.io.BufferedOutputStream; import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.File; import java.io.BufferedWriter; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.FileOutputStream; import java.io.DataOutputStream; import java.io.OutputStreamWriter; import java.io.BufferedOutputStream; import java.io.OutputStream; import java.util.List; import java.util.LinkedList; import java.util.ArrayList; import java.util.HashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.lang.annotation.Target; import java.io.OutputStreamWriter; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Various debugging/tracing tools related to {@link View} and the view hierarchy. Loading Loading @@ -105,13 +109,6 @@ public class ViewDebug { */ public static final boolean DEBUG_PROFILE_LAYOUT = false; /** * Profiles real fps (times between draws) and displays the result. * * @hide */ public static final boolean DEBUG_SHOW_FPS = false; /** * Enables detailed logging of drag/drop operations. * @hide Loading Loading @@ -396,6 +393,9 @@ public class ViewDebug { private static List<RecyclerTrace> sRecyclerTraces; private static String sRecyclerTracePrefix; private static final ThreadLocal<LooperProfiler> sLooperProfilerStorage = new ThreadLocal<LooperProfiler>(); /** * Returns the number of instanciated Views. * Loading @@ -418,6 +418,124 @@ public class ViewDebug { return Debug.countInstancesOfClass(ViewAncestor.class); } /** * Starts profiling the looper associated with the current thread. * You must call {@link #stopLooperProfiling} to end profiling * and obtain the traces. Both methods must be invoked on the * same thread. * * @param traceFile The path where to write the looper traces * * @see #stopLooperProfiling() */ public static void startLooperProfiling(File traceFile) { if (sLooperProfilerStorage.get() == null) { LooperProfiler profiler = new LooperProfiler(traceFile); sLooperProfilerStorage.set(profiler); Looper.myLooper().setMessageLogging(profiler); } } /** * Stops profiling the looper associated with the current thread. * * @see #startLooperProfiling(java.io.File) */ public static void stopLooperProfiling() { LooperProfiler profiler = sLooperProfilerStorage.get(); if (profiler != null) { sLooperProfilerStorage.remove(); Looper.myLooper().setMessageLogging(null); profiler.save(); } } private static class LooperProfiler implements Looper.Profiler, Printer { private static final int LOOPER_PROFILER_VERSION = 1; private static final String LOG_TAG = "LooperProfiler"; private final ArrayList<Entry> mTraces = new ArrayList<Entry>(512); private final File mTraceFile; public LooperProfiler(File traceFile) { mTraceFile = traceFile; } @Override public void println(String x) { // Ignore messages } @Override public void profile(Message message, long wallStart, long wallTime, long threadTime) { Entry entry = new Entry(); entry.messageId = message.what; entry.name = message.getTarget().getMessageName(message); entry.wallStart = wallStart; entry.wallTime = wallTime; entry.threadTime = threadTime; mTraces.add(entry); } void save() { // Don't block the UI thread new Thread(new Runnable() { @Override public void run() { saveTraces(); } }, "LooperProfiler[" + mTraceFile + "]").start(); } private void saveTraces() { FileOutputStream fos; try { fos = new FileOutputStream(mTraceFile); } catch (FileNotFoundException e) { Log.e(LOG_TAG, "Could not open trace file: " + mTraceFile); return; } DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fos)); try { out.writeInt(LOOPER_PROFILER_VERSION); out.writeInt(mTraces.size()); for (Entry entry : mTraces) { saveTrace(entry, out); } Log.d(LOG_TAG, "Looper traces ready: " + mTraceFile); } catch (IOException e) { Log.e(LOG_TAG, "Could not write trace file: ", e); } finally { try { out.close(); } catch (IOException e) { // Ignore } } } private void saveTrace(Entry entry, DataOutputStream out) throws IOException { out.writeInt(entry.messageId); out.writeUTF(entry.name); out.writeLong(entry.wallStart); out.writeLong(entry.wallTime); out.writeLong(entry.threadTime); } static class Entry { int messageId; String name; long wallStart; long wallTime; long threadTime; } } /** * Outputs a trace to the currently opened recycler traces. The trace records the type of * recycler action performed on the supplied view as well as a number of parameters. Loading