声明:由于本人一直用eng版的真机调试,所以此方法没有用过,记录在这里,有机会验证

-------------------------------------------------------------------

转:http://www.cnblogs.com/coding-way/p/4294225.html

最近在看一个老外写的东西,发现里面有个类,使用这个类可以让任何设备使用HierarchyView。

众所周知,市面上卖的Android设备,一般都不能使用HierarchyView,所以借此机会,了解一下HierarchyView的实现原理,并学习一下老外的解决方法。

HierarchyView的源码在/sdk/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer中,但貌似不全,

所以直接反编译/prebuilts/devtools/tools/lib/hierarchyviewer2lib.jar和/prebuilts/devtools/tools/lib/hierarchyviewer2.jar。

当对设备使用HierarchyView时,HierarchyView会给设备发送一个startViewServer的命令,下面源码时其调用顺序:

HierarchyViewerDirector.class

  public void populateDeviceSelectionModel() {
IDevice[] devices = DeviceBridge.getDevices();
for (IDevice device : devices)
deviceConnected(device);
} public void deviceConnected(final IDevice device)
{
executeInBackground("Connecting device", new Object()
{
public void run() {
if (!device.isOnline())
return;
IHvDevice hvDevice;
synchronized (HierarchyViewerDirector.mDevicesLock) {
hvDevice = (IHvDevice)HierarchyViewerDirector.this.mDevices.get(device);
if (hvDevice == null) {
hvDevice = HvDeviceFactory.create(device);
hvDevice.initializeViewDebug();
hvDevice.addWindowChangeListener(HierarchyViewerDirector.getDirector());
HierarchyViewerDirector.this.mDevices.put(device, hvDevice);
}
else {
hvDevice.initializeViewDebug();
}
} DeviceSelectionModel.getModel().addDevice(hvDevice);
HierarchyViewerDirector.this.focusChanged(device);
}
});
}

ViewServerDevice.class

  public boolean initializeViewDebug()
{
if (!this.mDevice.isOnline()) {
return false;
} DeviceBridge.setupDeviceForward(this.mDevice); return reloadWindows();
} public boolean reloadWindows()
{
if ((!DeviceBridge.isViewServerRunning(this.mDevice)) &&
(!DeviceBridge.startViewServer(this.mDevice))) {
Log.e("ViewServerDevice", "Unable to debug device: " + this.mDevice.getName());
DeviceBridge.removeDeviceForward(this.mDevice);
return false;
} this.mViewServerInfo = DeviceBridge.loadViewServerInfo(this.mDevice);
if (this.mViewServerInfo == null) {
return false;
} this.mWindows = DeviceBridge.loadWindows(this, this.mDevice);
return true;
}

DeviceBridge.class

  public static boolean startViewServer(IDevice device) {
return startViewServer(device, 4939);
} public static boolean startViewServer(IDevice device, int port) {
boolean[] result = new boolean[1];
try {
if (device.isOnline())
device.executeShellCommand(buildStartServerShellCommand(port), new BooleanResultReader(result));
}
catch (TimeoutException e)
{
Log.e("hierarchyviewer", "Timeout starting view server on device " + device);
} catch (IOException e) {
Log.e("hierarchyviewer", "Unable to start view server on device " + device);
} catch (AdbCommandRejectedException e) {
Log.e("hierarchyviewer", "Adb rejected command to start view server on device " + device);
} catch (ShellCommandUnresponsiveException e) {
Log.e("hierarchyviewer", "Unable to execute command to start view server on device " + device);
}
return result[0];
} private static String buildStartServerShellCommand(int port) {
return String.format("service call window %d i32 %d", new Object[] { Integer.valueOf(1), Integer.valueOf(port) });
}

从代码中可以看到,最终HierarchyView会让设备执行service命令,最终执行的命令是这样:

shell@device:/ $ service call window 1 i32 4939

这行命令其实是向android.view.IWindowManager发送一个CODE为1,值为4939的parcel。

其实就是调用WindowManagerService中的startViewServer方法,并把4939作为参数传入,接下来看看WindowManagerService.startViewServer的源码:

    public boolean startViewServer(int port) {
if (isSystemSecure()) {
return false;
} if (!checkCallingPermission(Manifest.permission.DUMP, "startViewServer")) {
return false;
} if (port < 1024) {
return false;
} if (mViewServer != null) {
if (!mViewServer.isRunning()) {
try {
return mViewServer.start();
} catch (IOException e) {
Slog.w(TAG, "View server did not start");
}
}
return false;
} try {
mViewServer = new ViewServer(this, port);
return mViewServer.start();
} catch (IOException e) {
Slog.w(TAG, "View server did not start");
}
return false;
} private boolean isSystemSecure() {
return "1".equals(SystemProperties.get(SYSTEM_SECURE, "1")) &&
"0".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
}

