Stetho 源码分析

实现原理

整体流程

自定义命令流程

自定义命令流程

stetho-server流程

stetho-server流程分析

具体执行流程

1、初始化方法

public static void initializeWithDefaults(final Context context) {
    initialize(new Initializer(context) {
        @Override
        protected Iterable<DumperPlugin> getDumperPlugins() {
            return new DefaultDumperPluginsBuilder(context).finish();
        }
        @Override
        protected Iterable<ChromeDevtoolsDomain> getInspectorModules() {
            return new DefaultInspectorModulesBuilder(context).finish();
        }
    });
}

public static void initialize(final Initializer initializer) {
    ...
    initializer.start();
}复制代码

2、在 initializer.start()之后调用了 serverManager.start(),在这里new Thred() 并开启Socket建立连接

while (!Thread.interrupted()) {
    try {
        LocalSocket socket = mServerSocket.accept();
        // Start worker thread
        Thread t = new WorkerThread(socket, mSocketHandler);
        t.setName(
                WORKER_THREAD_NAME_PREFIX +
                        "-" + mFriendlyName +
                        "-" + mThreadId.incrementAndGet());
        t.setDaemon(true);
        t.start();
    } catch (SocketException se) {
    ...
    } 
}复制代码

3、在WorkerThread中执行 mSocketHandler.onAccepted(mSocket);这里的mSocketHandler为 LazySocketHandler 类,由Stetho类传递过来,在LazySocketHandler的onAccepted方法中,真正处理逻辑的 SocketHandler 是由RealSocketHandlerFactory创建的ProtocolDetectingSocketHandler。

private class RealSocketHandlerFactory implements SocketHandlerFactory {
    @Override
    public SocketHandler create() {
        ProtocolDetectingSocketHandler socketHandler =
                new ProtocolDetectingSocketHandler(mContext);
        Iterable<DumperPlugin> dumperPlugins = getDumperPlugins();
        if (dumperPlugins != null) {
            Dumper dumper = new Dumper(dumperPlugins);
            socketHandler.addHandler(
                    new ProtocolDetectingSocketHandler.ExactMagicMatcher(
                            DumpappSocketLikeHandler.PROTOCOL_MAGIC),
                    new DumpappSocketLikeHandler(dumper));
            // Support the old HTTP-based protocol since it's relatively straight forward to do.
            DumpappHttpSocketLikeHandler legacyHandler = new DumpappHttpSocketLikeHandler(dumper);
            socketHandler.addHandler(
                    new ProtocolDetectingSocketHandler.ExactMagicMatcher(
                            "GET /dumpapp".getBytes()),
                    legacyHandler);
            socketHandler.addHandler(
                    new ProtocolDetectingSocketHandler.ExactMagicMatcher(
                            "POST /dumpapp".getBytes()),
                    legacyHandler);
        }
        Iterable<ChromeDevtoolsDomain> inspectorModules = getInspectorModules();
        if (inspectorModules != null) {
            socketHandler.addHandler(
                    new ProtocolDetectingSocketHandler.AlwaysMatchMatcher(),
                    new DevtoolsSocketHandler(mContext, inspectorModules));
        }
        return socketHandler;
    }
}复制代码

主要创建了两个处理器,用于处理Chrome Dev Tools相关的 DevtoolsSocketHandler,以及处理自定义命令的DumpappSocketLikeHandler。

4、在 ProtocolDetectingSocketHandler 的 onSecured 方法中,通过MagicMatcher区分当前请求由哪个处理器处理

for (int i = 0, N = mHandlers.size(); i < N; i++) {
    HandlerInfo handlerInfo = mHandlers.get(i);
    leakyIn.mark(SENSING_BUFFER_SIZE);
    boolean matches = handlerInfo.magicMatcher.matches(leakyIn);
    leakyIn.reset();
    if (matches) {
        SocketLike socketLike = new SocketLike(socket, leakyIn);
        handlerInfo.handler.onAccepted(socketLike);
        return;
    }
}复制代码

mHandlers 为List 类型,在RealSocketHandlerFactory执行了addHandler(),添加了Chrome Dev Tools和自定义命令的处理器

5、在分发处理器之后,处理器接收到信息执行onAccepted()方法,DevtoolsSocketHandler处理器调用LightHttpServer.serve()再次进行请求的分发处理(这里先分析 DevtoolsSocketHandler)

while ((request = readRequestMessage(scratchRequest, reader)) != null) {
    final LightHttpResponse response = scratchResponse;
    response.reset();
    boolean keepGoing = dispatchToHandler(anotherSocketLike, request, response);
    if (!keepGoing) {
        break;
    }
    writeFullResponse(response, writer, output);
}复制代码

