转载请注明:http://blog.csdn.net/liaoqianchuan00/article/details/23542373
Hierarchy Viewer允许你调试和优化你的UI界面,他向开发人员展示一个视图的层次结构,以及每个laytout中节点的performance信息。
打开Hierarchy View:Window->Open Perspective->Other..->HierarchyView.
选择我们要分析程序。点击下图所示红色图标位置。可以看见右边有3个面板,有Tree View, Tree Overview, Layout View。如果没有显示这些面板,可以在Window->Show View中选择打开。
l 可以放大缩小TreeView,也可以点击拖拽来查看其他节点。
l 在Filter byclass or id输入框中输入string来查找相应的class或者id,比如查找textView3.找到的节点背景色将变为蓝色。
l 也可以点击TreeView面板上方的按钮来保存图片。
l 每个View节点中可以显示ViewClass,Object 地址,View ID,Performance的圆圈,View序号(在父控件中的序号,从0开始)
l 点击TreeView中得ListView节点,可以看见他上方出现了一个屏幕上该控件的缩略图。如下图。我们也可以点击Layout view中得相应控件来快速找到一个View。
l 接着我们点击下图中红色方框中得图标,可以看到ListView中包含的控件节点都多了3个原点,这3个原点有绿黄红三种颜色,黄色和红色表示耗时久。绿色正常,而第一个点表示计算这个View size的时间。第二个点表示创建这个view的时间。第三个点表示在屏幕上画这个view的消耗时间。
你可以使用Tree OverView来更方便的移动TreeView视图。
你选中的View节点的属性列表,你可以很方便的查看这些属性而不需要去查看源代码。
另外一种展现View结构的方式,当你在TreeView中选中一个节点的时候,Layout View中会用红色来显示选中View的位置和大小等。在这个面板中,当你选中一个View的时候,会同时显示红色,白色,和浅红色区域,
l 红色:当前选中的view
l 浅红色:当前选中View的父控件。
l 白色:既不是当前选中View的父控件,也不是他得子空间。
为了安全性的保护,Hierarchy只能连接到开发者版本的android系统,如果你的机器是在商店或者运营商提供的机器,那么就会连接失败。解决方法是可以root你的机器,或者是用到ViewServer。
[2014-04-12 14:45:53 - ViewServerDevice]Unable todebug device: samsung-sm_n9008v-5d1a632d
[2014-04-12 14:45:53 - hierarchyviewer]Missingforwarded port for 5d1a632d。
1. 从https://github.com/romainguy/ViewServer处下载ViewServer文件,加入项目中,
2. 并且在Manifest中添加权限
<uses-permissionandroid:name="android.permission.INTERNET"/>
3. 在相应的Activity的onCreate,OnDestroy,onResume中添加相应的方法。
具体方法可参照ViewServer类的javadoc。
/*
* Copyright(C) 2011 The Android Open Source Project
*
* Licensedunder the Apache License, Version 2.0 (the "License");
* you may notuse this file except in compliance with the License.
* You mayobtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unlessrequired by applicable law or agreed to in writing, software
* distributedunder the License is distributed on an "AS IS" BASIS,
* WITHOUTWARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See theLicense for the specific language governing permissions and
* limitationsunder the License.
*/
package com.example.androidtest;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
importjava.util.concurrent.locks.ReentrantReadWriteLock;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewDebug;
/**
*<p>This class can be used to enable the use of HierarchyViewer inside an
* application.HierarchyViewer is an Android SDK tool that can be used
* to inspectand debug the user interface of running applications. For
* securityreasons, HierarchyViewer does not work on production builds
* (forinstance phones bought in store.) By using this class, you can
* makeHierarchyViewer work on any device. You must be very careful
* however toonly enable HierarchyViewer when debugging your
*application.</p>
*
* <p>Touse this view server, your application must require the INTERNET
*permission.</p>
*
* <p>Therecommended way to use this API is to register activities when
* they arecreated, and to unregister them when they get destroyed:</p>
*
* <pre>
* public classMyActivity extends Activity {
* public void onCreate(BundlesavedInstanceState) {
* super.onCreate(savedInstanceState);
* // Set content view, etc.
* ViewServer.get(this).addWindow(this);
* }
*
* public void onDestroy() {
* super.onDestroy();
* ViewServer.get(this).removeWindow(this);
* }
*
* public void onResume() {
* super.onResume();
* ViewServer.get(this).setFocusedWindow(this);
* }
* }
* </pre>
*
* <p>
* In a similarfashion, you can use this API with an InputMethodService:
* </p>
*
* <pre>
* public classMyInputMethodService extends InputMethodService {
* public void onCreate() {
* super.onCreate();
* View decorView =getWindow().getWindow().getDecorView();
* String name ="MyInputMethodService";
* ViewServer.get(this).addWindow(decorView, name);
* }
*
* public void onDestroy() {
* super.onDestroy();
* View decorView =getWindow().getWindow().getDecorView();
* ViewServer.get(this).removeWindow(decorView);
* }
*
* public void onStartInput(EditorInfoattribute, boolean restarting) {
* super.onStartInput(attribute,restarting);
* View decorView =getWindow().getWindow().getDecorView();
* ViewServer.get(this).setFocusedWindow(decorView);
* }
* }
* </pre>
*/
public class ViewServer implements Runnable {
/**
* Thedefault port used to start view servers.
*/
privatestatic final int VIEW_SERVER_DEFAULT_PORT = 4939;
privatestatic final int VIEW_SERVER_MAX_CONNECTIONS = 10;
privatestatic final String BUILD_TYPE_USER = "user";
// Debugfacility
privatestatic final String LOG_TAG = "ViewServer";
privatestatic final String VALUE_PROTOCOL_VERSION = "4";
privatestatic final String VALUE_SERVER_VERSION = "4";
// Protocolcommands
// Returnsthe protocol version
privatestatic final String COMMAND_PROTOCOL_VERSION = "PROTOCOL";
// Returnsthe server version
privatestatic final String COMMAND_SERVER_VERSION = "SERVER";
// Listsall of the available windows in the system
private staticfinal String COMMAND_WINDOW_MANAGER_LIST = "LIST";
// Keeps aconnection open and notifies when the list of windows changes
privatestatic final String COMMAND_WINDOW_MANAGER_AUTOLIST = "AUTOLIST";
// Returnsthe focused window
privatestatic final String COMMAND_WINDOW_MANAGER_GET_FOCUS = "GET_FOCUS";
privateServerSocket mServer;
privatefinal int mPort;
privateThread mThread;
privateExecutorService mThreadPool;
privatefinal List<WindowListener> mListeners =
newCopyOnWriteArrayList<ViewServer.WindowListener>();
privatefinal HashMap<View, String> mWindows = new HashMap<View, String>();
privatefinal ReentrantReadWriteLock mWindowsLock = new ReentrantReadWriteLock();
privateView mFocusedWindow;
privatefinal ReentrantReadWriteLock mFocusLock = new ReentrantReadWriteLock();
privatestatic ViewServer sServer;
/**
* Returnsa unique instance of the ViewServer. This method should only be
* calledfrom the main thread of your application. The server will have
* the samelifetime as your process.
*
* If yourapplication does not have the <code>android:debuggable</code>
* flag setin its manifest, the server returned by this method will
* be adummy object that does not do anything. This allows you to use
* the samecode in debug and release versions of your application.
*
* @paramcontext A Context used to check whether the application is
* debuggable, this can be theapplication context
*/
publicstatic ViewServer get(Context context) {
ApplicationInfo info = context.getApplicationInfo();
if(BUILD_TYPE_USER.equals(Build.TYPE) &&
(info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
if(sServer == null) {
sServer = new ViewServer(ViewServer.VIEW_SERVER_DEFAULT_PORT);
}
if(!sServer.isRunning()) {
try {
sServer.start();
} catch (IOException e) {
Log.d(LOG_TAG, "Error:", e);
}
}
} else{
sServer = new NoopViewServer();
}
returnsServer;
}
privateViewServer() {
mPort =-1;
}
/**
* Createsa new ViewServer associated with the specified window manager on the
*specified local port. The server is not started by default.
*
* @paramport The port for the server to listen to.
*
* @see #start()
*/
privateViewServer(int port) {
mPort =port;
}
/**
* Startsthe server.
*
* @returnTrue if the server was successfully created, or false if it already exists.
* @throwsIOException If the server cannot be created.
*
* @see#stop()
* @see#isRunning()
* @seeWindowManagerService#startViewServer(int)
*/
publicboolean start() throws IOException {
if(mThread != null) {
return false;
}
mThread = new Thread(this, "LocalView Server [port=" + mPort + "]");
mThreadPool = Executors.newFixedThreadPool(VIEW_SERVER_MAX_CONNECTIONS);
mThread.start();
returntrue;
}
/**
* Stopsthe server.
*
* @return True if the server was stopped,false if an error occurred or if the
* server wasn't started.
*
* @see#start()
* @see#isRunning()
* @seeWindowManagerService#stopViewServer()
*/
publicboolean stop() {
if(mThread != null) {
mThread.interrupt();
if(mThreadPool != null) {
try {
mThreadPool.shutdownNow();
} catch (SecurityException e) {
Log.w(LOG_TAG, "Could not stop all view server threads");
}
}
mThreadPool = null;
mThread = null;
try{
mServer.close();
mServer = null;
return true;
} catch (IOException e) {
Log.w(LOG_TAG, "Could not close the view server");
}
}
mWindowsLock.writeLock().lock();
try {
mWindows.clear();
}finally {
mWindowsLock.writeLock().unlock();
}
mFocusLock.writeLock().lock();
try {
mFocusedWindow = null;
}finally {
mFocusLock.writeLock().unlock();
}
returnfalse;
}
/**
*Indicates whether the server is currently running.
*
* @returnTrue if the server is running, false otherwise.
*
* @see#start()
* @see#stop()
* @seeWindowManagerService#isViewServerRunning()
*/
publicboolean isRunning() {
return mThread != null &&mThread.isAlive();
}
/**
* Invokethis method to register a new view hierarchy.
*
* @paramactivity The activity whose view hierarchy/window to register
*
* @see#addWindow(View, String)
* @see #removeWindow(Activity)
*/
public voidaddWindow(Activity activity) {
Stringname = activity.getTitle().toString();
if(TextUtils.isEmpty(name)) {
name = activity.getClass().getCanonicalName() +
"/0x" +System.identityHashCode(activity);
} else{
name += "(" + activity.getClass().getCanonicalName() +")";
}
addWindow(activity.getWindow().getDecorView(), name);
}
/**
* Invokethis method to unregister a view hierarchy.
*
* @paramactivity The activity whose view hierarchy/window to unregister
*
* @see#addWindow(Activity)
* @see#removeWindow(View)
*/
public voidremoveWindow(Activity activity) {
removeWindow(activity.getWindow().getDecorView());
}
/**
* Invokethis method to register a new view hierarchy.
*
* @paramview A view that belongs to the view hierarchy/window to register
* @namename The name of the view hierarchy/window to register
*
* @see#removeWindow(View)
*/
public voidaddWindow(View view, String name) {
mWindowsLock.writeLock().lock();
try {
mWindows.put(view.getRootView(), name);
}finally {
mWindowsLock.writeLock().unlock();
}
fireWindowsChangedEvent();
}
/**
* Invokethis method to unregister a view hierarchy.
*
* @paramview A view that belongs to the view hierarchy/window to unregister
*
* @see#addWindow(View, String)
*/
public voidremoveWindow(View view) {
mWindowsLock.writeLock().lock();
try {
mWindows.remove(view.getRootView());
}finally {
mWindowsLock.writeLock().unlock();
}
fireWindowsChangedEvent();
}
/**
* Invokethis method to change the currently focused window.
*
* @paramactivity The activity whose view hierarchy/window hasfocus,
* or null to remove focus
*/
public voidsetFocusedWindow(Activity activity) {
setFocusedWindow(activity.getWindow().getDecorView());
}
/**
* Invokethis method to change the currently focused window.
*
* @paramview A view that belongs to the view hierarchy/window that has focus,
* or null to remove focus
*/
public voidsetFocusedWindow(View view) {
mFocusLock.writeLock().lock();
try {
mFocusedWindow = view == null ? null : view.getRootView();
}finally {
mFocusLock.writeLock().unlock();
}
fireFocusChangedEvent();
}
/**
* Mainserver loop.
*/
public voidrun() {
try {
mServer = new ServerSocket(mPort, VIEW_SERVER_MAX_CONNECTIONS,InetAddress.getLocalHost());
} catch(Exception e) {
Log.w(LOG_TAG, "Starting ServerSocket error: ", e);
}
while(mServer != null && Thread.currentThread() == mThread) {
//Any uncaught exception will crash the system process
try{
Socket client = mServer.accept();
if (mThreadPool != null) {
mThreadPool.submit(new ViewServerWorker(client));
} else {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}catch (Exception e) {
Log.w(LOG_TAG, "Connection error: ", e);
}
}
}
privatestatic boolean writeValue(Socket client, String value) {
booleanresult;
BufferedWriter out = null;
try {
OutputStream clientStream = client.getOutputStream();
out= new BufferedWriter(new OutputStreamWriter(clientStream), 8 * 1024);
out.write(value);
out.write("\n");
out.flush();
result = true;
} catch(Exception e) {
result = false;
}finally {
if(out != null) {
try {
out.close();
} catch (IOException e) {
result = false;
}
}
}
returnresult;
}
privatevoid fireWindowsChangedEvent() {
for(WindowListener listener : mListeners) {
listener.windowsChanged();
}
}
privatevoid fireFocusChangedEvent() {
for(WindowListener listener : mListeners) {
listener.focusChanged();
}
}
privatevoid addWindowListener(WindowListener listener) {
if(!mListeners.contains(listener)) {
mListeners.add(listener);
}
}
privatevoid removeWindowListener(WindowListener listener) {
mListeners.remove(listener);
}
privateinterface WindowListener {
voidwindowsChanged();
voidfocusChanged();
}
privatestatic class UncloseableOutputStream extends OutputStream {
privatefinal OutputStream mStream;
UncloseableOutputStream(OutputStream stream) {
mStream = stream;
}
publicvoid close() throws IOException {
//Don't close the stream
}
public boolean equals(Object o) {
return mStream.equals(o);
}
publicvoid flush() throws IOException {
mStream.flush();
}
publicint hashCode() {
return mStream.hashCode();
}
public String toString() {
return mStream.toString();
}
publicvoid write(byte[] buffer, int offset, int count)
throws IOException {
mStream.write(buffer, offset, count);
}
publicvoid write(byte[] buffer) throws IOException {
mStream.write(buffer);
}
publicvoid write(int oneByte) throws IOException {
mStream.write(oneByte);
}
}
privatestatic class NoopViewServer extends ViewServer {
privateNoopViewServer() {
}
@Override
publicboolean start() throws IOException {
return false;
}
@Override
publicboolean stop() {
return false;
}
@Override
publicboolean isRunning() {
return false;
}
@Override
publicvoid addWindow(Activity activity) {
}
@Override
publicvoid removeWindow(Activity activity) {
}
@Override
publicvoid addWindow(View view, String name) {
}
@Override
publicvoid removeWindow(View view) {
}
@Override
publicvoid setFocusedWindow(Activity activity) {
}
@Override
publicvoid setFocusedWindow(View view) {
}
@Override
publicvoid run() {
}
}
privateclass ViewServerWorker implements Runnable, WindowListener {
privateSocket mClient;
privateboolean mNeedWindowListUpdate;
privateboolean mNeedFocusedWindowUpdate;
privatefinal Object[] mLock = new Object[0];
publicViewServerWorker(Socket client) {
mClient = client;
mNeedWindowListUpdate = false;
mNeedFocusedWindowUpdate = false;
}
publicvoid run() {
BufferedReader in = null;
try{
in = new BufferedReader(new InputStreamReader(mClient.getInputStream()),1024);
final String request = in.readLine();
String command;
String parameters;
int index = request.indexOf(' ');
if (index == -1) {
command = request;
parameters = "";
} else {
command = request.substring(0, index);
parameters = request.substring(index + 1);
}
boolean result;
if (COMMAND_PROTOCOL_VERSION.equalsIgnoreCase(command)) {
result = writeValue(mClient, VALUE_PROTOCOL_VERSION);
} else if (COMMAND_SERVER_VERSION.equalsIgnoreCase(command)) {
result = writeValue(mClient, VALUE_SERVER_VERSION);
} else if(COMMAND_WINDOW_MANAGER_LIST.equalsIgnoreCase(command)) {
result = listWindows(mClient);
} else if (COMMAND_WINDOW_MANAGER_GET_FOCUS.equalsIgnoreCase(command)) {
result = getFocusedWindow(mClient);
} else if (COMMAND_WINDOW_MANAGER_AUTOLIST.equalsIgnoreCase(command)) {
result = windowManagerAutolistLoop();
} else {
result = windowCommand(mClient, command, parameters);
}
if (!result) {
Log.w(LOG_TAG, "An error occurred with the command: " +command);
}
}catch(IOException e) {
Log.w(LOG_TAG, "Connection error: ", e);
}finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (mClient != null) {
try {
mClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
privateboolean windowCommand(Socket client, String command, String parameters) {
boolean success = true;
BufferedWriter out = null;
try{
// Find the hash code of the window
int index = parameters.indexOf(' ');
if (index == -1) {
index = parameters.length();
}
final String code = parameters.substring(0, index);
int hashCode = (int) Long.parseLong(code, 16);
// Extract the command's parameter after the window description
if (index < parameters.length()) {
parameters = parameters.substring(index + 1);
} else {
parameters = "";
}
final View window = findWindow(hashCode);
if (window == null) {
return false;
}
// call stuff
final Method dispatch =ViewDebug.class.getDeclaredMethod("dispatchCommand",
View.class,String.class, String.class, OutputStream.class);
dispatch.setAccessible(true);
dispatch.invoke(null, window, command, parameters,
newUncloseableOutputStream(client.getOutputStream()));
if (!client.isOutputShutdown()) {
out = new BufferedWriter(newOutputStreamWriter(client.getOutputStream()));
out.write("DONE\n");
out.flush();
}
}catch (Exception e) {
Log.w(LOG_TAG, "Could not send command " + command +
" with parameters" + parameters, e);
success = false;
}finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
success = false;
}
}
}
return success;
}
privateView findWindow(int hashCode) {
if(hashCode == -1) {
View window = null;
mWindowsLock.readLock().lock();
try {
window = mFocusedWindow;
} finally {
mWindowsLock.readLock().unlock();
}
return window;
}
mWindowsLock.readLock().lock();
try {
for (Entry<View, String> entry : mWindows.entrySet()) {
if (System.identityHashCode(entry.getKey()) == hashCode) {
return entry.getKey();
}
}
} finally {
mWindowsLock.readLock().unlock();
}
return null;
}
privateboolean listWindows(Socket client) {
boolean result = true;
BufferedWriter out = null;
try{
mWindowsLock.readLock().lock();
OutputStream clientStream = client.getOutputStream();
out = new BufferedWriter(new OutputStreamWriter(clientStream), 8 *1024);
for (Entry<View, String> entry : mWindows.entrySet()) {
out.write(Integer.toHexString(System.identityHashCode(entry.getKey())));
out.write(' ');
out.append(entry.getValue());
out.write('\n');
}
out.write("DONE.\n");
out.flush();
}catch (Exception e) {
result = false;
}finally {
mWindowsLock.readLock().unlock();
if (out != null) {
try {
out.close();
} catch (IOException e) {
result = false;
}
}
}
return result;
}
privateboolean getFocusedWindow(Socket client) {
boolean result = true;
String focusName = null;
BufferedWriter out = null;
try{
OutputStream clientStream = client.getOutputStream();
out = new BufferedWriter(new OutputStreamWriter(clientStream), 8 *1024);
View focusedWindow = null;
mFocusLock.readLock().lock();
try {
focusedWindow = mFocusedWindow;
} finally {
mFocusLock.readLock().unlock();
}
if (focusedWindow != null) {
mWindowsLock.readLock().lock();
try {
focusName =mWindows.get(mFocusedWindow);
} finally {
mWindowsLock.readLock().unlock();
}
out.write(Integer.toHexString(System.identityHashCode(focusedWindow)));
out.write(' ');
out.append(focusName);
}
out.write('\n');
out.flush();
}catch (Exception e) {
result = false;
}finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
result = false;
}
}
}
return result;
}
publicvoid windowsChanged() {
synchronized (mLock) {
mNeedWindowListUpdate = true;
mLock.notifyAll();
}
}
publicvoid focusChanged() {
synchronized (mLock) {
mNeedFocusedWindowUpdate = true;
mLock.notifyAll();
}
}
privateboolean windowManagerAutolistLoop() {
addWindowListener(this);
BufferedWriter out = null;
try{
out = new BufferedWriter(newOutputStreamWriter(mClient.getOutputStream()));
while (!Thread.interrupted()) {
boolean needWindowListUpdate = false;
boolean needFocusedWindowUpdate = false;
synchronized (mLock) {
while(!mNeedWindowListUpdate && !mNeedFocusedWindowUpdate) {
mLock.wait();
}
if (mNeedWindowListUpdate) {
mNeedWindowListUpdate = false;
needWindowListUpdate = true;
}
if(mNeedFocusedWindowUpdate) {
mNeedFocusedWindowUpdate = false;
needFocusedWindowUpdate = true;
}
}
if (needWindowListUpdate) {
out.write("LISTUPDATE\n");
out.flush();
}
if (needFocusedWindowUpdate) {
out.write("FOCUSUPDATE\n");
out.flush();
}
}
}catch (Exception e) {
Log.w(LOG_TAG, "Connection error: ", e);
}finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
// Ignore
}
}
removeWindowListener(this);
}
return true;
}
}
}