Hummer 跨端框架源碼解析

首先整體介紹一下Hummer框架逛揩,官網(wǎng)地址 https://hummer.didi.cn/home#/ 零抬;
Hummer 是一套高性能高可用的跨端開發(fā)框架,一套代碼可以同時支持開發(fā) Android 和 iOS 應(yīng)用∨练現(xiàn)已經(jīng)支持 Vue/TypeScript/JavaScript 三種語法可训,面向大前端開發(fā)人員淤堵,總有一款適合你。
總之一句話柬批,Hummer框架就是為了提高開發(fā)效率啸澡。

系列文章
《碼一個簡易的跨端框架》
這篇文章介紹如何將Quickjs在androidstudio中編譯,以及實現(xiàn)一個簡易的js->js引擎->c->java的回調(diào)過程氮帐,適用于想要了解跨端框架的人群嗅虏。

本篇文章要達(dá)到的目的

  1. Hummer框架的項目結(jié)構(gòu)簡介
  2. Hummer框架是如何將js代碼通過js引擎轉(zhuǎn)換成原生代碼的
Hummer項目結(jié)構(gòu)以及依賴關(guān)系
+--- project :hummer
|    +--- project :hummer-component
|    |    \--- project :hummer-sdk
|    |         +--- com.didi.hummer:hummer-annotation:0.2.2
|    |         +--- com.didi.hummer:hummer-plugin-interface:0.0.1
|    |         +--- project :hummer-core
|    \--- project :hummer-dev-tools
project:hummer

主要功能:

  1. 初始化SDK:
public static void init(Context context, HummerConfig config)
  1. 提供頁面:
public class HummerActivity
public class HummerFragment
project :hummer-component

主要功能:

  1. 定義Hummer組件;例如在js代碼中寫var button=new Button();對應(yīng)到原生組件就是
@Component("Button")
public class Button extends HMBase<android.widget.Button> 
@Component("Image")
public class Image extends HMBase<RoundedImageView>
@Component("View")
public class View extends HummerLayoutExtendView

具體這個組件是如何定義的上沐,以及為什么要繼承HMBase等我們以后再做分析皮服,這里主要是知道js中寫的組件對應(yīng)的都是原生組件在這個工程下就可以了。

project :hummer-sdk

主要功能:

  1. 適配器(Adapter):將網(wǎng)絡(luò)請求okhttp参咙、圖片加載glide冰更、本地存貯SharedPreferences等一些功能進(jìn)行隔離,對上層框架提供接口封裝底層實現(xiàn)方便以后替換實現(xiàn)昂勒。
  2. 上下文(Context):為Hummer的上下文環(huán)境蜀细,返回全局的屬性HummerLayout、JSContext等戈盈;處理生命周期onStart()奠衔、onResume、onPause塘娶、onStop归斤、onDestory等;給JS引擎注入JS模板mJsContext.evaluateJavaScript(AssetsUtil.readFile(HUMMER_DEFINITION_FILE), "HummerDefinition.js");刁岸;設(shè)置JS引擎回調(diào)到Java層的invoke類脏里,進(jìn)行具體的操作registerInvoker(new HummerInvoker()); registerInvoker(new NotifyCenterInvoker());
  3. 模塊層:將適配器(Adapter)的功能轉(zhuǎn)換成JS代碼可以用的Module
@Component("Memory")
public class Memory //存儲
@Component("Request")
public class Request implements ILifeCycle //網(wǎng)絡(luò)請求
@Component("Location")
public class HMLocation implements ILifeCycle //定位
  1. 頁面渲染(render):
  2. HummerLayout是對YogaLayout的封裝,修改了一些bug虹曙,以及支持對View的添加和刪除等Hummer中自定義的功能迫横;
  3. BackgroundDrawable這個主要是處理View背景的渲染,設(shè)置邊框的Hummer中的特殊操作酝碳;
  4. HummerStyleUtils主要是用來解析js代碼中的Style屬性矾踱,把他對應(yīng)到y(tǒng)ogalayout或者Hummer自定義的原生屬性上