里面会做一些权限检查,然后会调用ViewServer.start(),关键就在ViewServer里,先吧ViewServer完整的代码贴上:

 ViewServer.java

可以看到,ViewServer实现Runnable,接下来看看start的实现:

    boolean start() throws IOException {
if (mThread != null) {
return false;
} mServer = new ServerSocket(mPort, VIEW_SERVER_MAX_CONNECTIONS, InetAddress.getLocalHost());
mThread = new Thread(this, "Remote View Server [port=" + mPort + "]");
mThreadPool = Executors.newFixedThreadPool(VIEW_SERVER_MAX_CONNECTIONS);
mThread.start(); return true;
} public void run() {
while (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) {
Slog.w(LOG_TAG, "Connection error: ", e);
}
}
}

这个Server启动后,使用之前传进来的端口号(4939)创建个ServerSocket,然后在独立的线程里监听这个端口是否有客户端连接请求,有的话传给ViewServerWorker去处理:

class ViewServerWorker implements Runnable, WindowManagerService.WindowChangeListener {
private Socket mClient;
private boolean mNeedWindowListUpdate;
private boolean mNeedFocusedWindowUpdate; 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 = mWindowManager.viewServerListWindows(mClient);
} else if (COMMAND_WINDOW_MANAGER_GET_FOCUS.equalsIgnoreCase(command)) {
result = mWindowManager.viewServerGetFocusedWindow(mClient);
} else if (COMMAND_WINDOW_MANAGER_AUTOLIST.equalsIgnoreCase(command)) {
result = windowManagerAutolistLoop();
} else {
result = mWindowManager.viewServerWindowCommand(mClient,
command, parameters);
} if (!result) {
Slog.w(LOG_TAG, "An error occurred with the command: " + command);
}
} catch(IOException e) {
Slog.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();
}
}
}
} public void windowsChanged() {
synchronized(this) {
mNeedWindowListUpdate = true;
notifyAll();
}
} public void focusChanged() {
synchronized(this) {
mNeedFocusedWindowUpdate = true;
notifyAll();
}
} private boolean windowManagerAutolistLoop() {
mWindowManager.addWindowChangeListener(this);
BufferedWriter out = null;
try {
out = new BufferedWriter(new OutputStreamWriter(mClient.getOutputStream()));
while (!Thread.interrupted()) {
boolean needWindowListUpdate = false;
boolean needFocusedWindowUpdate = false;
synchronized (this) {
while (!mNeedWindowListUpdate && !mNeedFocusedWindowUpdate) {
wait();
}
if (mNeedWindowListUpdate) {
mNeedWindowListUpdate = false;
needWindowListUpdate = true;
}
if (mNeedFocusedWindowUpdate) {
mNeedFocusedWindowUpdate = false;
needFocusedWindowUpdate = true;
}
}
if (needWindowListUpdate) {
out.write("LIST UPDATE\n");
out.flush();
}
if (needFocusedWindowUpdate) {
out.write("ACTION_FOCUS UPDATE\n");
out.flush();
}
}
} catch (Exception e) {
// Ignore
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
// Ignore
}
}
mWindowManager.removeWindowChangeListener(this);
}
return true;
}
}

从代码中可以看到,HierarchyView通过Socket向设备发送命令,ViewServerWorker来解析处理命令,并把需要返回的值通过socket再发给HierarchyView。

至此,HierarchyView的大致原理已经了解,发现只要我们自己创建个ServerSocket,并且监听4939端口,然后模仿ViewServer处理相应命令就可以让设备使用HierarchyView了。

老外就是用的这个方法。所以我们就不用重复造轮子了

接下来看看老外的解决方法:

/*
* 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.server; 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; 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; /**
* <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/>
* <p>To use this view server, your application must require the INTERNET
* permission.</p>
* <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>
* <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/>
* <p>
* In a similar fashion, you can use this API with an InputMethodService:
* </p>
* <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<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.
* <p/>
* 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 java.io.IOException If the server cannot be created.
* @see #stop()
* @see #isRunning()
*/
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()
*/
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()
*/
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(android.view.View, String)
* @see #removeWindow(android.app.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(android.app.Activity)
* @see #removeWindow(android.view.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(android.view.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(android.view.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 {
InetAddress address = InetAddress.getLocalHost();
mServer = new ServerSocket(mPort, VIEW_SERVER_MAX_CONNECTIONS, address);
} 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("\n");
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 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(); Log.i("Command", "===>" + request); 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\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;
} 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('\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;
} 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('\n');
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\n");
out.flush();
}
if (needFocusedWindowUpdate) {
out.write("FOCUS UPDATE\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;
}
} 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);
}
} /**
* 一个空的ViewServer类
*/
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() {
}
}
}

