HierarchyViewer 에서 사용 할 수 없 는 문제 해결
48395 단어 androidHierarchyV
Tip: 이 방법 을 사용 하면 HierarchyViewer 에 View 를 몇 층 더 추가 할 수 있 습 니 다. 앞의 몇 층 이 자신의 View 가 아 닌 지 주의해 야 합 니 다.
/* * Copyright (C) 2011 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 com.xmn.util;
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;
import java.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 inspect and debug the user interface of running applications. For * security reasons, HierarchyViewer does not work on production builds * (for instance phones bought in store.) By using this class, you can * make HierarchyViewer work on any device. You must be very careful * however to only enable HierarchyViewer when debugging your * application.</p> * * <p>To use this view server, your application must require the INTERNET * permission.</p> * * <p>The recommended way to use this API is to register activities when * they are created, and to unregister them when they get destroyed:</p> * * <pre> * public class MyActivity extends Activity { * public void onCreate(Bundle savedInstanceState) { * 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 similar fashion, you can use this API with an InputMethodService: * </p> * * <pre> * public class MyInputMethodService 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(EditorInfo attribute, boolean restarting) { * super.onStartInput(attribute, restarting); * View decorView = getWindow().getWindow().getDecorView(); * ViewServer.get(this).setFocusedWindow(decorView); * } * } * </pre> */
public class ViewServer implements Runnable {
/** * The default port used to start view servers. */
private static final int VIEW_SERVER_DEFAULT_PORT = 4939;
private static final int VIEW_SERVER_MAX_CONNECTIONS = 10;
private static final String BUILD_TYPE_USER = "user";
// Debug facility
private static final String LOG_TAG = "ViewServer";
private static final String VALUE_PROTOCOL_VERSION = "4";
private static final String VALUE_SERVER_VERSION = "4";
// Protocol commands
// Returns the protocol version
private static final String COMMAND_PROTOCOL_VERSION = "PROTOCOL";
// Returns the server version
private static final String COMMAND_SERVER_VERSION = "SERVER";
// Lists all of the available windows in the system
private static final String COMMAND_WINDOW_MANAGER_LIST = "LIST";
// Keeps a connection open and notifies when the list of windows changes
private static final String COMMAND_WINDOW_MANAGER_AUTOLIST = "AUTOLIST";
// Returns the focused window
private static final String COMMAND_WINDOW_MANAGER_GET_FOCUS = "GET_FOCUS";
private ServerSocket mServer;
private final int mPort;
private Thread mThread;
private ExecutorService mThreadPool;
private final List<WindowListener> mListeners =
new CopyOnWriteArrayList<ViewServer.WindowListener>();
private final HashMap<View, String> mWindows = new HashMap<View, String>();
private final ReentrantReadWriteLock mWindowsLock = new ReentrantReadWriteLock();
private View mFocusedWindow;
private final ReentrantReadWriteLock mFocusLock = new ReentrantReadWriteLock();
private static ViewServer sServer;
/** * Returns a unique instance of the ViewServer. This method should only be * called from the main thread of your application. The server will have * the same lifetime as your process. * * If your application does not have the <code>android:debuggable</code> * flag set in its manifest, the server returned by this method will * be a dummy object that does not do anything. This allows you to use * the same code in debug and release versions of your application. * * @param context A Context used to check whether the application is * debuggable, this can be the application context */
public static 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();
}
return sServer;
}
private ViewServer() {
mPort = -1;
}
/** * Creates a new ViewServer associated with the specified window manager on the * specified local port. The server is not started by default. * * @param port The port for the server to listen to. * * @see #start() */
private ViewServer(int port) {
mPort = port;
}
/** * Starts the server. * * @return True if the server was successfully created, or false if it already exists. * @throws IOException If the server cannot be created. * * @see #stop() * @see #isRunning() * @see WindowManagerService#startViewServer(int) */
public boolean start() throws IOException {
if (mThread != null) {
return false;
}
mThread = new Thread(this, "Local View Server [port=" + mPort + "]");
mThreadPool = Executors.newFixedThreadPool(VIEW_SERVER_MAX_CONNECTIONS);
mThread.start();
return true;
}
/** * Stops the server. * * @return True if the server was stopped, false if an error occurred or if the * server wasn't started. * * @see #start() * @see #isRunning() * @see WindowManagerService#stopViewServer() */
public boolean 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();
}
return false;
}
/** * Indicates whether the server is currently running. * * @return True if the server is running, false otherwise. * * @see #start() * @see #stop() * @see WindowManagerService#isViewServerRunning() */
public boolean isRunning() {
return mThread != null && mThread.isAlive();
}
/** * Invoke this method to register a new view hierarchy. * * @param activity The activity whose view hierarchy/window to register * * @see #addWindow(View, String) * @see #removeWindow(Activity) */
public void addWindow(Activity activity) {
String name = 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);
}
/** * Invoke this method to unregister a view hierarchy. * * @param activity The activity whose view hierarchy/window to unregister * * @see #addWindow(Activity) * @see #removeWindow(View) */
public void removeWindow(Activity activity) {
removeWindow(activity.getWindow().getDecorView());
}
/** * Invoke this method to register a new view hierarchy. * * @param view A view that belongs to the view hierarchy/window to register * @name name The name of the view hierarchy/window to register * * @see #removeWindow(View) */
public void addWindow(View view, String name) {
mWindowsLock.writeLock().lock();
try {
mWindows.put(view.getRootView(), name);
} finally {
mWindowsLock.writeLock().unlock();
}
fireWindowsChangedEvent();
}
/** * Invoke this method to unregister a view hierarchy. * * @param view A view that belongs to the view hierarchy/window to unregister * * @see #addWindow(View, String) */
public void removeWindow(View view) {
mWindowsLock.writeLock().lock();
try {
mWindows.remove(view.getRootView());
} finally {
mWindowsLock.writeLock().unlock();
}
fireWindowsChangedEvent();
}
/** * Invoke this method to change the currently focused window. * * @param activity The activity whose view hierarchy/window hasfocus, * or null to remove focus */
public void setFocusedWindow(Activity activity) {
setFocusedWindow(activity.getWindow().getDecorView());
}
/** * Invoke this method to change the currently focused window. * * @param view A view that belongs to the view hierarchy/window that has focus, * or null to remove focus */
public void setFocusedWindow(View view) {
mFocusLock.writeLock().lock();
try {
mFocusedWindow = view == null ? null : view.getRootView();
} finally {
mFocusLock.writeLock().unlock();
}
fireFocusChangedEvent();
}
/** * Main server loop. */
public void run() {
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);
}
}
}
private static boolean writeValue(Socket client, String value) {
boolean result;
BufferedWriter out = null;
try {
OutputStream clientStream = client.getOutputStream();
out = new BufferedWriter(new OutputStreamWriter(clientStream), 8 * 1024);
out.write(value);
out.write("
");
out.flush();
result = true;
} catch (Exception e) {
result = false;
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
result = false;
}
}
}
return result;
}
private void fireWindowsChangedEvent() {
for (WindowListener listener : mListeners) {
listener.windowsChanged();
}
}
private void fireFocusChangedEvent() {
for (WindowListener listener : mListeners) {
listener.focusChanged();
}
}
private void addWindowListener(WindowListener listener) {
if (!mListeners.contains(listener)) {
mListeners.add(listener);
}
}
private void removeWindowListener(WindowListener listener) {
mListeners.remove(listener);
}
private interface WindowListener {
void windowsChanged();
void focusChanged();
}
private static class UncloseableOutputStream extends OutputStream {
private final OutputStream mStream;
UncloseableOutputStream(OutputStream stream) {
mStream = stream;
}
public void close() throws IOException {
// Don't close the stream
}
public boolean equals(Object o) {
return mStream.equals(o);
}
public void flush() throws IOException {
mStream.flush();
}
public int hashCode() {
return mStream.hashCode();
}
public String toString() {
return mStream.toString();
}
public void write(byte[] buffer, int offset, int count)
throws IOException {
mStream.write(buffer, offset, count);
}
public void write(byte[] buffer) throws IOException {
mStream.write(buffer);
}
public void write(int oneByte) throws IOException {
mStream.write(oneByte);
}
}
private static class NoopViewServer extends ViewServer {
private NoopViewServer() {
}
@Override
public boolean start() throws IOException {
return false;
}
@Override
public boolean stop() {
return false;
}
@Override
public boolean isRunning() {
return false;
}
@Override
public void addWindow(Activity activity) {
}
@Override
public void removeWindow(Activity activity) {
}
@Override
public void addWindow(View view, String name) {
}
@Override
public void removeWindow(View view) {
}
@Override
public void setFocusedWindow(Activity activity) {
}
@Override
public void setFocusedWindow(View view) {
}
@Override
public void run() {
}
}
private class ViewServerWorker implements Runnable, WindowListener {
private Socket mClient;
private boolean mNeedWindowListUpdate;
private boolean mNeedFocusedWindowUpdate;
private final Object[] mLock = new Object[0];
public ViewServerWorker(Socket client) {
mClient = client;
mNeedWindowListUpdate = false;
mNeedFocusedWindowUpdate = false;
}
public void 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();
}
}
}
}
private boolean 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,
new UncloseableOutputStream(client.getOutputStream()));
if (!client.isOutputShutdown()) {
out = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
out.write("DONE
");
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;
}
private View 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;
}
private boolean 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('
');
}
out.write("DONE.
");
out.flush();
} catch (Exception e) {
result = false;
} finally {
mWindowsLock.readLock().unlock();
if (out != null) {
try {
out.close();
} catch (IOException e) {
result = false;
}
}
}
return result;
}
private boolean 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('
');
out.flush();
} catch (Exception e) {
result = false;
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
result = false;
}
}
}
return result;
}
public void windowsChanged() {
synchronized (mLock) {
mNeedWindowListUpdate = true;
mLock.notifyAll();
}
}
public void focusChanged() {
synchronized (mLock) {
mNeedFocusedWindowUpdate = true;
mLock.notifyAll();
}
}
private boolean windowManagerAutolistLoop() {
addWindowListener(this);
BufferedWriter out = null;
try {
out = new BufferedWriter(new OutputStreamWriter(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("LIST UPDATE
");
out.flush();
}
if (needFocusedWindowUpdate) {
out.write("FOCUS UPDATE
");
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;
}
}
}
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Kotlin의 기초 - 2부지난 글에서는 Kotlin이 무엇인지, Kotlin의 특징, Kotlin에서 변수 및 데이터 유형을 선언하는 방법과 같은 Kotlin의 기본 개념에 대해 배웠습니다. 유형 변환은 데이터 변수의 한 유형을 다른 데이터...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.