您的位置:首页 > 其它

ViewServer源码分析

2015-10-09 16:31 381 查看

ViewServer源码分析

正常情况下,基于安全考虑,在手机没有root时,HierarchyViewer工具无法正常工作。通过在应用中接入ViewServer类,可以实现用HierarchyViewer工具对查看和调试应用的UI。

如何接入ViewServer类

需要申明权限:

参照如下方式,在应用onCreate时注册,在onDestroy时取消注册(对于Activity和Service稍有不同)

public class MyActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//在onCreate时,进行注册
ViewServer.get(this).addWindow(this);
}

public void onDestroy() {
super.onDestroy();
//在onDestroy时,取消注册
ViewServer.get(this).removeWindow(this);
}

public void onResume() {
super.onResume();
ViewServer.get(this).setFocusedWindow(this);
}
}


对于InputMethodService,可以使用如下方式,和Activity稍有不同

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);
//这里第一个getWindwo返回的是Dialog对象,第二个getWindow方法返回的是Window对象
View decorView = getWindow().getWindow().getDecorView();
ViewServer.get(this).setFocusedWindow(decorView);
}
}


针对Activity形式window和Service形式window,ViewServer提供了重载方法addWindow(Activity)和addWindow(Activity, String)

addWindow(Activity)方法最终还是会调用到addWindow(Activity, String)方法。

源码分析

ViewServer是一个Runnable子类,并且ViewServer是singleton类型的,

public class ViewServer implements Runnable


先分析ViewServer是如何初始化的

private static ViewServer sServer;

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


ROM编译类型在prop文件的ro.build.type中获得,Build.TYPE=getString(“ro.build.type”),BUILD_TYPE_USER表明手机ROM的编译形式是“user”形式的;

info.flags & ApplicationInfo.FLAG_DEBUGGABLE表明apk是debug形式的。ApplicationInfo.FLAG_DEBUGGABLE属性只能在apk编译时设置而不能在AndroidManifest.xml的节点中设置属性:android:deubuggable=true。

ViewServer的实例创建成功后,接下来是调用sServer.start()启动当前ViewServer线程

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


由于ViewServer继承Runnable的,ViewServer类必须覆写Runnable的run方法,当执行mThread.start()时,会执行到ViewServer的run方法中:

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


run()方法中首先在指定端口上创建ServerSocket对象mServer,该ServerSocket就是用来和pc上的HierarchyViewer工具进行对象。

mServer = new ServerSocket(mPort, VIEW_SERVER_MAX_CONNECTIONS, InetAddress.getLocalHost());


ServerSocket创建成功后,开启监听,等待HierarchyViewer的socket链接,该方法是阻塞式的,

Socket client = mServer.accept();


当收到HierarchyViewer的socket链接后,阻塞结束,以该socket对象为参数通过线程池的方式启动ViewServerWorker工作线程。

由以上分析可知,ViewSever在初始化阶段工作主要是:启动自己,然后创建ServerSocket对象,并监听指定端口,等待HierarchyViewer的

socket链接,当收到socket连接时,启动工作线程ViewServerWorker。下一节继续分析ViewServerWorker。

下面分析ViewServerWorker。

mThreadPool.submit(new ViewServerWorker(client));


在ViewServer中的ServerSocket收到HierarchyViewer的socket连接后,会初始化并启动ViewServerWorker线程,

ViewServerWorker的构造方法很简单,只是进行了下变量的初始化

public ViewServerWorker(Socket client) {
mClient = client;
mNeedWindowListUpdate = false;
mNeedFocusedWindowUpdate = false;
}


每一个mClient代表ViewServer和HierarchyViewer之间的一个链接:

当ViewServerWorker被submit后,会执行ViewServerWorker的run方法:

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 {
.........
}
}


由run方法的实现可知,该方法是从从socket中读取命令,执行对象的处理,在将结果写入socket中。

PROCOTOL和SERVER命令

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


根据HierarchyViewer client端的”Command”要求,返回对应的protocol version和server version。

LIST命令

else if (COMMAND_WINDOW_MANAGER_LIST.equalsIgnoreCase(command)) {
result = listWindows(mClient);
}


当HierarchyViewer发送“LSIT”命令时,ViewServer端回对应收到该命令:

//send ‘LIST’ command
out.write("LIST");
out.newLine();
out.flush();

//receive response from viewserver
String context="";
String line;
while ((line = in.readLine()) != null) {
if ("DONE.".equalsIgnoreCase(line)) { //$NON-NLS-1$
break;
}
context+=line+"\r\n";
}


在打开HierarchyViewer时,会显示每个设备当前活动的Activity列表,如下图:



图1

44fd2c87 com.android.systemui.ImageWallpaper
4618bb17 com.android.launcher/com.android.launcher2.Launcher
453678b9 com.my/com.my.EntryOne
450acdc0 Keyguard
4572a2f0 TrackingView
45124560 StatusBarExpanded
44feeff0 StatusBar
44f08650 RecentsPanel
<br />
表一


上述显示结果即来自于ViewServer的listWindows方法。

每一列前面的8位16进制hashCode来自于

out.write(Integer.toHexString(System.identityHashCode(entry.getKey())));

每一列后面的字符串来自于:

out.append(entry.getValue());

listWindows源码:该方法重点关注for循环中的代码即可。表一显示结果就是由该for循环得到。

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


GET_FOCUS命令

else if (COMMAND_WINDOW_MANAGER_GET_FOCUS.equalsIgnoreCase(command)) {
result = getFocusedWindow(mClient);
}


分析getFocusedWindow源码:

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
focusedWindow = mFocusedWindow;
} finally {
mFocusLock.readLock().unlock();
}

if (focusedWindow != null) {
mWindowsLock.readLock().lock();
try {
//获得focustedWindow的名字
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 {
......
}

return result;
}


该方法作用是找出当前focus window然后用和“LIST”命令相同的格式将结果写会到socket中,供HierarchyViewer使用。

这里的所谓focus window也就是当前正在和用户交互的activity。即图1中的com.my/com.my.EntryOne

AUTOLIST命令

该命令用于更新winddow(listWindows方法)和focus window(getFocusedWindow方法)

else if (COMMAND_WINDOW_MANAGER_AUTOLIST.equalsIgnoreCase(command)) {
result = windowManagerAutolistLoop();
}


windowManagerAutolistLoop()源码:

windowManagerAutolistLoop的作用需要结合windowsChanged和focusChanged方法一起看:

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


}

看windowsChanged方法和focusChanged的执行桟

onCreate -> addWindow(removeWindow) -> fireWindowsChangedEvent -> windowsChanged

onResume -> setFocusedWindow-> fireFocusChangedEvent -> focusChanged


当收到COMMAND_WINDOW_MANAGER_AUTOLIST命令时,执行windowManagerAutolistLoop方法,该方法显示执行mLock.wait()挂起,当有windowsChanged或者focusChanged时,继续执行,向HierarchyViewer客户端发送”LIST UPDATE\n”或者”FOCUS UPDATE\n”命令,这样HierarchyViewer那边会王socket中写入”LIST”和”FOCUS”命令,并收到ViewServer的返回结果,并进行显示。

其他命令

其他命令默认执行windowCommand方法

windowCommand(Socket client, String command, String parameters) {
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: