标签:
此为转帖,原文请参见: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了。
老外就是用的这个方法
。所以我们就不用重复造轮子了
。
接下来看看老外的解决方法:
解决问题的类
使用方法如下:
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的解决方法
标签:
原文地址:http://www.cnblogs.com/wont/p/4294337.html