project :hummer-core

主要功能:
這層主要是和JS引擎交互層,

  1. 調(diào)用native本地方法注冊JS引擎的BridgeHummerBridge.initHummerBridge(long jsContext)
  2. JS引擎解析JS代碼后通過JNI接口回調(diào)Java層的方法HummerBridge.invoke(String className, long objectID, String methodName, long... params)
  3. 通過native本地方法執(zhí)行疏哗、注入JS代碼的方法JavaScriptRuntime.evaluateJavaScriptNative(long jsContext, String script, String scriptId)
  4. JS引擎代碼層呛讲,我們看jni目錄下,有hermes、jsc(JavaScriptCore)贝搁、qjs(Quickjs)吗氏,還有編譯他們的CMakeList.txt文件,我們目前只看qjs目錄雷逆;./quickjs目錄為github上下載的源碼弦讽;./hummer目錄為對quickjs代碼封裝的JNI接口,提供給java層使用关面;HummerBridge.cpp坦袍、HummerRecycler.cpp等目前不做過多代碼解析
Hummer頁面渲染(一)
Hummer頁面渲染時序圖(一).jpg
  1. Hummer是如何注冊quickjs_bridge:
    時序圖步驟4十厢、5等太、6、7蛮放、8缩抡、9、10包颁、11瞻想、12、13代碼非常簡單都是順序調(diào)用娩嚼;
    重點介紹14蘑险、15、16岳悟、17
HummerBridge.cpp
/**
 * 14步
 * 對QuickJs引擎設(shè)置屬性佃迄,也就是設(shè)置回調(diào)方法,將invoke這個C方法通過JS_SetPropertyStr設(shè)置到Quickjs引擎中
 * 這樣在js代碼中就可以調(diào)用invoke這個全局方法了
 */
extern "C"
JNIEXPORT void JNICALL
Java_com_didi_hummer_core_engine_jsc_jni_HummerBridge_initHummerBridge(JNIEnv *env, jobject thiz, jlong js_context) {
    auto jsContext = QJS_CONTEXT(js_context);

    //創(chuàng)建HummerBridge類的jni對象
    jobject bridge = env->NewGlobalRef(thiz);
    HUMMER_BRIDGE_MAP[js_context] = bridge;

    //找到HummerBridge.java
    //private long invoke(String className, long objectID, String methodName, long... params)
    HUMMER_BRIDGE_INVOKE_ID = env->GetMethodID(
            env->GetObjectClass(thiz),
            "invoke",
            "(Ljava/lang/String;JLjava/lang/String;[J)J");

    //將C方法invoke創(chuàng)建成JS引擎中的方法
    auto funcName = "invoke";
    auto invokeFunc = JS_NewCFunction(jsContext, invoke, funcName, strlen(funcName));

    //將這個創(chuàng)建之后的js引擎中的invokeFunc方法注入到j(luò)s引擎中贵少,成為全局方法呵俏,這樣在寫js代碼時就可以直接使用invoke這個方法
    JS_SetPropertyStr(jsContext,
            JS_GetGlobalObject(jsContext),
            "invoke",
            invokeFunc);
}
/**
 *15步
 * js引擎通過JS_SetPropertyStr將這個方法注入到j(luò)s引擎中,寫js代碼時可以直接調(diào)用這個方法滔灶,
 * 這個方法的聲明是參照J(rèn)SCFunction方法聲明的可以查看普碎,在這個方法中通過jni調(diào)用java中的代碼,
 * 實現(xiàn)js引擎和java代碼的通信
 */
