package libs import ( "context" "encoding/json" "fmt" "log" "strings" ) // ---------- JSON-RPC 请求/响应结构 ---------- // JsonRpcRequest JSON-RPC 2.0 请求 type JsonRpcRequest struct { JsonRpc string `json:"jsonrpc"` ID int `json:"id"` Method string `json:"method"` Params interface{} `json:"params"` } // JsonRpcResponse JSON-RPC 2.0 响应 type JsonRpcResponse struct { JsonRpc string `json:"jsonrpc"` ID int `json:"id"` Result *json.RawMessage `json:"result,omitempty"` Error *JsonRpcError `json:"error,omitempty"` } // JsonRpcError JSON-RPC 错误对象 type JsonRpcError struct { Code int `json:"code"` Message string `json:"message"` Data string `json:"data,omitempty"` // Java 堆栈信息 } // ---------- JSON-RPC 调用函数 ---------- // JsonRpcCall 向 UIAutomator2 服务发送 JSON-RPC 调用 // dev 实现 AdbDevice 接口,devicePort 为设备端服务端口 // method 为 RPC 方法名,params 为参数 // timeout 为请求超时(秒),debug 为 true 时输出调试信息 // // 返回值为 JSON 原始字节(由调用者根据需要解析) // // 可能返回的错误类型: // - *UiObjectNotFoundError: UI 元素未找到 // - *UiAutomationNotConnectedError: UIAutomation 服务断开 // - *RPCStackOverflowError: Java 端栈溢出 // - *RPCUnknownError: 未知 RPC 错误 // - *RPCInvalidError: 无效的 RPC 响应 func JsonRpcCall(ctx context.Context, dev AdbDevice, devicePort int, method string, params interface{}, timeout float64, debug bool) (json.RawMessage, error) { // 构造 JSON-RPC 请求体 payload := map[string]interface{}{ "jsonrpc": "2.0", "id": 1, "method": method, "params": params, } // 通过 HTTP 发送到 /jsonrpc/0 端点 resp, err := HttpRequest(ctx, dev, devicePort, "POST", "/jsonrpc/0", payload, timeout, debug) if err != nil { return nil, err } // 解析响应 var rpcResp JsonRpcResponse if err := json.Unmarshal(resp.Content, &rpcResp); err != nil { return nil, &RPCInvalidError{Message: fmt.Sprintf("JSON 解析失败: %v", err)} } // 处理 RPC 错误 if rpcResp.Error != nil { return nil, handleRpcError(rpcResp.Error, resp.Text(), params) } // 确保有结果字段 if rpcResp.Result == nil { return nil, &RPCInvalidError{Message: "响应中缺少 result 字段"} } return *rpcResp.Result, nil } // handleRpcError 根据 JSON-RPC 错误内容映射到具体的 Go 错误类型 func handleRpcError(rpcErr *JsonRpcError, rawText string, params interface{}) error { code := rpcErr.Code message := rpcErr.Message data := rpcErr.Data if debug := false; debug { log.Printf("JSON-RPC 错误: code=%d message=%s", code, message) } // UIAutomation 未连接 if strings.Contains(rawText, "UiAutomation not connected") { return &UiAutomationNotConnectedError{Message: "UiAutomation not connected"} } if strings.Contains(message, "android.os.DeadObjectException") { return &UiAutomationNotConnectedError{Message: "android.os.DeadObjectException"} } if strings.Contains(message, "android.os.DeadSystemRuntimeException") { return &UiAutomationNotConnectedError{Message: "android.os.DeadSystemRuntimeException"} } // UI 元素未找到 if strings.Contains(message, "uiautomator.UiObjectNotFoundException") { return &UiObjectNotFoundError{ Code: code, Message: message, Params: params, } } // 栈溢出 if strings.Contains(message, "java.lang.StackOverflowError") { truncated := data if len(data) > 2000 { truncated = data[:1000] + "..." + data[len(data)-1000:] } return &RPCStackOverflowError{ RPCError: RPCError{ Code: code, Message: fmt.Sprintf("StackOverflowError: %s", message), Data: truncated, Params: params, }, } } // 未知 RPC 错误 return &RPCUnknownError{ RPCError: RPCError{ Code: code, Message: fmt.Sprintf("未知 RPC 错误: %d %s", code, message), Data: data, Params: params, }, } } // ---------- JSON-RPC 动态调用包装器 ---------- // JsonRpcWrapper 提供动态方法名的 JSON-RPC 调用 // 通过记录方法名并在 Call 时发送请求,实现类似 Python 的动态属性访问 type JsonRpcWrapper struct { // caller 为实际发送 JSON-RPC 请求的函数 caller func(method string, params interface{}, timeout float64) (json.RawMessage, error) } // NewJsonRpcWrapper 创建 JSON-RPC 动态调用包装器 func NewJsonRpcWrapper(caller func(method string, params interface{}, timeout float64) (json.RawMessage, error)) *JsonRpcWrapper { return &JsonRpcWrapper{caller: caller} } // Call 发送 JSON-RPC 调用 // method 为 RPC 方法名(如 "click"、"setText") // params 为方法参数,通常为 []interface{} 或 map[string]interface{} // timeout 为超时时间(秒),0 使用默认值 func (w *JsonRpcWrapper) Call(method string, params interface{}, timeout ...float64) (json.RawMessage, error) { t := HTTPTimeout if len(timeout) > 0 && timeout[0] > 0 { t = timeout[0] } return w.caller(method, params, t) } // CallResult 发送 JSON-RPC 调用并将结果解析到指定结构体 func (w *JsonRpcWrapper) CallResult(result interface{}, method string, params interface{}, timeout ...float64) error { raw, err := w.Call(method, params, timeout...) if err != nil { return err } return json.Unmarshal(raw, result) }