weexteam/weex_devtools_android

View on GitHub
inspector/src/main/java/com/taobao/weex/devtools/debug/WXDebugBridge.java

Summary

Maintainability
F
4 days
Test Coverage
package com.taobao.weex.devtools.debug;

import android.text.TextUtils;
import android.util.Log;

import com.alibaba.fastjson.JSON;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;
import com.taobao.weex.WXEnvironment;
import com.taobao.weex.bridge.WXBridge;
import com.taobao.weex.bridge.WXBridgeManager;
import com.taobao.weex.bridge.WXDebugJsBridge;
import com.taobao.weex.bridge.WXJSObject;
import com.taobao.weex.bridge.WXParams;
import com.taobao.weex.common.IWXBridge;
import com.taobao.weex.devtools.common.LogUtil;
import com.taobao.weex.devtools.websocket.SimpleSession;
import com.taobao.weex.dom.CSSShorthand;
import com.taobao.weex.layout.ContentBoxMeasurement;
import com.taobao.weex.utils.WXWsonJSONSwitch;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

/**
 * Created by budao on 16/6/25
 * .
 */
public class WXDebugBridge implements IWXBridge {

    private static final String TAG = "WXDebugBridge";
    private static volatile WXDebugBridge sInstance;
    private final Object mLock = new Object();
    private WXBridgeManager mJsManager;
    private volatile SimpleSession mSession;
    private IWXBridge mOriginBridge;
    private WXDebugJsBridge mWXDebugJsBridge;
    public static final MediaType MEDIA_TYPE_MARKDOWN
            = MediaType.parse("application/json; charset=utf-8");

    private final OkHttpClient client = new OkHttpClient();
    private String syncCallJSURL = "";

    private WXDebugBridge() {
        //TODO params
        mOriginBridge = new WXBridge();
    }

    public static WXDebugBridge getInstance() {
        if (sInstance == null) {
            synchronized (WXDebugBridge.class) {
                if (sInstance == null) {
                    sInstance = new WXDebugBridge();
                }
            }
        }

        return sInstance;
    }

