您好,登錄后才能下訂單哦!
上一篇文章《Monkey源碼分析之運行流程》給出了monkey運行的整個流程,讓我們有一個概貌,那么往后的文章我們會嘗試進一步的闡述相關的一些知識點。
這里先把整個monkey類的結構圖給出來供大家參考,該圖源自網上(我自己的backbook pro上沒有安裝OmniGraffle工具,55美金,不舍得,所以直接貼網上的)
圖中有幾點需要注意下的:
本文我們重點是以處理來來自網絡sokcet也就是monkeyrunner的命令為例子來闡述事件源是怎么處理的,其他的源大同小異。
在開始之前我們需要先去了解幾個基礎類,這樣子我們才方便分析。
我們在獲取了事件源之后,會把這些事件排隊放入一個隊列,然后其他地方就可以去把隊列里面的事件取出來進一步進行處理了。那么這里我們先看下維護這個事件隊列的相應代碼:
public static interface CommandQueue { /** * Enqueue an event to be returned later. This allows a * command to return multiple events. Commands using the * command queue still have to return a valid event from their * translateCommand method. The returned command will be * executed before anything put into the queue. * * @param e the event to be enqueued. */ public void enqueueEvent(MonkeyEvent e); }; // Queue of Events to be processed. This allows commands to push // multiple events into the queue to be processed. private static class CommandQueueImpl implements CommandQueue{ private final Queue<MonkeyEvent> queuedEvents = new LinkedList<MonkeyEvent>(); public void enqueueEvent(MonkeyEvent e) { queuedEvents.offer(e); } /** * Get the next queued event to excecute. * * @return the next event, or null if there aren't any more. */ public MonkeyEvent getNextQueuedEvent() { return queuedEvents.poll(); } };
接口CommandQueue只定義個了一個方法enqueueEvent,由實現類CommandQueueImpl來實現,而實現類維護了一個MonkeyEvent類型的由LinkedList實現的隊列quequeEvents,然后實現了兩個方法來分別往這個隊列里面放和取事件。挺簡單的實現,這里主要是要提醒大家queueEvents這個隊列的重要性。這里要注意的是MonkeyEventScript和monkeyEventRandom這兩個事件源維護隊列的類稍微有些不一樣,用的是MonkeyEventQueue這個類,但是其實這個類也是繼承自上面描述的LinkedList的,所以原理是一樣的。
最后創建和維護一個CommandQueueImple這個實現類的一個實例commandQueque來轉被對里面的quequeEvents進行管理。
private final CommandQueueImpl commandQueue = new CommandQueueImpl();
下一個我們需要了解的基礎內部類就是MonkeCommand。從數據源過來的命令都是一串字符串,我們需要把它轉換成對應的monkey事件并存入到我們上面提到的由CommandQueque維護的事件隊列quequeEvents里面。首先我們看下MonkeyCommand這個接口:
/** * Interface that MonkeyCommands must implement. */ public interface MonkeyCommand { /** * Translate the command line into a sequence of MonkeyEvents. * * @param command the command line. * @param queue the command queue. * @return MonkeyCommandReturn indicating what happened. */ MonkeyCommandReturn translateCommand(List<String> command, CommandQueue queue); }
它只定義了一個實現類需要實現的方法translateCommand,從它的描述和接受的的參數可以知道,這個方法要做的事情就是把從事件源接受到的字符串命令轉換成上面說的CommandQueue類型維護的那個eventQueues。以monkeyrunner發過來的press這個命令為例子,傳過來給monkey的字串是"press KEY_COKDE"(請查看《MonkeyRunner源碼分析之與Android設備通訊方式》)
針對每一個命令都會有一個對應的MonkeyCommand的實現類來做真正的字串到事件的翻譯工作,以剛才提到的press這個命令為例子,我們看下它的實現代碼:
/** * Command to "press" a buttons (Sends an up and down key event.) */ private static class PressCommand implements MonkeyCommand { // press keycode public MonkeyCommandReturn translateCommand(List<String> command, CommandQueue queue) { if (command.size() == 2) { int keyCode = getKeyCode(command.get(1)); if (keyCode < 0) { // Ok, you gave us something bad. Log.e(TAG, "Can't find keyname: " + command.get(1)); return EARG; } queue.enqueueEvent(new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, keyCode)); queue.enqueueEvent(new MonkeyKeyEvent(KeyEvent.ACTION_UP, keyCode)); return OK; } return EARG; } }以monkeyrunner過來的'press KEY_CODE'為例分析這段代碼:
private static final Map<String, MonkeyCommand> COMMAND_MAP = new HashMap<String, MonkeyCommand>(); static { // Add in all the commands we support COMMAND_MAP.put("flip", new FlipCommand()); COMMAND_MAP.put("touch", new TouchCommand()); COMMAND_MAP.put("trackball", new TrackballCommand()); COMMAND_MAP.put("key", new KeyCommand()); COMMAND_MAP.put("sleep", new SleepCommand()); COMMAND_MAP.put("wake", new WakeCommand()); COMMAND_MAP.put("tap", new TapCommand()); COMMAND_MAP.put("press", new PressCommand()); COMMAND_MAP.put("type", new TypeCommand()); COMMAND_MAP.put("listvar", new MonkeySourceNetworkVars.ListVarCommand()); COMMAND_MAP.put("getvar", new MonkeySourceNetworkVars.GetVarCommand()); COMMAND_MAP.put("listviews", new MonkeySourceNetworkViews.ListViewsCommand()); COMMAND_MAP.put("queryview", new MonkeySourceNetworkViews.QueryViewCommand()); COMMAND_MAP.put("getrootview", new MonkeySourceNetworkViews.GetRootViewCommand()); COMMAND_MAP.put("getviewswithtext", new MonkeySourceNetworkViews.GetViewsWithTextCommand()); COMMAND_MAP.put("deferreturn", new DeferReturnCommand()); }
終于到了如何獲取事件的分析了,我們繼續以MonkeySourceNetwork這個處理monkeyrunner過來的網絡命令為例子,看下它是如何處理monkeyrunner過來的命令的。我們先看下它實現的接口類MonkeyEventSource
/** * event source interface */ public interface MonkeyEventSource { /** * @return the next monkey event from the source */ public MonkeyEvent getNextEvent(); /** * set verbose to allow different level of log * * @param verbose output mode? 1= verbose, 2=very verbose */ public void setVerbose(int verbose); /** * check whether precondition is satisfied * * @return false if something fails, e.g. factor failure in random source or * file can not open from script source etc */ public boolean validate(); }
public MonkeySourceNetwork(int port) throws IOException { // Only bind this to local host. This means that you can only // talk to the monkey locally, or though adb port forwarding. serverSocket = new ServerSocket(port, 0, // default backlog InetAddress.getLocalHost()); }所做的事情就是通過指定的端口實例化一個ServerSocket,這里要注意它綁定的只是本地主機地址,意思是說只有本地的socket連接或者通過端口轉發連過來的adb端口(也就是我們這篇文章關注的monkeyrunner啟動的那個adb)才會被接受。
這里只是實例化了一個socket,現在為止還沒有真正啟動起來的,也就是說還沒有開始真正的啟動對指定端口的監聽的。真正開始監聽是startServer這個方法觸發的:
/** * Start a network server listening on the specified port. The * network protocol is a line oriented protocol, where each line * is a different command that can be run. * * @param port the port to listen on */ private void startServer() throws IOException { clientSocket = serverSocket.accept(); // At this point, we have a client connected. // Attach the accessibility listeners so that we can start receiving // view events. Do this before wake so we can catch the wake event // if possible. MonkeySourceNetworkViews.setup(); // Wake the device up in preparation for doing some commands. wake(); input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); // auto-flush output = new PrintWriter(clientSocket.getOutputStream(), true); }這里除了開始監聽端口之外,還如monkeyrunner對端口讀寫的情況一樣,維護和實例化了input和output這兩個成員變量來專門對端口數據進行操作。
public MonkeyEvent getNextEvent() { if (!started) { try { startServer(); } catch (IOException e) { Log.e(TAG, "Got IOException from server", e); return null; } started = true; } // Now, get the next command. This call may block, but that's OK try { while (true) { // Check to see if we have any events queued up. If // we do, use those until we have no more. Then get // more input from the user. MonkeyEvent queuedEvent = commandQueue.getNextQueuedEvent(); if (queuedEvent != null) { // dispatch the event return queuedEvent; } // Check to see if we have any returns that have been deferred. If so, now that // we've run the queued commands, wait for the given event to happen (or the timeout // to be reached), and handle the deferred MonkeyCommandReturn. if (deferredReturn != null) { Log.d(TAG, "Waiting for event"); MonkeyCommandReturn ret = deferredReturn.waitForEvent(); deferredReturn = null; handleReturn(ret); } String command = input.readLine(); if (command == null) { Log.d(TAG, "Connection dropped."); // Treat this exactly the same as if the user had // ended the session cleanly with a done commant. command = DONE; } if (DONE.equals(command)) { // stop the server so it can accept new connections try { stopServer(); } catch (IOException e) { Log.e(TAG, "Got IOException shutting down!", e); return null; } // return a noop event so we keep executing the main // loop return new MonkeyNoopEvent(); } // Do quit checking here if (QUIT.equals(command)) { // then we're done Log.d(TAG, "Quit requested"); // let the host know the command ran OK returnOk(); return null; } // Do comment checking here. Comments aren't a // command, so we don't echo anything back to the // user. if (command.startsWith("#")) { // keep going continue; } // Translate the command line. This will handle returning error/ok to the user translateCommand(command); } } catch (IOException e) { Log.e(TAG, "Exception: ", e); return null; } }有了以上介紹的那些背景知識,這段代碼的理解就不會太費力了,我這里大概描述下:
/** * Translate the given command line into a MonkeyEvent. * * @param commandLine the full command line given. */ private void translateCommand(String commandLine) { Log.d(TAG, "translateCommand: " + commandLine); List<String> parts = commandLineSplit(commandLine); if (parts.size() > 0) { MonkeyCommand command = COMMAND_MAP.get(parts.get(0)); if (command != null) { MonkeyCommandReturn ret = command.translateCommand(parts, commandQueue); handleReturn(ret); } } }很簡單,就是獲取monkeyunner進來的命令字串列表的的第一個值,然后通過上面的COMMAND_MAP把字串轉換成對應的MonkeyCommand實現類,然后調用其tranlsateCommand把該字串命令翻譯成對應的MonkeyEvent并存儲到事件隊列。
COMMAND_MAP.put("press", new PressCommand());所以調用的就是PressCommand這個MonkeyCommand接口實現類的translateCommand方法來把press這個命令轉換成對應的MonkeyKeyEvent了。
作者 | 自主博客 | 微信 | CSDN |
天地會珠海分舵 | http://techgogogo.com | 服務號:TechGoGoGo 掃描碼:
| 向AI問一下細節 免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。 猜你喜歡最新資訊相關推薦相關標簽AI
助 手
龙泉市|
昌宁县|
商水县|
芷江|
马公市|
甘谷县|
定陶县|
湾仔区|
汤阴县|
甘肃省|
洛川县|
珲春市|
阳原县|
盐城市|
巴东县|
雷州市|
荥经县|
绥德县|
富川|
星座|
西城区|
河西区|
香河县|
长子县|
黔南|
贞丰县|
中山市|
潍坊市|
扬中市|
宁武县|
南木林县|
彝良县|
津市市|
桐梓县|
黄石市|
松原市|
南溪县|
德庆县|
都兰县|
余干县|
岚皋县|
|