static JSValue invoke(JSContext* ctx, JSValueConst thisObject, int argumentCount, JSValueConst* arguments) {
    long ctxId = QJS_CONTEXT_ID(ctx);
    jobject bridge = HUMMER_BRIDGE_MAP[ctxId];
    if (bridge == nullptr) return JS_NULL;

    JNIEnv* env = JNI_GetEnv();
    //取出 long invoke(String className,long objectID,String methodName,long ...param);的最后一個參數(shù)long數(shù)組
    jlongArray params = nullptr;
    if (argumentCount > 3) {
        int methodParamsCount = argumentCount - 3;
        params = env->NewLongArray(methodParamsCount);
        jlong paramsC[methodParamsCount];
        for (int i = 3; i < argumentCount; i++) {
            paramsC[i - 3] = QJS_VALUE_PTR(arguments[i]);
        }
        env->SetLongArrayRegion(params, 0, methodParamsCount, paramsC);
    }
    //取出objectID
    int64_t objId;
    JS_ToInt64(ctx, &objId, arguments[INDEX_OBJECT_ID]);
    //取出className
    jstring className = TypeConvertor::JSString2JavaString(ctx, arguments[INDEX_CLASS_NAME]);
    //取出methodName
    jstring methodName = TypeConvertor::JSString2JavaString(ctx, arguments[INDEX_METHOD_NAME]);

    //調(diào)用HummerBridge.java
    // private long invoke(String className, long objectID, String methodName, long... params)
    jlong ret = env->CallLongMethod(
            bridge, HUMMER_BRIDGE_INVOKE_ID,
            className, objId, methodName,
            params);

    env->DeleteLocalRef(className);
    env->DeleteLocalRef(methodName);
    env->DeleteLocalRef(params);

    JNI_DetachEnv();

    return QJS_VALUE(ret);
}
HummerBridge.java
     /**
     * 16步
     * JNI回調(diào)的java代碼
     */
    private long invoke(String className, long objectID, String methodName, long... params) {
        if (mCallback == null) {
            return JSCUtils.POINTER_NULL;
        }
        long result = JSCUtils.POINTER_NULL;
        Object[] parameters = null;
        try {
            // 把long型的JS對象指針參數(shù)轉(zhuǎn)換為Object或JSValue對象參數(shù)
            parameters = JSCUtils.jsValuesToObjects(jsContext, params);
            // 執(zhí)行具體的invoke方法录平,并得到Object類型的返回值
            Object ret = mCallback.onInvoke(className, objectID, methodName, parameters);
            // 把Object類型的返回值轉(zhuǎn)換成long型的JS對象指針
            result = JSCUtils.objectToJsValue(jsContext, ret);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
JSCHummerContext.java
  /**
     *  17步
     * 從HummerBridge.java通過callback回調(diào)到這個方法中
     * 在mRegistry.get(className)中找到注冊過的Invoker組件
     * 具體注冊:例如
     * registerInvoker(new HummerInvoker());
     * registerInvoker(new NotifyCenterInvoker());
     * registerInvoker(new Button$$Invoker());
     * registerInvoker(new View$$Invoker());
     */
    @Override
    public Object onInvoke(String className, long objectID, String methodName, Object... params) {
        Invoker invoker = mRegistry.get(className);
        if (invoker == null) {
            HMLog.w("HummerNative", String.format("Invoker error: can't find this class [%s]", className));
            return null;
        }
        return invoker.onInvoke(this, objectID, methodName, params);
    }
  1. Hummer是如何注冊js_base模板代碼麻车、js_component組件模板代碼
    18步;注冊js_base模板代碼
HummerContext.java
18步
protected void onCreate() {
        ......
        //注冊組件
        registerInvoker(new HummerInvoker());
        registerInvoker(new NotifyCenterInvoker());
        //注冊js_base模板代碼
        mJsContext.evaluateJavaScript(AssetsUtil.readFile(HUMMER_DEFINITION_FILE), "HummerDefinition.js");
        ......
    }
/**
*HummerDefinition.js文件截選
 *預(yù)先將這段js代碼注入引擎斗这,js組件都繼承Base類
 *構(gòu)造方法绪氛,以及每個定義的方法都調(diào)用了之前實現(xiàn)在js引擎中
 *創(chuàng)建好的invoke方法,通過這個方法回調(diào)到j(luò)ava側(cè)執(zhí)行相關(guān)操作
 */
class Base {
    constructor(className, ...args) {
        this.className = className;
        this.objID = idGenerator();
        this.recycler = new Recycler(this.objID);

        let params = transArgs(...args);
        invoke(this.className, this.objID, "constructor",
                                        this, ...params);

        this.initialize(...args);
    }
    initialize(...args) {}
    set style(arg) {
        this._style = arg;
        arg = transSingleArg(arg);
        invoke(this.className, this.objID, "setStyle", arg);
    }
    get style() {
        return this._style;
    }
}

19步注冊js_component組件模板代碼

HummerRegister$$hummer_component.java  
  /**
   * 注冊java層組件涝影,注入js_component模板代碼
   * js_component繼承js_base的Base類枣察,以及通過
   * 預(yù)先注冊好的invoke方法和java側(cè)建立聯(lián)系,
   * 回調(diào)到registerInvoker注冊的Java側(cè)組件進(jìn)行相關(guān)操作
   */
  public static void init(HummerContext hummerContext) {
    hummerContext.registerInvoker(new Image$$Invoker());
    hummerContext.registerInvoker(new Loading$$Invoker());
    hummerContext.registerInvoker(new TextArea$$Invoker());
    hummerContext.registerInvoker(new Input$$Invoker());
    hummerContext.registerInvoker(new Switch$$Invoker());
    hummerContext.registerInvoker(new ViewPager$$Invoker());
    hummerContext.registerInvoker(new Toast$$Invoker());
    hummerContext.registerInvoker(new Dialog$$Invoker());
    hummerContext.registerInvoker(new Button$$Invoker());
    hummerContext.registerInvoker(new List$$Invoker());
    hummerContext.registerInvoker(new View$$Invoker());
    hummerContext.registerInvoker(new Anchor$$Invoker());
    hummerContext.registerInvoker(new Text$$Invoker());
    hummerContext.registerInvoker(new Scroller$$Invoker());
    hummerContext.registerInvoker(new HorizontalScroller$$Invoker());
    hummerContext.evaluateJavaScript(JS_CODE, "hummer_component.js");
  }

hummer_component.js代碼片段
/**
 *繼承js_base的Base類,擁有了Base類的基礎(chǔ)能力
 *定義自己的特有能力appendChild方法內(nèi)調(diào)用
 *js注入的invoke方法序目,通知到j(luò)ava側(cè)View組件執(zhí)行相關(guān)操作
 */
class View extends Base {
      constructor(...args) {
          super('View', ...args);
      }
      appendChild(...args) {
          let stash = args;
          args = transArgs(...args);
          invoke('View', this.objID, 'appendChild', ...args);
      }
      removeChild(...args) {
          let stash = args;
          args = transArgs(...args);
          invoke('View', this.objID, 'removeChild', ...args);
      }
}

class Button extends Base {
    constructor(...args) {
        super('Button', ...args);
    }
    set text(arg) {
        this._text = arg;
        arg = transSingleArg(arg);
        invoke('Button', this.objID, 'setText', arg);
    }
    get text() {
        return this._text;
    }
}
  1. Hummer是如何注冊原生組件
    20步注冊原生組件
HummerRegister$$hummer_component.java
//在這個aop生成的java文件中注冊原生組件臂痕,在這里注冊之后
// 17步的Invoker invoker = mRegistry.get(className);才能獲取到,
//才能執(zhí)行到j(luò)ava組件中的invoke方法執(zhí)行相關(guān)的操作
hummerContext.registerInvoker(new View$$Invoker());
/**
* 17步根據(jù)className獲取到對應(yīng)的組件猿涨,執(zhí)行android組件的invoke方法
 * aop根據(jù)@Component("View")生成的代碼
 * 具體執(zhí)行回調(diào)到invoke方法通過methodName進(jìn)行判斷
 */
public class View$$Invoker extends BaseInvoker<View> {
  @Override
  public String getName() {
    return "View";
  }
  @Override
  protected View createInstance(JSValue jsValue, Object[] params) {
    String param0 = params.length > 0 && params[0] != null ? String.valueOf(params[0]) : null;
    return new View(mHummerContext, jsValue, param0);
  }
  @Override
  protected Object invoke(View instance, String methodName, Object[] params) {
    Object jsRet = null;
    switch (methodName) {
      case "appendChild": {
        long childId0 = params.length > 0 && params[0] != null ? ((Number) params[0]).longValue() : 0;
        HMBase param0 = mInstanceManager.get(childId0);
        instance.appendChild(param0);
      } break;
      case "removeChild": {
        long childId0 = params.length > 0 && params[0] != null ? ((Number) params[0]).longValue() : 0;
        HMBase param0 = mInstanceManager.get(childId0);
        instance.removeChild(param0);
      } break;
    }
    return jsRet;
  }
}
Hummer頁面渲染(二)
Hummer頁面渲染時序圖(二).jpg

通過上面介紹我們明白了

  1. js引擎和java側(cè)的交互通道
  2. js代碼中的組件是通過預(yù)先注入js_base握童、js_component代碼實現(xiàn)的
  3. java側(cè)組件的注入,實現(xiàn)js側(cè)和java側(cè)一對一的組件
  4. 我們寫的js組件通過1叛赚、2應(yīng)對到3中的java側(cè)組件執(zhí)行相關(guān)操作澡绩,

下面介紹回調(diào)到j(luò)ava側(cè)組件之后我們 如何操作
第一步創(chuàng)建對象

//js側(cè)創(chuàng)建一個對象回調(diào)到BaseInvoker.java文件中,調(diào)用如下方法創(chuàng)建一個對象
//根據(jù)objectID保存到mInstanceManager中
private T getInstance(long objectID, String methodName, Object... params) {
        // objectID <= 0說明是static方法
        if (objectID <= 0) {
            return null;
        }
        T instance = mInstanceManager.get(objectID);
        if (instance == null && "constructor".equals(methodName)) {
            JSValue jsValue = params.length > 0 ? ((JSValue) params[0]) : null;
            if (jsValue != null) {
                if (params.length > 1) {
                    Object[] parameters = Arrays.copyOfRange(params, 1, params.length);
                    instance = createInstance(jsValue, parameters);
                } else {
                    instance = createInstance(jsValue);
                }
                if (instance instanceof ILifeCycle) {
                    ((ILifeCycle) instance).onCreate();
                }
                mInstanceManager.put(objectID, instance);
            }
        }
        return instance;
    }

第二步設(shè)置屬性

//創(chuàng)建完成對象我們js側(cè)調(diào)用 this.style = {}設(shè)置樣式
//回調(diào)到BaseInvoker.java文件中
//invokeHMBase這個方法根據(jù)methodName來匹配js側(cè)調(diào)用了什么方法俺附,去執(zhí)行相應(yīng)的操作
    private Object invokeHMBase(T instance, String methodName, Object... params) {
        HMBase base = (HMBase) instance;
        Object ret = null;
        switch (methodName) {
            case "setStyle":
                //解析js側(cè)傳過來的style樣式
                Map<String, Object> styleMap = HMJsonUtil.toMap(String.valueOf(params[0])); // json耗時1ms
                base.setStyle(getSortedMap(styleMap)); // sort耗時1ms
                break;
                ......
        }
        return ret;
    }
//因為每個對象的基類都是HMBase.java肥卡,所以調(diào)用這個類的
//setStyle方法,這個方法會調(diào)用HummerNode.setStyle方法
//在這個方法中調(diào)用HummerStyleUtils.applyStyle(linkView, style);
//來具體解析Style屬性事镣,YogaNode支持的屬性直接設(shè)置步鉴,有很多不支持的屬性需要
//Hummer自己進(jìn)行處理,自己處理部分查看HMBase.setHummerStyle方法璃哟,都是背景的處理
// 設(shè)置控件樣式
    static void applyStyle(boolean needIntercept, HMBase view, Map style) {
        if (view == null || style == null) {
            return;
        }
        Map<String, Object> transitionStyle = new HashMap<>();
        for (Object k : style.keySet()) {
            String key = String.valueOf(k);
            Object value = style.get(k);
            if (needIntercept && HummerLayoutExtendUtils.interceptDisplayStyle(view, key, value)) {
                // 如果需要攔截氛琢,并且是Display相關(guān)屬性,需要先被攔截
                continue;
            } else if (Hummer.POSITION.equals(key) || Hummer.POSITION_TYPE.equals(key) || Hummer.DISPLAY.equals(key)) {
                // 如果是Position或者Display相關(guān)屬性随闪,優(yōu)先處理
                if (view.setHummerStyle(key, value)) {
                    continue;
                }
            } else if (key.startsWith(Hummer.TRANSITION)) {
                // transition 參數(shù)跳過  在最后處理
                transitionStyle.put(key, value);
                continue;
            }
            try {
                // transform 必須走 animator 進(jìn)行變化
                if (Hummer.TRANSFORM.equals(key) || (isTransitionStyle(key) && (view.supportTransitionStyle("all") || view.supportTransitionStyle(key)))) {
                    view.handleTransitionStyle(key, value);
                } else if (isYogaStyle(key)) {
                    // 處理Yoga支持樣式
                    applyYogaStyle(view.getYogaNode(), key, value); // 耗時1ms
                } else {
                    // 處理Hummer自定義樣式
                    applyHummerStyle(view, key, value); // 耗時1ms
                }
            } catch (Exception e) {
                // 處理異常信息顯示不全的問題阳似,補充異常是在哪個key和value下產(chǎn)生
                ExceptionUtil.addStackTrace(e, new StackTraceElement("<<Style>>", "", String.format("%s: %s", key, value), -1));
                HummerException.nativeException(view.getJSValue().getJSContext(), e);
            }
        }
        view.getView().requestLayout();
        // 設(shè)置transition樣式
        for (Object k : transitionStyle.keySet()) {
            String key = String.valueOf(k);
            Object value = style.get(k);

            view.setTransitionStyle(key, value);
        }
        view.runAnimator();
    }

第三步渲染

//js側(cè)調(diào)用view.appendCild()會回調(diào)到invoke方法,通過methodName判斷
@Override
  protected Object invoke(View instance, String methodName, Object[] params) {
    Object jsRet = null;
    switch (methodName) {
      case "appendChild": {
        long childId0 = params.length > 0 && params[0] != null ? ((Number) params[0]).longValue() : 0;
        HMBase param0 = mInstanceManager.get(childId0);
        instance.appendChild(param0);
      } break;
    }
    return jsRet;
  }
//調(diào)用到j(luò)ava側(cè)View組件
@Component("View")
public class View extends HummerLayoutExtendView {
   ......
    @Override
    @JsMethod("appendChild")
    public void appendChild(HMBase child) {
        super.appendChild(child);
        if (child == null) {
            return;
        }
        hummerNode.appendChild(child.getNode());
       ......
    }
    ......
}
    /**
     * 從如下代碼看如果YogaLayout支持的屬性直接調(diào)用addView添加
     * 如果不支持的屬性需要單獨進(jìn)行處理
     */
    public void appendChild(HMBase child) {
        .....
        // 支持 position:fixed 布局樣式铐伴,添加至窗口視圖
        if (child.getPosition() == HummerLayoutExtendUtils.Position.FIXED) {
            hummerContext.getContainer().addView(child);
            FixedNoneBox fixedNoneBox = new FixedNoneBox(hummerContext);
            fixedNoneBoxMap.put(child, fixedNoneBox);
            finalChild = fixedNoneBox;
        }
        // 支持 display:inline 布局樣式撮奏,外部擴展橫向flex布局
        if (getDisplay() == HummerLayoutExtendUtils.Display.BLOCK) {
            HummerLayoutExtendUtils.markExtendCssView(child);
            if ((child.getDisplay() == HummerLayoutExtendUtils.Display.INLINE
                    || child.getDisplay() == HummerLayoutExtendUtils.Display.INLINE_BLOCK)) {
                HMBase lastChild = getView().getLastChild();
                if (lastChild instanceof InlineBox) {
                    child.setInlineBox((InlineBox) lastChild);
                    ((InlineBox) lastChild).add(child);
                    finalChild = null;
                } else {
                    InlineBox box = new InlineBox(hummerContext);
                    child.setInlineBox(box);
                    box.add(child);
                    inlineBoxes.add(box);
                    finalChild = box;
                }
            }
        }
        // 擴展樣式處理
        if (HummerLayoutExtendUtils.isExtendCssView(child)) {
            HummerLayoutExtendUtils.applyChildDisplayStyle(getDisplay().value(), child);
        }
        // 默認(rèn)處理
        if (finalChild != null) {
            getView().addView(finalChild);
        }
    }

 /**
     *HummerLayout中調(diào)用YogaLayout.addView添加View,此時subview.getYogaNode()已經(jīng)設(shè)置好Yoga支持的屬性
     *添加子視圖
     * @param subview 子視圖
     * @param index 位置
     */
    public void addView(@NonNull HMBase subview, int index) {
        if (subview == null) {
            return;
        }
        addView(subview.getView(), subview.getYogaNode());
        // 當(dāng)removeChild再addChild的時候盛杰,data會被置為null挽荡,需要重新setData
        if (subview.getYogaNode().getData() == null) {
            subview.getYogaNode().setData(subview.getView());
        }
        int childCount = getYogaNode().getChildCount();
        index = index < 0 ? childCount : index;
        getYogaNode().addChildAt(subview.getYogaNode(), index);
        children.put(subview.getViewID(), subview);
        if (index == childCount) {
            lastChild = subview;
        }
    }

總結(jié):

  1. Hummer框架通過quickjs和jni實現(xiàn)對js側(cè)和java側(cè)的通信橋梁;
  2. 通過提前注入js組件使得開發(fā)者可以使用預(yù)先設(shè)置的組件和功能即供;
  3. java側(cè)注冊和js一對一的組件定拟,通過橋梁的方式回調(diào)到j(luò)ava側(cè)的組件實現(xiàn)相應(yīng)的操作
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市逗嫡,隨后出現(xiàn)的幾起案子青自,更是在濱河造成了極大的恐慌,老刑警劉巖驱证,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件延窜,死亡現(xiàn)場離奇詭異,居然都是意外死亡抹锄,警方通過查閱死者的電腦和手機逆瑞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門荠藤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人获高,你說我怎么就攤上這事哈肖。” “怎么了念秧?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵淤井,是天一觀的道長。 經(jīng)常有香客問我摊趾,道長币狠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任砾层,我火速辦了婚禮漩绵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘梢为。我一直安慰自己渐行,他們只是感情好轰坊,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布铸董。 她就那樣靜靜地躺著,像睡著了一般肴沫。 火紅的嫁衣襯著肌膚如雪粟害。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天颤芬,我揣著相機與錄音悲幅,去河邊找鬼。 笑死站蝠,一個胖子當(dāng)著我的面吹牛汰具,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播菱魔,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼留荔,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了澜倦?” 一聲冷哼從身側(cè)響起聚蝶,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎藻治,沒想到半個月后碘勉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡桩卵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年验靡,在試婚紗的時候發(fā)現(xiàn)自己被綠了倍宾。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡胜嗓,死狀恐怖凿宾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情兼蕊,我是刑警寧澤初厚,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站孙技,受9級特大地震影響产禾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜牵啦,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一亚情、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧哈雏,春花似錦楞件、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至彭羹,卻和暖如春黄伊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背派殷。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工还最, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人毡惜。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓拓轻,卻偏偏與公主長得像,于是被迫代替她去往敵國和親经伙。 傳聞我的和親對象是個殘疾皇子扶叉,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容