dispatchToHandler(anotherSocketLike, request, response)

private boolean dispatchToHandler(SocketLike socketLike, LightHttpRequest request, LightHttpResponse response)
        throws IOException {
    HttpHandler handler = mHandlerRegistry.lookup(request.uri.getPath());
    if (handler == null) {
    ...
    } else {
        try {
            return handler.handleRequest(socketLike, request, response);
        } catch (RuntimeException e) {
        ...
        }
    }
}复制代码

mHandlerRegistry.lookup(request.uri.getPath()) 通过path获取不同的处理器

public synchronized HttpHandler lookup(String path) {
    for (int i = 0, N = mPathMatchers.size(); i < N; i++) {
        if (mPathMatchers.get(i).match(path)) {
            return mHttpHandlers.get(i);
        }
    }
    return null;
}复制代码

这里mPathMatchers,mHttpHandlers维护了一个path和处理器的对应关系,

  • ChromeDiscoveryHandler对应的path为
    • /json
    • /json/version
    • /json/activate/ + PAGE_ID
  • WebSocketHandler对应path为:/inspector
  • DumpappLegacyHttpHandler对应的path为:/dumpapp

6、WebSocketHandler.handleRequest() 处理请求,接着会调用WebSocketSession.handle()方法,在这里mReadHandler.readLoop(mReadCallback) 循环读取WebSocket中的数据Frame,Frame具体结构可参照 RFC6455 文档。

public void readLoop(ReadCallback readCallback) throws IOException {
    Frame frame = new Frame();
    do {
        frame.readFrom(mBufferedInput);
        mCurrentPayload.write(frame.payloadData, 0, (int) frame.payloadLen);
        if (frame.fin) {
            byte[] completePayload = mCurrentPayload.toByteArray();
            readCallback.onCompleteFrame(frame.opcode, completePayload, completePayload.length);
            mCurrentPayload.reset();
        }
    } while (frame.opcode != Frame.OPCODE_CONNECTION_CLOSE);
}复制代码

在 readCallback.onCompleteFrame(frame.opcode, completePayload, completePayload.length);方法中再次进行分发处理

public void onCompleteFrame(byte opcode, byte[] payload, int payloadLen) {
    switch (opcode) {
        case Frame.OPCODE_CONNECTION_CLOSE:
            handleClose(payload, payloadLen);
            break;
        case Frame.OPCODE_CONNECTION_PING:
            handlePing(payload, payloadLen);
            break;
        case Frame.OPCODE_CONNECTION_PONG:
            handlePong(payload, payloadLen);
            break;
        case Frame.OPCODE_TEXT_FRAME:
            handleTextFrame(payload, payloadLen);
            break;
        case Frame.OPCODE_BINARY_FRAME:
            handleBinaryFrame(payload, payloadLen);
            break;
        default:
            signalError(new IOException("Unsupported frame opcode=" + opcode));
            break;
    }
}复制代码
private void handleTextFrame(byte[] payload, int payloadLen) {
    mEndpoint.onMessage(WebSocketSession.this, new String(payload, 0, payloadLen));
}复制代码

这里 mEndpoint 为 ChromeDevtoolsServer,在ChromeDevtoolsServer中的onMessage()方法中,handleRemoteMessage(peer, message) 分为处理响应和请求:handleRemoteRequest()和handleRemoteResponse()

7、在handleRemoteRequest()中调用MethodDispatcher.dispatch(peer,request.method,request.params)进行具体的方法分发

public JSONObject dispatch(JsonRpcPeer peer, String methodName, @Nullable JSONObject params) throws JsonRpcException {
    MethodDispatchHelper dispatchHelper = findMethodDispatcher(methodName);
    ...
    try {
        return dispatchHelper.invoke(peer, params);
    } catch (InvocationTargetException e) {
    ...
    } 
}复制代码

在findMethodDispatcher(methodName)方法中首先buildDispatchTable()使用反射的方式维护了method和MethodDispatchHelper的映射关系,然后通过methodName获取MethodDispatchHelper。

