ViewDebugpublic class ViewDebug extends Object Various debugging/tracing tools related to {@link View} and the view hierarchy. |
Fields Summary |
---|
public static final boolean | TRACE_HIERARCHYEnables or disables view hierarchy tracing. Any invoker of
{@link #trace(View, android.view.ViewDebug.HierarchyTraceType)} should first
check that this value is set to true as not to affect performance. | public static final boolean | TRACE_RECYCLEREnables or disables view recycler tracing. Any invoker of
{@link #trace(View, android.view.ViewDebug.RecyclerTraceType, int[])} should first
check that this value is set to true as not to affect performance. | static final String | SYSTEM_PROPERTY_CAPTURE_VIEWThe system property of dynamic switch for capturing view information
when it is set, we dump interested fields and methods for the view on focus | static final String | SYSTEM_PROPERTY_CAPTURE_EVENTThe system property of dynamic switch for capturing event information
when it is set, we log key events, touch/motion and trackball events | private static HashMap | mCapturedViewMethodsForClasses | private static HashMap | mCapturedViewFieldsForClasses | private static final int | CAPTURE_TIMEOUT | private static final String | REMOTE_COMMAND_CAPTURE | private static final String | REMOTE_COMMAND_DUMP | private static final String | REMOTE_COMMAND_INVALIDATE | private static final String | REMOTE_COMMAND_REQUEST_LAYOUT | private static final String | REMOTE_PROFILE | private static HashMap | sFieldsForClasses | private static HashMap | sMethodsForClasses | private static HashMap | sAnnotations | private static BufferedWriter | sHierarchyTraces | private static ViewRoot | sHierarhcyRoot | private static String | sHierarchyTracePrefix | private static View | sRecyclerOwnerView | private static List | sRecyclerViews | private static List | sRecyclerTraces | private static String | sRecyclerTracePrefix |
Methods Summary |
---|
private static void | capture(View root, java.io.OutputStream clientStream, java.lang.String parameter)
final View captureView = findView(root, parameter);
if (captureView != null) {
final CountDownLatch latch = new CountDownLatch(1);
final Bitmap[] cache = new Bitmap[1];
root.post(new Runnable() {
public void run() {
try {
cache[0] = captureView.createSnapshot(
Bitmap.Config.ARGB_8888, 0);
} catch (OutOfMemoryError e) {
try {
cache[0] = captureView.createSnapshot(
Bitmap.Config.ARGB_4444, 0);
} catch (OutOfMemoryError e2) {
Log.w("View", "Out of memory for bitmap");
}
} finally {
latch.countDown();
}
}
});
try {
latch.await(CAPTURE_TIMEOUT, TimeUnit.MILLISECONDS);
if (cache[0] != null) {
BufferedOutputStream out = null;
try {
out = new BufferedOutputStream(clientStream, 32 * 1024);
cache[0].compress(Bitmap.CompressFormat.PNG, 100, out);
out.flush();
} finally {
if (out != null) {
out.close();
}
cache[0].recycle();
}
} else {
Log.w("View", "Failed to create capture bitmap!");
clientStream.close();
}
} catch (InterruptedException e) {
Log.w("View", "Could not complete the capture of the view " + captureView);
Thread.currentThread().interrupt();
}
}
| private static java.lang.String | capturedViewExportFields(java.lang.Object obj, java.lang.Class klass, java.lang.String prefix)
if (obj == null) {
return "null";
}
StringBuilder sb = new StringBuilder();
final Field[] fields = capturedViewGetPropertyFields(klass);
int count = fields.length;
for (int i = 0; i < count; i++) {
final Field field = fields[i];
try {
Object fieldValue = field.get(obj);
sb.append(prefix);
sb.append(field.getName());
sb.append("=");
if (fieldValue != null) {
final String value = fieldValue.toString().replace("\n", "\\n");
sb.append(value);
} else {
sb.append("null");
}
sb.append(' ");
} catch (IllegalAccessException e) {
//Exception IllegalAccess, it is OK here
//we simply ignore this field
}
}
return sb.toString();
| private static java.lang.String | capturedViewExportMethods(java.lang.Object obj, java.lang.Class klass, java.lang.String prefix)
if (obj == null) {
return "null";
}
StringBuilder sb = new StringBuilder();
final Method[] methods = capturedViewGetPropertyMethods(klass);
int count = methods.length;
for (int i = 0; i < count; i++) {
final Method method = methods[i];
try {
Object methodValue = method.invoke(obj, (Object[]) null);
final Class<?> returnType = method.getReturnType();
CapturedViewProperty property = method.getAnnotation(CapturedViewProperty.class);
if (property.retrieveReturn()) {
//we are interested in the second level data only
sb.append(capturedViewExportMethods(methodValue, returnType, method.getName() + "#"));
} else {
sb.append(prefix);
sb.append(method.getName());
sb.append("()=");
if (methodValue != null) {
final String value = methodValue.toString().replace("\n", "\\n");
sb.append(value);
} else {
sb.append("null");
}
sb.append("; ");
}
} catch (IllegalAccessException e) {
//Exception IllegalAccess, it is OK here
//we simply ignore this method
} catch (InvocationTargetException e) {
//Exception InvocationTarget, it is OK here
//we simply ignore this method
}
}
return sb.toString();
| private static java.lang.reflect.Field[] | capturedViewGetPropertyFields(java.lang.Class klass)
if (mCapturedViewFieldsForClasses == null) {
mCapturedViewFieldsForClasses = new HashMap<Class<?>, Field[]>();
}
final HashMap<Class<?>, Field[]> map = mCapturedViewFieldsForClasses;
Field[] fields = map.get(klass);
if (fields != null) {
return fields;
}
final ArrayList<Field> foundFields = new ArrayList<Field>();
fields = klass.getFields();
int count = fields.length;
for (int i = 0; i < count; i++) {
final Field field = fields[i];
if (field.isAnnotationPresent(CapturedViewProperty.class)) {
field.setAccessible(true);
foundFields.add(field);
}
}
fields = foundFields.toArray(new Field[foundFields.size()]);
map.put(klass, fields);
return fields;
| private static java.lang.reflect.Method[] | capturedViewGetPropertyMethods(java.lang.Class klass)
if (mCapturedViewMethodsForClasses == null) {
mCapturedViewMethodsForClasses = new HashMap<Class<?>, Method[]>();
}
final HashMap<Class<?>, Method[]> map = mCapturedViewMethodsForClasses;
Method[] methods = map.get(klass);
if (methods != null) {
return methods;
}
final ArrayList<Method> foundMethods = new ArrayList<Method>();
methods = klass.getMethods();
int count = methods.length;
for (int i = 0; i < count; i++) {
final Method method = methods[i];
if (method.getParameterTypes().length == 0 &&
method.isAnnotationPresent(CapturedViewProperty.class) &&
method.getReturnType() != Void.class) {
method.setAccessible(true);
foundMethods.add(method);
}
}
methods = foundMethods.toArray(new Method[foundMethods.size()]);
map.put(klass, methods);
return methods;
| static void | dispatchCommand(View view, java.lang.String command, java.lang.String parameters, java.io.OutputStream clientStream)
// Paranoid but safe...
view = view.getRootView();
if (REMOTE_COMMAND_DUMP.equalsIgnoreCase(command)) {
dump(view, clientStream);
} else {
final String[] params = parameters.split(" ");
if (REMOTE_COMMAND_CAPTURE.equalsIgnoreCase(command)) {
capture(view, clientStream, params[0]);
} else if (REMOTE_COMMAND_INVALIDATE.equalsIgnoreCase(command)) {
invalidate(view, params[0]);
} else if (REMOTE_COMMAND_REQUEST_LAYOUT.equalsIgnoreCase(command)) {
requestLayout(view, params[0]);
} else if (REMOTE_PROFILE.equalsIgnoreCase(command)) {
profile(view, clientStream, params[0]);
}
}
| private static void | dump(View root, java.io.OutputStream clientStream)
BufferedWriter out = null;
try {
out = new BufferedWriter(new OutputStreamWriter(clientStream), 32 * 1024);
View view = root.getRootView();
if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
dumpViewHierarchyWithProperties(group.getContext(), group, out, 0);
}
out.write("DONE.");
out.newLine();
} catch (Exception e) {
android.util.Log.w("View", "Problem dumping the view:", e);
} finally {
if (out != null) {
out.close();
}
}
| public static void | dumpCapturedView(java.lang.String tag, java.lang.Object view)Dump view info for id based instrument test generation
(and possibly further data analysis). The results are dumped
to the log.
Class<?> klass = view.getClass();
StringBuilder sb = new StringBuilder(klass.getName() + ": ");
sb.append(capturedViewExportFields(view, klass, ""));
sb.append(capturedViewExportMethods(view, klass, ""));
Log.d(tag, sb.toString());
| private static boolean | dumpView(java.lang.Object view, java.io.BufferedWriter out, int level)
try {
for (int i = 0; i < level; i++) {
out.write(' ");
}
out.write(view.getClass().getName());
out.write('@");
out.write(Integer.toHexString(view.hashCode()));
out.newLine();
} catch (IOException e) {
Log.w("View", "Error while dumping hierarchy tree");
return false;
}
return true;
| private static void | dumpViewHierarchy(ViewGroup group, java.io.BufferedWriter out, int level)
if (!dumpView(group, out, level)) {
return;
}
final int count = group.getChildCount();
for (int i = 0; i < count; i++) {
final View view = group.getChildAt(i);
if (view instanceof ViewGroup) {
dumpViewHierarchy((ViewGroup) view, out, level + 1);
} else {
dumpView(view, out, level + 1);
}
}
| private static void | dumpViewHierarchyWithProperties(android.content.Context context, ViewGroup group, java.io.BufferedWriter out, int level)
if (!dumpViewWithProperties(context, group, out, level)) {
return;
}
final int count = group.getChildCount();
for (int i = 0; i < count; i++) {
final View view = group.getChildAt(i);
if (view instanceof ViewGroup) {
dumpViewHierarchyWithProperties(context, (ViewGroup) view, out, level + 1);
} else {
dumpViewWithProperties(context, view, out, level + 1);
}
}
| private static void | dumpViewProperties(android.content.Context context, java.lang.Object view, java.io.BufferedWriter out)
dumpViewProperties(context, view, out, "");
| private static void | dumpViewProperties(android.content.Context context, java.lang.Object view, java.io.BufferedWriter out, java.lang.String prefix)
Class<?> klass = view.getClass();
do {
exportFields(context, view, out, klass, prefix);
exportMethods(context, view, out, klass, prefix);
klass = klass.getSuperclass();
} while (klass != Object.class);
| private static boolean | dumpViewWithProperties(android.content.Context context, View view, java.io.BufferedWriter out, int level)
try {
for (int i = 0; i < level; i++) {
out.write(' ");
}
out.write(view.getClass().getName());
out.write('@");
out.write(Integer.toHexString(view.hashCode()));
out.write(' ");
dumpViewProperties(context, view, out);
out.newLine();
} catch (IOException e) {
Log.w("View", "Error while dumping hierarchy tree");
return false;
}
return true;
| private static void | exportFields(android.content.Context context, java.lang.Object view, java.io.BufferedWriter out, java.lang.Class klass, java.lang.String prefix)
final Field[] fields = getExportedPropertyFields(klass);
int count = fields.length;
for (int i = 0; i < count; i++) {
final Field field = fields[i];
//noinspection EmptyCatchBlock
try {
Object fieldValue = null;
final Class<?> type = field.getType();
if (type == int.class) {
final ExportedProperty property = sAnnotations.get(field);
if (property.resolveId() && context != null) {
final int id = field.getInt(view);
fieldValue = resolveId(context, id);
} else {
final IntToString[] mapping = property.mapping();
if (mapping.length > 0) {
final int intValue = field.getInt(view);
int mappingCount = mapping.length;
for (int j = 0; j < mappingCount; j++) {
final IntToString mapped = mapping[j];
if (mapped.from() == intValue) {
fieldValue = mapped.to();
break;
}
}
if (fieldValue == null) {
fieldValue = intValue;
}
}
}
} else if (type == int[].class) {
final ExportedProperty property = sAnnotations.get(field);
final int[] array = (int[]) field.get(view);
final String valuePrefix = prefix + field.getName() + '_";
final String suffix = "";
exportUnrolledArray(context, out, property, array, valuePrefix, suffix);
// We exit here!
return;
} else if (!type.isPrimitive()) {
final ExportedProperty property = sAnnotations.get(field);
if (property.deepExport()) {
dumpViewProperties(context, field.get(view), out,
prefix + property.prefix());
continue;
}
}
if (fieldValue == null) {
fieldValue = field.get(view);
}
writeEntry(out, prefix, field.getName(), "", fieldValue);
} catch (IllegalAccessException e) {
}
}
| private static void | exportMethods(android.content.Context context, java.lang.Object view, java.io.BufferedWriter out, java.lang.Class klass, java.lang.String prefix)
final Method[] methods = getExportedPropertyMethods(klass);
int count = methods.length;
for (int i = 0; i < count; i++) {
final Method method = methods[i];
//noinspection EmptyCatchBlock
try {
// TODO: This should happen on the UI thread
Object methodValue = method.invoke(view, (Object[]) null);
final Class<?> returnType = method.getReturnType();
if (returnType == int.class) {
final ExportedProperty property = sAnnotations.get(method);
if (property.resolveId() && context != null) {
final int id = (Integer) methodValue;
methodValue = resolveId(context, id);
} else {
final IntToString[] mapping = property.mapping();
if (mapping.length > 0) {
final int intValue = (Integer) methodValue;
boolean mapped = false;
int mappingCount = mapping.length;
for (int j = 0; j < mappingCount; j++) {
final IntToString mapper = mapping[j];
if (mapper.from() == intValue) {
methodValue = mapper.to();
mapped = true;
break;
}
}
if (!mapped) {
methodValue = intValue;
}
}
}
} else if (returnType == int[].class) {
final ExportedProperty property = sAnnotations.get(method);
final int[] array = (int[]) methodValue;
final String valuePrefix = prefix + method.getName() + '_";
final String suffix = "()";
exportUnrolledArray(context, out, property, array, valuePrefix, suffix);
} else if (!returnType.isPrimitive()) {
final ExportedProperty property = sAnnotations.get(method);
if (property.deepExport()) {
dumpViewProperties(context, methodValue, out, prefix + property.prefix());
continue;
}
}
writeEntry(out, prefix, method.getName(), "()", methodValue);
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
}
}
| private static void | exportUnrolledArray(android.content.Context context, java.io.BufferedWriter out, android.view.ViewDebug$ExportedProperty property, int[] array, java.lang.String prefix, java.lang.String suffix)
final IntToString[] indexMapping = property.indexMapping();
final boolean hasIndexMapping = indexMapping.length > 0;
final IntToString[] mapping = property.mapping();
final boolean hasMapping = mapping.length > 0;
final boolean resolveId = property.resolveId() && context != null;
final int valuesCount = array.length;
for (int j = 0; j < valuesCount; j++) {
String name;
String value;
final int intValue = array[j];
name = String.valueOf(j);
if (hasIndexMapping) {
int mappingCount = indexMapping.length;
for (int k = 0; k < mappingCount; k++) {
final IntToString mapped = indexMapping[k];
if (mapped.from() == j) {
name = mapped.to();
break;
}
}
}
value = String.valueOf(intValue);
if (hasMapping) {
int mappingCount = mapping.length;
for (int k = 0; k < mappingCount; k++) {
final IntToString mapped = mapping[k];
if (mapped.from() == intValue) {
value = mapped.to();
break;
}
}
}
if (resolveId) {
value = (String) resolveId(context, intValue);
}
writeEntry(out, prefix, name, suffix, value);
}
| private static View | findView(View root, java.lang.String parameter)
// Look by type/hashcode
if (parameter.indexOf('@") != -1) {
final String[] ids = parameter.split("@");
final String className = ids[0];
final int hashCode = Integer.parseInt(ids[1], 16);
View view = root.getRootView();
if (view instanceof ViewGroup) {
return findView((ViewGroup) view, className, hashCode);
}
} else {
// Look by id
final int id = root.getResources().getIdentifier(parameter, null, null);
return root.getRootView().findViewById(id);
}
return null;
| private static View | findView(ViewGroup group, java.lang.String className, int hashCode)
if (isRequestedView(group, className, hashCode)) {
return group;
}
final int count = group.getChildCount();
for (int i = 0; i < count; i++) {
final View view = group.getChildAt(i);
if (view instanceof ViewGroup) {
final View found = findView((ViewGroup) view, className, hashCode);
if (found != null) {
return found;
}
} else if (isRequestedView(view, className, hashCode)) {
return view;
}
}
return null;
| private static java.lang.reflect.Field[] | getExportedPropertyFields(java.lang.Class klass)
if (sFieldsForClasses == null) {
sFieldsForClasses = new HashMap<Class<?>, Field[]>();
}
if (sAnnotations == null) {
sAnnotations = new HashMap<AccessibleObject, ExportedProperty>(512);
}
final HashMap<Class<?>, Field[]> map = sFieldsForClasses;
final HashMap<AccessibleObject, ExportedProperty> annotations = sAnnotations;
Field[] fields = map.get(klass);
if (fields != null) {
return fields;
}
final ArrayList<Field> foundFields = new ArrayList<Field>();
fields = klass.getDeclaredFields();
int count = fields.length;
for (int i = 0; i < count; i++) {
final Field field = fields[i];
if (field.isAnnotationPresent(ExportedProperty.class)) {
field.setAccessible(true);
foundFields.add(field);
annotations.put(field, field.getAnnotation(ExportedProperty.class));
}
}
fields = foundFields.toArray(new Field[foundFields.size()]);
map.put(klass, fields);
return fields;
| private static java.lang.reflect.Method[] | getExportedPropertyMethods(java.lang.Class klass)
if (sMethodsForClasses == null) {
sMethodsForClasses = new HashMap<Class<?>, Method[]>(100);
}
if (sAnnotations == null) {
sAnnotations = new HashMap<AccessibleObject, ExportedProperty>(512);
}
final HashMap<Class<?>, Method[]> map = sMethodsForClasses;
final HashMap<AccessibleObject, ExportedProperty> annotations = sAnnotations;
Method[] methods = map.get(klass);
if (methods != null) {
return methods;
}
final ArrayList<Method> foundMethods = new ArrayList<Method>();
methods = klass.getDeclaredMethods();
int count = methods.length;
for (int i = 0; i < count; i++) {
final Method method = methods[i];
if (method.getParameterTypes().length == 0 &&
method.isAnnotationPresent(ExportedProperty.class) &&
method.getReturnType() != Void.class) {
method.setAccessible(true);
foundMethods.add(method);
annotations.put(method, method.getAnnotation(ExportedProperty.class));
}
}
methods = foundMethods.toArray(new Method[foundMethods.size()]);
map.put(klass, methods);
return methods;
| public static long | getViewInstanceCount()Returns the number of instanciated Views.
return View.sInstanceCount;
| public static long | getViewRootInstanceCount()Returns the number of instanciated ViewRoots.
return ViewRoot.getInstanceCount();
| private static void | invalidate(View root, java.lang.String parameter)
final View view = findView(root, parameter);
if (view != null) {
view.postInvalidate();
}
| private static boolean | isRequestedView(View view, java.lang.String className, int hashCode)
return view.getClass().getName().equals(className) && view.hashCode() == hashCode;
| private static void | profile(View root, java.io.OutputStream clientStream, java.lang.String parameter)
final View view = findView(root, parameter);
BufferedWriter out = null;
try {
out = new BufferedWriter(new OutputStreamWriter(clientStream), 32 * 1024);
if (view != null) {
final long durationMeasure = profileViewOperation(view, new ViewOperation<Void>() {
public Void[] pre() {
forceLayout(view);
return null;
}
private void forceLayout(View view) {
view.forceLayout();
if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
final int count = group.getChildCount();
for (int i = 0; i < count; i++) {
forceLayout(group.getChildAt(i));
}
}
}
public void run(Void... data) {
view.measure(view.mOldWidthMeasureSpec, view.mOldHeightMeasureSpec);
}
public void post(Void... data) {
}
});
final long durationLayout = profileViewOperation(view, new ViewOperation<Void>() {
public Void[] pre() {
return null;
}
public void run(Void... data) {
view.layout(view.mLeft, view.mTop, view.mRight, view.mBottom);
}
public void post(Void... data) {
}
});
final long durationDraw = profileViewOperation(view, new ViewOperation<Object>() {
public Object[] pre() {
final DisplayMetrics metrics = view.getResources().getDisplayMetrics();
final Bitmap bitmap = Bitmap.createBitmap(metrics.widthPixels,
metrics.heightPixels, Bitmap.Config.RGB_565);
final Canvas canvas = new Canvas(bitmap);
return new Object[] { bitmap, canvas };
}
public void run(Object... data) {
view.draw((Canvas) data[1]);
}
public void post(Object... data) {
((Bitmap) data[0]).recycle();
}
});
out.write(String.valueOf(durationMeasure));
out.write(' ");
out.write(String.valueOf(durationLayout));
out.write(' ");
out.write(String.valueOf(durationDraw));
out.newLine();
} else {
out.write("-1 -1 -1");
out.newLine();
}
} catch (Exception e) {
android.util.Log.w("View", "Problem profiling the view:", e);
} finally {
if (out != null) {
out.close();
}
}
| private static long | profileViewOperation(View view, android.view.ViewDebug$ViewOperation operation)
final CountDownLatch latch = new CountDownLatch(1);
final long[] duration = new long[1];
view.post(new Runnable() {
public void run() {
try {
T[] data = operation.pre();
long start = Debug.threadCpuTimeNanos();
operation.run(data);
duration[0] = Debug.threadCpuTimeNanos() - start;
operation.post(data);
} finally {
latch.countDown();
}
}
});
try {
latch.await(CAPTURE_TIMEOUT, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Log.w("View", "Could not complete the profiling of the view " + view);
Thread.currentThread().interrupt();
return -1;
}
return duration[0];
| private static void | requestLayout(View root, java.lang.String parameter)
final View view = findView(root, parameter);
if (view != null) {
root.post(new Runnable() {
public void run() {
view.requestLayout();
}
});
}
| private static java.lang.Object | resolveId(android.content.Context context, int id)
Object fieldValue;
final Resources resources = context.getResources();
if (id >= 0) {
try {
fieldValue = resources.getResourceTypeName(id) + '/" +
resources.getResourceEntryName(id);
} catch (Resources.NotFoundException e) {
fieldValue = "id/0x" + Integer.toHexString(id);
}
} else {
fieldValue = "NO_ID";
}
return fieldValue;
| public static void | startHierarchyTracing(java.lang.String prefix, View view)Starts tracing the view hierarchy of the specified view. The trace is identified by a prefix,
used to build the traces files names: /EXTERNAL/view-hierarchy/PREFIX.traces and
/EXTERNAL/view-hierarchy/PREFIX.tree .
Only one view hierarchy can be traced at the same time. After calling this method, any
other invocation will result in a IllegalStateException unless
{@link #stopHierarchyTracing()} is invoked before.
Calling this method creates the file /EXTERNAL/view-hierarchy/PREFIX.traces
containing all the traces (or method calls) relative to the specified view's hierarchy.
This method will return immediately if TRACE_HIERARCHY is false.
//noinspection PointlessBooleanExpression,ConstantConditions
if (!TRACE_HIERARCHY) {
return;
}
if (sHierarhcyRoot != null) {
throw new IllegalStateException("You must call stopHierarchyTracing() before running" +
" a new trace!");
}
File hierarchyDump = new File(Environment.getExternalStorageDirectory(), "view-hierarchy/");
//noinspection ResultOfMethodCallIgnored
hierarchyDump.mkdirs();
hierarchyDump = new File(hierarchyDump, prefix + ".traces");
sHierarchyTracePrefix = prefix;
try {
sHierarchyTraces = new BufferedWriter(new FileWriter(hierarchyDump), 8 * 1024);
} catch (IOException e) {
Log.e("View", "Could not dump view hierarchy");
return;
}
sHierarhcyRoot = (ViewRoot) view.getRootView().getParent();
| public static void | startRecyclerTracing(java.lang.String prefix, View view)Starts tracing the view recycler of the specified view. The trace is identified by a prefix,
used to build the traces files names: /EXTERNAL/view-recycler/PREFIX.traces and
/EXTERNAL/view-recycler/PREFIX.recycler .
Only one view recycler can be traced at the same time. After calling this method, any
other invocation will result in a IllegalStateException unless
{@link #stopRecyclerTracing()} is invoked before.
Traces files are created only after {@link #stopRecyclerTracing()} is invoked.
This method will return immediately if TRACE_RECYCLER is false.
//noinspection PointlessBooleanExpression,ConstantConditions
if (!TRACE_RECYCLER) {
return;
}
if (sRecyclerOwnerView != null) {
throw new IllegalStateException("You must call stopRecyclerTracing() before running" +
" a new trace!");
}
sRecyclerTracePrefix = prefix;
sRecyclerOwnerView = view;
sRecyclerViews = new ArrayList<View>();
sRecyclerTraces = new LinkedList<RecyclerTrace>();
| public static void | stopHierarchyTracing()Stops the current view hierarchy tracing. This method closes the file
/EXTERNAL/view-hierarchy/PREFIX.traces .
Calling this method creates the file /EXTERNAL/view-hierarchy/PREFIX.tree
containing the view hierarchy of the view supplied to
{@link #startHierarchyTracing(String, View)}.
This method will return immediately if TRACE_HIERARCHY is false.
//noinspection PointlessBooleanExpression,ConstantConditions
if (!TRACE_HIERARCHY) {
return;
}
if (sHierarhcyRoot == null || sHierarchyTraces == null) {
throw new IllegalStateException("You must call startHierarchyTracing() before" +
" stopHierarchyTracing()!");
}
try {
sHierarchyTraces.close();
} catch (IOException e) {
Log.e("View", "Could not write view traces");
}
sHierarchyTraces = null;
File hierarchyDump = new File(Environment.getExternalStorageDirectory(), "view-hierarchy/");
//noinspection ResultOfMethodCallIgnored
hierarchyDump.mkdirs();
hierarchyDump = new File(hierarchyDump, sHierarchyTracePrefix + ".tree");
BufferedWriter out;
try {
out = new BufferedWriter(new FileWriter(hierarchyDump), 8 * 1024);
} catch (IOException e) {
Log.e("View", "Could not dump view hierarchy");
return;
}
View view = sHierarhcyRoot.getView();
if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
dumpViewHierarchy(group, out, 0);
try {
out.close();
} catch (IOException e) {
Log.e("View", "Could not dump view hierarchy");
}
}
sHierarhcyRoot = null;
| public static void | stopRecyclerTracing()Stops the current view recycer tracing.
Calling this method creates the file /EXTERNAL/view-recycler/PREFIX.traces
containing all the traces (or method calls) relative to the specified view's recycler.
Calling this method creates the file /EXTERNAL/view-recycler/PREFIX.recycler
containing all of the views used by the recycler of the view supplied to
{@link #startRecyclerTracing(String, View)}.
This method will return immediately if TRACE_RECYCLER is false.
//noinspection PointlessBooleanExpression,ConstantConditions
if (!TRACE_RECYCLER) {
return;
}
if (sRecyclerOwnerView == null || sRecyclerViews == null) {
throw new IllegalStateException("You must call startRecyclerTracing() before" +
" stopRecyclerTracing()!");
}
File recyclerDump = new File(Environment.getExternalStorageDirectory(), "view-recycler/");
//noinspection ResultOfMethodCallIgnored
recyclerDump.mkdirs();
recyclerDump = new File(recyclerDump, sRecyclerTracePrefix + ".recycler");
try {
final BufferedWriter out = new BufferedWriter(new FileWriter(recyclerDump), 8 * 1024);
for (View view : sRecyclerViews) {
final String name = view.getClass().getName();
out.write(name);
out.newLine();
}
out.close();
} catch (IOException e) {
Log.e("View", "Could not dump recycler content");
return;
}
recyclerDump = new File(Environment.getExternalStorageDirectory(), "view-recycler/");
recyclerDump = new File(recyclerDump, sRecyclerTracePrefix + ".traces");
try {
final FileOutputStream file = new FileOutputStream(recyclerDump);
final DataOutputStream out = new DataOutputStream(file);
for (RecyclerTrace trace : sRecyclerTraces) {
out.writeInt(trace.view);
out.writeInt(trace.type.ordinal());
out.writeInt(trace.position);
out.writeInt(trace.indexOnScreen);
out.flush();
}
out.close();
} catch (IOException e) {
Log.e("View", "Could not dump recycler traces");
return;
}
sRecyclerViews.clear();
sRecyclerViews = null;
sRecyclerTraces.clear();
sRecyclerTraces = null;
sRecyclerOwnerView = null;
| public static void | trace(View view, android.view.ViewDebug$RecyclerTraceType type, int parameters)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.
if (sRecyclerOwnerView == null || sRecyclerViews == null) {
return;
}
if (!sRecyclerViews.contains(view)) {
sRecyclerViews.add(view);
}
final int index = sRecyclerViews.indexOf(view);
RecyclerTrace trace = new RecyclerTrace();
trace.view = index;
trace.type = type;
trace.position = parameters[0];
trace.indexOnScreen = parameters[1];
sRecyclerTraces.add(trace);
| public static void | trace(View view, android.view.ViewDebug$HierarchyTraceType type)Outputs a trace to the currently opened traces file. The trace contains the class name
and instance's hashcode of the specified view as well as the supplied trace type.
if (sHierarchyTraces == null) {
return;
}
try {
sHierarchyTraces.write(type.name());
sHierarchyTraces.write(' ");
sHierarchyTraces.write(view.getClass().getName());
sHierarchyTraces.write('@");
sHierarchyTraces.write(Integer.toHexString(view.hashCode()));
sHierarchyTraces.newLine();
} catch (IOException e) {
Log.w("View", "Error while dumping trace of type " + type + " for view " + view);
}
| private static void | writeEntry(java.io.BufferedWriter out, java.lang.String prefix, java.lang.String name, java.lang.String suffix, java.lang.Object value)
out.write(prefix);
out.write(name);
out.write(suffix);
out.write("=");
writeValue(out, value);
out.write(' ");
| private static void | writeValue(java.io.BufferedWriter out, java.lang.Object value)
if (value != null) {
String output = value.toString().replace("\n", "\\n");
out.write(String.valueOf(output.length()));
out.write(",");
out.write(output);
} else {
out.write("4,null");
}
|
|