纯粹作为个人学习记录
时序图:
源码解读:
https://github.com/openatx/android-uiautomator-server
这块框架源码的作者,代码写的真的很漂亮
主服务入口:
package com.github.uiautomator.stub;
AutomatorHttpServer 轻量级 HTTP 服务
int PORT = 9008;
AutomatorHttpServer server = new AutomatorHttpServer(PORT);
把
public class AutomatorHttpServer extends NanoHTTPD {
public AutomatorHttpServer(int port) {
super(port);
}
### 把路由与JsonRpc结合
private Map<String, JsonRpcServer> router = new HashMap<String, JsonRpcServer>();
public void route(String uri, JsonRpcServer rpc) {
router.put(uri, rpc);
}
@Override
public Response serve(String uri, Method method,
Map<String, String> headers, Map<String, String> params,
Map<String, String> files) {
Log.d(String.format("URI: %s, Method: %s, params, %s, files: %s", uri, method, params, files));
if ("/stop".equals(uri)) {
stop();
return newFixedLengthResponse("Server stopped!!!");
} else if ("/ping".equals(uri)) {
return newFixedLengthResponse("pong");
} else if ("/screenshot/0".equals(uri)) {
float scale = 1.0f;
if (params.containsKey("scale")) {
try {
scale = Float.parseFloat(params.get("scale"));
} catch (NumberFormatException e) {
}
}
int quality = 100;
if (params.containsKey("quality")) {
try {
quality = Integer.parseInt(params.get("quality"));
} catch (NumberFormatException e) {
}
}
File f = new File(InstrumentationRegistry.getTargetContext().getFilesDir(), "screenshot.png");
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).takeScreenshot(f, scale, quality);
try {
return newChunkedResponse(Response.Status.OK, "image/png", new FileInputStream(f));
} catch (FileNotFoundException e) {
Log.e(e.getMessage());
return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "Internal Server Error!!!");
}
} else if (router.containsKey(uri)) {
JsonRpcServer jsonRpcServer = router.get(uri);
ByteArrayInputStream is = null;
if (params.get("NanoHttpd.QUERY_STRING") != null)
is = new ByteArrayInputStream(params.get("NanoHttpd.QUERY_STRING").getBytes());
else if (files.get("postData") != null)
is = new ByteArrayInputStream(files.get("postData").getBytes());
else
return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "Invalid http post data!");
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
#把http请求转换成rpc请求进行处理
jsonRpcServer.handleRequest(is, os);
return newFixedLengthResponse(Response.Status.OK, "application/json", new ByteArrayInputStream(os.toByteArray()), os.size());
} catch (IOException e) {
return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "Internal Server Error!!!");
}
} else
return newFixedLengthResponse(Response.Status.NOT_FOUND, MIME_PLAINTEXT, "Not Found!!!");
}
}
初始化,把 AutomatorService 实现和接口分别填入 JsonRpcServer 中,处理由 JsonRpcServer 进行处理
@Before
public void setUp() throws Exception {
launchService();
server.route("/jsonrpc/0", new JsonRpcServer(new ObjectMapper(), new AutomatorServiceImpl(), AutomatorService.class));
server.start();
}
JsonRpc 消息处理格式
{ "method": "方法名", "params": [“参数数组”], "id": 方法ID}
看下 AutomatorServiceImpl 的实现
@Override
public boolean click(int x, int y) {
return device.click(x, y);
}
再看下 device.click 是哪里的方法,是原生 Uiautomator 的方法了
package android.support.test.uiautomator;
/**
* Perform a click at arbitrary coordinates specified by the user
*
* @param x coordinate
* @param y coordinate
* @return true if the click succeeded else false
* @since API Level 16
*/
public boolean click(int x, int y) {
Tracer.trace(x, y);
if (x >= getDisplayWidth() || y >= getDisplayHeight()) {
return (false);
}
return getAutomatorBridge().getInteractionController().clickNoSync(x, y);
}
再看下 click 的数据入参为 (int x, int y)
所以对应的 JSONRPC 报文格式应该是类似:
预期:{ "method": "click", "params": [11,22], "id": "asdabdabsdada"}
实际:{"jsonrpc": "2.0", "id": "b1e82a522ee935dcff77740bbe3e4921", "method": "click", "params": [134.5, 1003.5]}