private static Map<String, MethodDispatchHelper> buildDispatchTable(ObjectMapper objectMapper, Iterable<ChromeDevtoolsDomain> domainHandlers) {
    Util.throwIfNull(objectMapper);
    HashMap<String, MethodDispatchHelper> methods = new HashMap<String, MethodDispatchHelper>();
    for (ChromeDevtoolsDomain domainHandler : Util.throwIfNull(domainHandlers)) {
        Class<?> handlerClass = domainHandler.getClass();
        String domainName = handlerClass.getSimpleName();
        for (Method method : handlerClass.getDeclaredMethods()) {
            if (isDevtoolsMethod(method)) {
                MethodDispatchHelper dispatchHelper = new MethodDispatchHelper(
                        objectMapper,
                        domainHandler,
                        method);
                methods.put(domainName + "." + method.getName(), dispatchHelper);
            }
        }
    }
    return Collections.unmodifiableMap(methods);
}复制代码

ChromeDevtoolsDomain的实现类包含:

  • DOM
  • DOMStorage
  • Worker
  • CSS
  • Debugger
  • Profiler
  • HeapProfiler
  • Console
  • Page
  • Database
  • Inspector
  • Runtime
  • Network

对应Chrome的调试页面,类的具体内容可参照Chrome DevTools Protocol协议,类封装了具体的方法和请求体、返回体的参数,在这里实现了对数据库、SP等的具体操作方法,此处不做过多分析。

8、在 MethodDispatchHelper.invoke()方法中使用反射的方式执行了 ChromeDevtoolsDomain 中的方法,获取结果后在handleRemoteRequest()封装数据返回。

public JSONObject invoke(JsonRpcPeer peer, @Nullable JSONObject params)
        throws InvocationTargetException, IllegalAccessException, JSONException, JsonRpcException {
    Object internalResult = mMethod.invoke(mInstance, peer, params);
    if (internalResult == null || internalResult instanceof EmptyResult) {
        return new JSONObject();
    } else {
        JsonRpcResult convertableResult = (JsonRpcResult) internalResult;
        return mObjectMapper.convertValue(convertableResult, JSONObject.class);
    }
}复制代码

9、自定义命令的实现:

在第4步接收到请求分发处理器之后,DumpappSocketLikeHandler执行onAccepted()方法,执行dump()方法

static void dump(Dumper dumper, Framer framer, String[] args) throws IOException {
    try {
        int exitCode = dumper.dump(
                framer.getStdin(),
                framer.getStdout(),
                framer.getStderr(),
                args);
        framer.writeExitCode(exitCode);
    } catch (DumpappOutputBrokenException e) {
        ...
    }
}复制代码
public int dump(InputStream input, PrintStream out, PrintStream err, String[] args) {
    try {
        return doDump(input, out, err, args);
    } catch (ParseException e) {
        err.println(e.getMessage());
        dumpUsage(err);
        return 1;
    }复制代码
private int doDump(InputStream input, PrintStream out, PrintStream err, String[] args)
        throws ParseException, DumpException {
    CommandLine parsedArgs = mParser.parse(mGlobalOptions.options, args, true);
    if (parsedArgs.hasOption(mGlobalOptions.optionHelp.getOpt())) {
        dumpUsage(out);
        return 0;
    } else if (parsedArgs.hasOption(mGlobalOptions.optionListPlugins.getOpt())) {
        dumpAvailablePlugins(out);
        return 0;
    } else if (!parsedArgs.getArgList().isEmpty()) {
        dumpPluginOutput(input, out, err, parsedArgs);
        return 0;
    } else {
        // Didn't understand the options, spit out help but use a non-success exit code.
        dumpUsage(err);
        return 1;
    }
}复制代码

在使用 CommandLine(Apache Commons CLI库)解析参数之后,根据参数返回使用说明、帮助或者在dumpPluginOutput()执行具体的命令

private void dumpPluginOutput(InputStream input, PrintStream out, PrintStream err, CommandLine parsedArgs) throws DumpException {
    List<String> args = new ArrayList(parsedArgs.getArgList());
    if (args.size() < 1) {
        throw new DumpException("Expected plugin argument");
    }
    String pluginName = args.remove(0);
    DumperPlugin plugin = mDumperPlugins.get(pluginName);
    if (plugin == null) {
        throw new DumpException("No plugin named '" + pluginName + "'");
    }
    DumperContext dumperContext = new DumperContext(input, out, err, mParser, args);
    plugin.dump(dumperContext);
}复制代码

mDumperPlugins 在第3步中由Dumper dumper = new Dumper(dumperPlugins)传递,dumperPlugins包含了所有自定义命令 name和DumperPlugin的映射关系,在获取到DumperPlugin执行dump()方法,dump()方法在自定义DumperPlugin时具体实现。

命令行交互建立连接的实现在Stetho项目的/scripts/dumpapp中。

转载于:https://juejin.im/post/59db7d086fb9a0450670917f

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值