使用方法如下:

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);
}
}

使用时要注意:app要添加INTERNET权限,并且android:debugable要为true,eclipse或者studio直接run到手机都是debugable的,所以这点不用担心。

HierarchyView的实现原理和Android设备无法使用HierarchyView的解决方法的更多相关文章

  1. [原创]HierarchyView的实现原理和Android设备无法使用HierarchyView的解决方法

    最近在看一个老外写的东西,发现里面有个类,使用这个类可以让任何设备使用HierarchyView. 众所周知,市面上卖的Android设备,一般都不能使用HierarchyView,所以借此机会,了解 ...

  2. Android SDK下载失败的解决方法

    Android SDK下载失败的解决方法 图1 在下载过程中,Android SDK Manager Log中出现下面出错信息: Preparing toinstall archives Downlo ...

  3. android studio 更新 Gradle错误解决方法(Gradle sync failed)

    android studio 更新 Gradle错误解决方法   Android Studio每次更新版本都会更新Gradle这个插件,但由于长城的问题每次更新都是失败,又是停止在Refreshing ...

  4. Cannot+use+T4+templates+inside+a+.NET+Core+project,NetCore2.0无法使用T4模板解决方法

    Cannot+use+T4+templates+inside+a+.NET+Core+project,NetCore2.0无法使用T4模板解决方法 请见:https://csharp.wekeepco ...

  5. php程序无法使用localhost连接数据库解决方法(linux)

    php程序无法使用localhost连接数据库解决方法(linux) 出现这种情况是因为PHP无法正确获取mysql.sock 在php.ini文件中指定即可解决问题. 修改如下: 找到 mysql. ...

  6. Android webview 写入cookie的解决方法以及一些属性设置

    原文地址:https://www.2cto.com/kf/201703/616868.html Android webview 写入cookie的解决方法以及一些属性设置,webview怎么设置写入C ...

  7. 【转】ubuntu连接android设备(附最简单方法)

    原文网址:http://blog.csdn.net/maosidiaoxian/article/details/22661725 在ubuntu下连接android设备,虽然不用像windows那样安 ...

  8. ubuntu连接android设备(附最简单方法)

    在ubuntu下连接android设备,虽然不用像windows那样安装驱动,然而却会遇见一个错误:输入adb shell,会提示insufficient permissions for device ...

  9. 通过扫码打开IOS的App Store下载APP(Android版暂时没找到解决方法)

    项目需求:扫码根据不同平台下载不同版本的APP.主要是ios和Android. 网上找了很多,前面判断平台的代码很容易找到,但是后面的就有些坑了.有的人的是根本跑不通.有的是代码补全. 下面是 微信扫 ...

随机推荐

  1. 如何在google test中指定只运行一部分测试

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:如何在google test中指定只运行一部分测试.

  2. 我最喜欢的visual studio 2013的新特性

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:我最喜欢的visual studio 2013的新特性.

  3. HDU 4389——X mod f(x)(数位DP)

    X mod f(x) Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Probl ...

  4. 谈谈Angular关于$watch,$apply 以及 $digest的工作原理

    这篇文章主要是面向那些刚开始学AngularJs和想要了解数据绑定(data-binding)是怎么工作的, 如果你已经熟悉如何使用angularjs了,我强烈建议你不用阅读了. angularjs使 ...

  5. java 集合排序(转)

    Java API针对集合类型排序提供了两种支持:java.util.Collections.sort(java.util.List)java.util.Collections.sort(java.ut ...

  6. python学习笔记--基础语法

    等待用户输入 #!/usr/bin/python raw_input("\n\nPress the enter key to exit.") 简单的判断 #!/usr/bin/py ...

  7. Elasticsearch .Net Client NEST使用说明 2.x

    Elasticsearch .net client NEST使用说明 2.x Elasticsearch.Net与NEST是Elasticsearch为C#提供的一套客户端驱动,方便C#调用Elast ...

  8. [Eclipse]The type XXX cannot be resolved. It is indirectly referenced from required .class files

    在Eclipse中遇到The type XXX cannot be resolved. It is indirectly referenced from required .class files错误 ...

  9. Android(java)学习笔记194:ListView编写步骤(重点)

    1.ListView在我们的手机android编写程序中使用是十分广泛的,比如如下图中 短信 和 手机设置 都是ListView的效果: 手机设置:             短信:    2.正因为这 ...

  10. 简单地使用jquery的validate

    简单地使用jquery的validate ——@梁WP 摘要:本文通过一个很简单的例子,讲解了jquery validate的最基础使用方式. 一.源代码 注意事项都写在代码的注释里了,哈哈. < ...