    @Override
    public int initFramework(String framework, WXParams params) {
        while (mSession == null || (mSession != null && !mSession.isOpen())) {
            synchronized (mLock) {
                try {
                    Log.v(TAG, "waiting for session now");
                    mLock.wait(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        return sendMessage(getInitFrameworkMessage(framework, params));
    }

    @Override
    public int initFrameworkEnv(String framework, WXParams wxParams, String cacheDir, boolean pieSupport) {
        return initFramework(framework, wxParams);
    }

    @Override
    public void refreshInstance(String instanceId, String namespace, String function, WXJSObject[] args) {
//        mOriginBridge.refreshInstance(instanceId, namespace, function, args);
    }

    @Override
    public int execJS(String instanceId, String namespace, String function, WXJSObject[] args) {
        ArrayList<Object> array = new ArrayList<>();
        int argsCount = args == null ? 0 : args.length;
        for (int i = 0; i < argsCount; i++) {
            if (args[i] != null) {
                if (args[i].type != WXJSObject.String) {
                    array.add(WXWsonJSONSwitch.convertWXJSObjectDataToJSON(args[i]));
                } else {
                    array.add(args[i].data);
                }
            }
        }

        Map<String, Object> func = new HashMap<>();
        if (TextUtils.equals(function, "registerComponents") || TextUtils.equals(function, "registerModules") || TextUtils.equals(function, "destroyInstance")) {
          func.put(WXDebugConstants.METHOD, function);
        } else if (TextUtils.equals(function, "createInstance")) {
          func.put(WXDebugConstants.METHOD, "createInstance");
        } else {
          func.put(WXDebugConstants.METHOD, WXDebugConstants.WEEX_CALL_JAVASCRIPT);
        }
        func.put(WXDebugConstants.ARGS, array);

        Map<String, Object> map = new HashMap<>();
        map.put(WXDebugConstants.METHOD, WXDebugConstants.METHOD_CALL_JS);
        map.put(WXDebugConstants.PARAMS, func);
        return sendMessage(JSON.toJSONString(map));
    }

    @Override
    public byte[] execJSWithResult(String instanceId, String namespace, String function, WXJSObject[] args) {

        String result = "";

        ArrayList<Object> array = new ArrayList<>();
        int argsCount = args == null ? 0 : args.length;
        for (int i = 0; i < argsCount; i++) {
            if (args[i] != null) {
                if (args[i].type != WXJSObject.String) {
                    array.add(WXWsonJSONSwitch.convertWXJSObjectDataToJSON(args[i]));
                } else {
                    array.add(args[i].data);
                }
            }
        }

        Map<String, Object> func = new HashMap<>();
        if (TextUtils.equals(function, "registerComponents") || TextUtils.equals(function, "registerModules") || TextUtils.equals(function, "destroyInstance")) {
            func.put(WXDebugConstants.METHOD, function);
        } else {
            func.put(WXDebugConstants.METHOD, WXDebugConstants.WEEX_CALL_JAVASCRIPT);
        }
        func.put(WXDebugConstants.ARGS, array);

        Map<String, Object> map = new HashMap<>();
        map.put(WXDebugConstants.METHOD, WXDebugConstants.METHOD_CALL_JS);
        map.put(WXDebugConstants.PARAMS, func);

        if (TextUtils.isEmpty(syncCallJSURL))
            return new byte[0];

        Request request = new Request.Builder()
                .url(syncCallJSURL)
                .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, JSON.toJSONString(map)))
                .build();

        Response response = null;
        try {
            response = client.newCall(request).execute();
            if (response.isSuccessful()) {
                result = response.body().string();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return WXWsonJSONSwitch.convertJSONToWsonIfUseWson(result.getBytes());
    }

    @Override
    public int execJSService(String javascript) {
        if (!TextUtils.isEmpty(javascript)) {
            Map<String, Object> params = new HashMap<>();
            params.put(WXDebugConstants.PARAM_JS_SOURCE, javascript);

            Map<String, Object> map = new HashMap<>();
            map.put(WXDebugConstants.METHOD, WXDebugConstants.METHOD_IMPORT_JS);
            map.put(WXDebugConstants.PARAMS, params);
            return sendMessage(JSON.toJSONString(map));
        }
        return 0;
    }

    public void onVsync(String instanceId) {

    }

    @Override
    public void takeHeapSnapshot(String filename) {
        LogUtil.log("warning", "Ignore invoke takeSnapshot: " + filename);
    }

    @Override
    public int createInstanceContext(String s, String s1, String s2, WXJSObject[] wxjsObjects) {

        WXJSObject wxjsObjects1[] = new WXJSObject[4];
        WXJSObject wxjsObjects2[] = new WXJSObject[3];

        WXJSObject instanceId = wxjsObjects[0];
        WXJSObject code = wxjsObjects[1];
        WXJSObject bundleUrl = wxjsObjects[2];
        WXJSObject options = wxjsObjects[3];
        WXJSObject raxApi = wxjsObjects[4];

        wxjsObjects1[0] = instanceId;
        wxjsObjects1[1] = bundleUrl;
        wxjsObjects1[2] = options;
        wxjsObjects1[3] = raxApi;
        doCreateInstanceContext(s, s1, "createInstanceContext", wxjsObjects1);

        wxjsObjects2[0] = instanceId;
        wxjsObjects2[1] = code;
        wxjsObjects2[2] = bundleUrl;
        return doImportScript(s, s1, "importScript", wxjsObjects2);
    }

    private int doCreateInstanceContext(String instanceId, String namespace, String function, WXJSObject[] args) {
        ArrayList<Object> array = new ArrayList<>();
        int argsCount = args == null ? 0 : args.length;
        for (int i = 0; i < argsCount; i++) {
            if (args[i].type != WXJSObject.String) {
                array.add(WXWsonJSONSwitch.convertWXJSObjectDataToJSON(args[i]));
            } else {
                array.add(args[i].data);
            }
        }

        Map<String, Object> map = new HashMap<>();
        map.put(WXDebugConstants.METHOD, WXDebugConstants.METHOD_CALL_JS);

        Map<String, Object> func = new HashMap<>();
        func.put(WXDebugConstants.METHOD, function);
        func.put(WXDebugConstants.ARGS, array);

        map.put(WXDebugConstants.PARAMS, func);
        return sendMessage(JSON.toJSONString(map));
    }

    private int doImportScript(String instanceId, String namespace, String function, WXJSObject[] args) {
        ArrayList<Object> array = new ArrayList<>();
        int argsCount = args == null ? 0 : args.length;
        for (int i = 0; i < argsCount; i++) {
            if (args[i].type != WXJSObject.String) {
                array.add(WXWsonJSONSwitch.convertWXJSObjectDataToJSON(args[i]));
            } else {
                array.add(args[i].data);
            }
        }

        Map<String, Object> func = new HashMap<>();
        func.put(WXDebugConstants.METHOD, function);
        func.put(WXDebugConstants.ARGS, array);

        Map<String, Object> map = new HashMap<>();
        map.put(WXDebugConstants.METHOD, WXDebugConstants.METHOD_CALL_JS);
        map.put(WXDebugConstants.PARAMS, func);
        return sendMessage(JSON.toJSONString(map));
    }

    @Override
    public int destoryInstance(String s, String s1, String s2, WXJSObject[] wxjsObjects) {
        return execJS(s, s1, s2, wxjsObjects);
    }

    @Override
    public String execJSOnInstance(String s, String s1, int i) {
        return mOriginBridge.execJSOnInstance(s, s1, i);
    }

    @Override
    public int callNative(String instanceId, byte[] tasks, String callback) {
        return callNative(instanceId, new String(tasks), callback);
    }

    /**
     * js call native
     */
    @Override
    public int callNative(String instanceId, String tasks, String callback) {
        return mOriginBridge.callNative(instanceId, tasks, callback);
    }

    @Override
    public void reportJSException(String instanceId, String func, String exception) {
        mOriginBridge.reportJSException(instanceId, func, exception);
    }

    @Override
    public Object callNativeModule(String instanceId, String module, String method, byte[] arguments, byte[] options) {
        return mOriginBridge.callNativeModule(instanceId, module, method, arguments, options);
    }

    @Override
    public void callNativeComponent(String instanceId, String componentRef, String method, byte[] arguments, byte[] options) {
        mOriginBridge.callNativeComponent(instanceId, componentRef, method, arguments, options);
    }

    @Override
    public int callUpdateFinish(String instanceId, byte[] tasks, String callback) {
        return mOriginBridge.callUpdateFinish(instanceId, tasks, callback);
    }

    @Override
    public int callRefreshFinish(String instanceId, byte[] tasks, String callback) {
        return mOriginBridge.callRefreshFinish(instanceId, tasks, callback);
    }

    @Override
    public int callAddEvent(String instanceId, String ref, String event) {
        return mOriginBridge.callAddEvent(instanceId, ref, event);
    }

    @Override
    public int callRemoveEvent(String instanceId, String ref, String event) {
        return mOriginBridge.callRemoveEvent(instanceId, ref, event);
    }

    @Override
    public int callUpdateStyle(String instanceId, String ref, HashMap<String, Object> styles, HashMap<String, String> paddings, HashMap<String, String> margins, HashMap<String, String> borders) {
        return mOriginBridge.callUpdateStyle(instanceId, ref, styles, paddings, margins, borders);
    }

    @Override
    public int callUpdateAttrs(String instanceId, String ref, HashMap<String, String> attrs) {
        return mOriginBridge.callUpdateAttrs(instanceId, ref, attrs);
    }

    @Override
    public int callLayout(String s, String s1, int i, int i1, int i2, int i3, int i4, int i5, int i6) {
        return mOriginBridge.callLayout(s, s1, i, i1, i2, i3, i4, i5, i6);
    }


    @Override
    public int callCreateFinish(String instanceId) {
        return mOriginBridge.callCreateFinish(instanceId);
    }

    @Override
    public int callRenderSuccess(String s) {
        return -1;
    }

    @Override
    public int callAppendTreeCreateFinish(String instanceId, String ref) {
        return mOriginBridge.callAppendTreeCreateFinish(instanceId, ref);
    }

    @Override
    public void reportServerCrash(String instanceId, String crashFile) {
        LogUtil.e("ServerCrash: instanceId: " + instanceId + ", crashFile: " + crashFile);
    }

    @Override
    public int callCreateBody(String pageId, String componentType, String ref, HashMap<String, String> styles, HashMap<String, String> attributes, HashSet<String> events, float[] margins, float[] paddings, float[] borders) {
        return mOriginBridge.callCreateBody(pageId, componentType, ref, styles, attributes, events, margins, paddings, borders);
    }

    @Override
    public int callAddElement(String instanceId, String componentType, String ref, int index, String parentRef, HashMap<String, String> styles, HashMap<String, String> attributes, HashSet<String> events, float[] margins, float[] paddings, float[] borders, boolean willLayout) {
        return  mOriginBridge.callAddElement(instanceId, componentType, ref, index, parentRef, styles, attributes, events, margins, paddings, borders, willLayout);
    }


    @Override
    public int callRemoveElement(String instanceId, String ref) {
        return mOriginBridge.callRemoveElement(instanceId, ref);
    }

    @Override
    public int callMoveElement(String instanceId, String ref, String parentRef, int index) {
        return mOriginBridge.callMoveElement(instanceId, ref, parentRef, index);
    }


    @Override
    public int callHasTransitionPros(String instanceId, String ref, HashMap<String, String> styles) {
        return mOriginBridge.callHasTransitionPros(instanceId, ref, styles);
    }

    @Override
    public ContentBoxMeasurement getMeasurementFunc(String instanceId, long renderObjectPtr) {
        return mOriginBridge.getMeasurementFunc(instanceId, renderObjectPtr);
    }

    @Override
    public void bindMeasurementToRenderObject(long ptr) {
        mOriginBridge.bindMeasurementToRenderObject(ptr);
    }

    @Override
    public void setRenderContainerWrapContent(boolean b, String s) {
        mOriginBridge.setRenderContainerWrapContent(b, s);
    }

    @Override
    public long[] getFirstScreenRenderTime(String instanceId) {
        return mOriginBridge.getFirstScreenRenderTime(instanceId);
    }

    @Override
    public long[] getRenderFinishTime(String instanceId) {
        return mOriginBridge.getRenderFinishTime(instanceId);
    }

    @Override
    public void setDefaultHeightAndWidthIntoRootDom(String s, float v, float v1, boolean b, boolean b1) {
        mOriginBridge.setDefaultHeightAndWidthIntoRootDom(s, v, v1, b, b1);
    }

    @Override
    public void onInstanceClose(String s) {
        mOriginBridge.onInstanceClose(s);
    }

    @Override
    public void forceLayout(String s) {
        mOriginBridge.forceLayout(s);
    }

    @Override
    public boolean notifyLayout(String s) {
        return mOriginBridge.notifyLayout(s);
    }

    @Override
    public void setStyleWidth(String instanceId, String ref, float value) {
        mOriginBridge.setStyleWidth(instanceId, ref, value);
    }

    @Override
    public void setStyleHeight(String instanceId, String ref, float value) {
        mOriginBridge.setStyleHeight(instanceId, ref, value);
    }

    @Override
    public void setMargin(String instanceId, String ref, CSSShorthand.EDGE edge, float value) {
        mOriginBridge.setMargin(instanceId, ref, edge, value);
    }

    @Override
    public void setPadding(String instanceId, String ref, CSSShorthand.EDGE edge, float value) {
        mOriginBridge.setPadding(instanceId, ref, edge, value);
    }

    @Override
    public void setPosition(String instanceId, String ref, CSSShorthand.EDGE edge, float value) {
        mOriginBridge.setPosition(instanceId, ref, edge, value);
    }

    @Override
    public void markDirty(String instanceId, String ref, boolean dirty) {
        mOriginBridge.markDirty(instanceId, ref, dirty);
    }

    @Override
    public void registerCoreEnv(String key, String value) {
        mOriginBridge.registerCoreEnv(key, value);
    }

    @Override
    public void reportNativeInitStatus(String statusCode, String errorMsg) {
        mOriginBridge.reportNativeInitStatus(statusCode, errorMsg);
    }

    @Override
    public void setTimeoutNative(String callbackId, String time) {
        mOriginBridge.setTimeoutNative(callbackId, time);
    }

    @Override
    public void setJSFrmVersion(String version) {
        mOriginBridge.setJSFrmVersion(version);
    }

    @Override
    public void resetWXBridge(boolean remoteDebug) {
        final String className = this.getClass().getName().replace('.', '/');
        mWXDebugJsBridge.resetWXBridge(this, className);
    }

    @Override
    public void fireEventOnDataRenderNode(String instanceId, String ref, String type, String data) {

    }

    public void setSession(SimpleSession session) {
        mSession = session;
        if (mSession instanceof SocketClient) {
            String[] temp = ((SocketClient)mSession).getUrl().split("debugProxy/native");
            if (temp.length < 2)
                return;
            syncCallJSURL = temp[0] + "syncCallJS" + temp[1];
            syncCallJSURL = "http://" + syncCallJSURL.split("://")[1];
        }
    }

    public void setBridgeManager(WXBridgeManager bridgeManager) {
        mJsManager = bridgeManager;
    }

    public void sendToRemote(String message) {
        if (mSession != null && mSession.isOpen()) {
            mSession.sendText(message);
        }
    }

    public void post(Runnable runnable) {
        if (mSession != null && mSession.isOpen()) {
            mSession.post(runnable);
        }
    }

    public boolean isSessionActive() {
        return mSession != null && mSession.isOpen();
    }

    public void onConnected() {
        Log.v(TAG, "connect to debug server success");
        synchronized (mLock) {
            mLock.notify();
        }
    }

    public void onDisConnected() {
        Log.w(TAG, "WebSocket disconnected");
        synchronized (mLock) {
            mSession = null;
            mLock.notify();
        }
    }

    private String getInitFrameworkMessage(String framework, WXParams params) {
        Map<String, Object> func = new HashMap<>();
        func.put(WXDebugConstants.PARAM_JS_SOURCE, framework);
        func.put(WXDebugConstants.PARAM_LAYOUT_SANDBOX, "true");
        if (params != null) {
            Map<String, Object> environmentMap = getEnvironmentMap(params);
            if (environmentMap != null && environmentMap.size() > 0) {
                Map<String, Object> wxEnvironment = new HashMap<>();
                wxEnvironment.put(WXDebugConstants.ENV_WX_ENVIRONMENT, environmentMap);
                func.put(WXDebugConstants.PARAM_INIT_ENV, wxEnvironment);
            }
        }

        Map<String, Object> map = new HashMap<>();
        map.put(WXDebugConstants.METHOD, WXDebugConstants.METHOD_INIT_RUNTIME);
        map.put(WXDebugConstants.PARAMS, func);

        return JSON.toJSONString(map);
    }

    private Map<String, Object> getEnvironmentMap(WXParams params) {
        Map<String, Object> environment = new HashMap<>();
        environment.put(WXDebugConstants.ENV_APP_NAME, params.getAppName());
        environment.put(WXDebugConstants.ENV_APP_VERSION, params.getAppVersion());
        environment.put(WXDebugConstants.ENV_PLATFORM, params.getPlatform());
        environment.put(WXDebugConstants.ENV_OS_VERSION, params.getOsVersion());
        environment.put(WXDebugConstants.ENV_LOG_LEVEL, params.getLogLevel());
        environment.put(WXDebugConstants.ENV_WEEX_VERSION, params.getWeexVersion());
        environment.put(WXDebugConstants.ENV_DEVICE_MODEL, params.getDeviceModel());
        environment.put(WXDebugConstants.ENV_INFO_COLLECT, params.getShouldInfoCollect());
        environment.put(WXDebugConstants.ENV_DEVICE_WIDTH, params.getDeviceWidth());
        environment.put(WXDebugConstants.ENV_DEVICE_HEIGHT, params.getDeviceHeight());
        environment.put("runtime", "devtools");

        environment.putAll(WXEnvironment.getCustomOptions());

        return environment;
    }

    private int sendMessage(String message) {
        if (mSession != null && mSession.isOpen()) {
            mSession.sendText(message);
            return 1;
        } else {
            // session error, we need stop debug mode and switch to local runtime
            WXBridgeManager.getInstance().stopRemoteDebug();
            return 0;
        }
    }

    public void setWXDebugJsBridge(WXDebugJsBridge wxDebugJsBridge) {
        this.mWXDebugJsBridge = wxDebugJsBridge;
    }

    public WXDebugJsBridge getWXDebugJsBridge() {
        return mWXDebugJsBridge;
    }
}