weex eros框架原始碼解析

雅爸學技術發表於2018-05-27

weex eros是基於alibaba weex框架進行二次封裝的客戶端跨平臺開發框架,主要是為前端開發者(可以不用熟悉客戶端開發)提供的一站式客戶端app開發解決方案。官網地址為:https://bmfe.github.io/eros-docs/#/。為了方便前端開發者和客戶端開發者瞭解框架的原理和遇到問題時更快的定位,我將從原始碼的角度解析功能。


eros主要功能介紹

  1. eros cli是eros開發腳手架,可以用來生成eros專案,提供了編譯,打包,執行,更新等一系列開發功能。
  2. eros widget在js端提供了vue例項中可以方便呼叫的許多原生擴充套件功能(請求,路由,事件釋出訂閱,本地儲存等)。
  3. eros module在原生端對eros widget功能進行真正實現。
  4. eros component在原生端實現的擴充套件元件
  5. eros通過appboard配置實現多頁面的公共程式碼js bundle抽出。
  6. eros通過mediator實現多頁面記憶體共享。

首先從eros widget原始碼開始分析

eros widget提供了請求,路由,本地儲存,原生導航,釋出訂閱等功能,下面將從請求功能去分析eros widget原始碼實現。


在eors官網中介紹的請求的定義是axios,前端的開發者看到這個會覺得很熟悉,因為有個著名的前端開源Http請求框架也叫作這個名字,我剛開始看到這個也是非常興奮,因為axios開源框架功能很強大,所以覺得基於業務的Http請求封裝很easy,可以直接複用了,但是用了之後發現不是那麼回事,後面研究了原始碼之後,才知道eros只是把請求功能叫axios這個名字,但是跟開源框架axios背後實現原理,實現功能完全不一樣!所以大家不要誤解了!

我們先從Js呼叫端開始分析:

  1. 通過eros-cli建立專案之後,我們可以看到關於請求的配置有兩個地方:

weex eros框架原始碼解析
在config/index.js檔案中我們可以看到就做了一個事情:new widget()建立了一個widget物件。new widget是eros widget功能的入口,在new widget()時通過傳參完成了widget相關功能的配置,圖中ajax物件正是對widget axios功能的配置。通過require引用找到Widget定義的地方,分析widget原始碼,也就是eros widget庫的相關程式碼。

// 配置方法
import './src/mixins.js'

import './src/font.js'
// 彈窗
import './src/notice.js'
// 獲取地理位置
import './src/geo.js'
// 獲取相機資訊
import './src/camera.js'
// 圖片操作相關
import './src/image.js'
// 設定導航
import './src/nav.js'
// 支付相關(已抽離第三方外掛)
// import './src/pay.js'
// bindingx
import './src/bindingx.js'
// 儲存相關
import './src/storage.js'
// 全域性事件
import './src/events.js'
// 分享(已抽離第三方外掛)
// import './src/share.js'
// 工具方法
import './src/tools.js'

import './src/coms.js'

// 路由
import Router from './src/router.js'
// 傳送請求
import Axios from './src/axios.js'

let instance = null
export default class Widget {
	constructor ({ router, ajax }) {
		if (!instance) {
		    //這裡是我們需要關注的,這裡建立的請求物件,並且掛載在Vue裡
			Vue.use(new Axios(ajax))
			Vue.use(new Router(router))
			instance = this
		}
        return instance
	}
}
複製程式碼

2.程式碼我們可以很清楚的看到,整個widget的功能都是在這裡被引入的,相關的配置資訊也是通過widget的建構函式傳到對應功能模組的。我們可以看到我們分析的Axios定義就在./src/axios.js這個檔案裡。繼續往下分析axios.js程式碼:

import _isFunction from 'lodash/isFunction'
const bmAxios = weex.requireModule('bmAxios')//請求的真正執行者,這裡對應一個weex原生自定義module,註冊名字叫bmAxios
export default class Axios {
    //建構函式,通過外部傳遞請求相關配置引數
    constructor ({ timeout, apis, baseUrl = '', requestHandler, responseHandler }) {
        this.apis = apis //apis是配置請求別名的到路徑的對映,別名通過它可以找到真實路徑
        this.timeout = timeout//請求的超時時間
        this.baseUrl = baseUrl//請求的基礎url
        this.requestHandler = requestHandler//請求的攔截器
        this.responseHandler = responseHandler//響應的攔截器
        return this
    }
    //vue自定義元件安裝函式,前面說的vue.use()會呼叫該函式進行自定義元件的新增
    install (Vue) {
        /**
         * Contributor: Eric Xiao.
         * Description: extend promise.
         * Eros thanks every contributor.
         */
        Promise.prototype.finally = function (callback) {
            const P = this.constructor;
            return this.then(
                value => P.resolve(callback()).then(() => value),
                reason => P.resolve(callback()).then(() => { throw reason })
            );
        };
        Promise.prototype.done = function (onFulfilled, onRejected) {
            this.then(onFulfilled, onRejected)
                .catch(function (reason) {
                    // Throw a global error
                    setTimeout(() => { throw reason }, 0);
                });
        };

        const self = this
        //此處是在vue原型中掛載一個$fetch函式,掛載之後在vue例項中可以通過this.$fetch進行呼叫該函式,該函式是eros http請求的呼叫函式,支援多種http請求方式。
        Vue.prototype.$fetch = (options) => {
            return new Promise((resolve, reject) => {
                //判斷requestHandle是否是函式,如果是函式則先執行requestHandle對請求進行處理,處理完成後再由handleAxios處理,否則直接由handleAxios處理。
                if (_isFunction(self.requestHandler)) {
                    self.requestHandler(options, () => { 
                    //統一的http功能處理
                    handleAxios(options, resolve, reject) 
                    })
                } else {
                  //統一的http功能處理
                    handleAxios(options, resolve, reject)
                }
            })
        }
        
        //vue原型上提供this.$get發起get請求的呼叫,邏輯同上
        Vue.prototype.$get = (options) => {
            options.method = 'GET'
            return new Promise((resolve, reject) => {
                if (_isFunction(self.requestHandler)) {
                    self.requestHandler(options, () => { handleAxios(options, resolve, reject) })
                } else {
                    handleAxios(options, resolve, reject)
                }
            })
        }
        //vue原型上提供this.$post發起Post請求的呼叫,邏輯同上
        Vue.prototype.$post = (options) => {
            options.method = 'POST'
            return new Promise((resolve, reject) => {
                if (_isFunction(self.requestHandler)) {
                    self.requestHandler(options, () => { handleAxios(options, resolve, reject) })
                } else {
                    handleAxios(options, resolve, reject)
                }
            })
        }
        //請求js端真正處理者
        function handleAxios (options, resolve, reject) {
            //首先對請求引數進行解析
            const { name, url, data, method, header, params } = options
            //通過name去apis配置裡找真正請求路徑
            const requestPath = name && pathFormater(name, params)
            //這裡才是真正的請求功能的發起者,通過eros自定義weex的bmAxios原生模組真正發起請求,通過傳遞迴調函式給原生從而在請求執行完之後獲取到請求結果,再由promise對結果進行分發,如果自己定義了responseHandler函式,則由呼叫者在responseHandler函式中根據http status和業務code去分發請求結果
            bmAxios.fetch({
                url: url || (self.baseUrl + requestPath),
                data: data || {},
                method: method && method.toUpperCase() || 'GET',
                header: header || {},
                timeout: self.timeout || 30000
            }, (resData) => {
                // 統一的監控
                if (_isFunction(self.responseHandler)) {
                    self.responseHandler(options, resData, resolve, reject)
                } else {
                    resolve(resData)
                }
            })
        }
        //此函式是用來根據name去對映apis路徑,以及對路徑引數進行賦值替換
        function pathFormater (name, params) {
            let _path = self.apis[name]
            const matcher = _path.match(/[^{][a-zA-Z0-9]+(?=\})/g)

            if (matcher && matcher.length) {
                matcher.forEach(item => {
                    if (!params[item]) throw new Error(`you are using dynamic params, but ${item} not existed in your params`)
                    _path = _path.replace(`{${item}}`, params[item] || 'undefined')
                })
            }

            return _path
        }
    }
}
複製程式碼

3.通過上面的程式碼分析我們知道了在vue中通過this.$fetch呼叫真正的js處理邏輯,實際上js只是對請求引數進行定義和簡單功能的封裝。真正實現http呼叫處還是在原生,這個原生是相對的,如果jsbundle是執行在android上則是通過android weex模組去呼叫,如果執行在ios則通過ios weex模組去呼叫。

下面分析eros android端原始碼

  1. 首先找到android erosSdk中的 AxiosModule.java類檔案。這個類裡面定義了js端呼叫的相關方法。
package com.benmu.framework.extend.module;


import com.benmu.framework.constant.WXConstant;
import com.benmu.framework.constant.WXEventCenter;
import com.benmu.framework.manager.ManagerFactory;
import com.benmu.framework.manager.impl.dispatcher.DispatchEventManager;
import com.benmu.framework.model.WeexEventBean;
import com.taobao.weex.annotation.JSMethod;
import com.taobao.weex.bridge.JSCallback;
import com.taobao.weex.common.WXModule;


/**
 * Created by Carry on 17/1/16.
 */

public class AxiosModule extends WXModule {

    @JSMethod(uiThread = false)
    public void fetch(String params, final JSCallback jsCallback) {
        WeexEventBean eventBean = new WeexEventBean();
        eventBean.setContext(mWXSDKInstance.getContext());
        eventBean.setKey(   WXEventCenter.EVENT_FETCH);
        eventBean.setJsParams(params);
        eventBean.setJscallback(jsCallback);
        ManagerFactory.getManagerService(DispatchEventManager.class).getBus().post
                (eventBean);
    }
    //其他省略
    ....
}
複製程式碼
  1. 整個類看起來很簡單,跟js端bmAxios對應的方法就一個fetch(),裡面有兩個引數,一個為js傳遞過來的請求引數,另外一個是js端的回撥函式。我們可以看到方法的實現裡面並沒有具體的執行邏輯,只是建立了一個叫做WeexEventBean的實體類,並把引數params和jsCallback等傳給了它,並通過類似bus的物件給post出去了。在這裡說一下,所有的eros自定義module都是通過事件匯流排(android otto框架)的機制進行分發和功能處理的。WeexEventBean是對所有Module功能入口的統一封裝,它通過統一包裝所有的js呼叫請求,根據建立時定義的key,通過反射分發到對應key的功能類上進行處理。下面我們找到WeexEventBean事件的接收處。
package com.benmu.framework.event;

import android.app.Activity;
import android.content.Context;
import android.text.TextUtils;
import com.benmu.framework.adapter.router.RouterTracker;
import com.benmu.framework.constant.WXEventCenter;
import com.benmu.framework.manager.ManagerFactory;
import com.benmu.framework.manager.impl.dispatcher.DispatchEventManager;
import com.benmu.framework.model.BaseEventBean;
import com.benmu.framework.model.WeexEventBean;
imporot cm.benmu.framework.utils.JsPoster;
import com.benmu.wxbase.EventGate;
import com.benmu.wxbase.EventGateFactory;
import com.squareup.otto.Subscribe;
import com.taobao.weex.WXSDKInstance;
import com.taobao.weex.bridge.JSCallback;

import java.util.ArrayList;

/**
 * Created by Carry on 2017/8/23.
 */

public class DispatchEventCenter {
    //這裡DispatchEventCenter建立了一個單例物件
    private static DispatchEventCenter mInstance = new DispatchEventCenter();

    private DispatchEventCenter() {
    }

    public static DispatchEventCenter getInstance() {
        return mInstance;
    }

    //這裡對DispatchEventCenter物件進行註冊,註冊之後可以接收Bus分發的事件
    public void register() {
        ManagerFactory.getManagerService(DispatchEventManager.class).getBus().register(this);
    }

     //這裡對DispatchEventCenter物件進行解注,解注之後取消接收Bus分發的事件
    public void unregister() {
        ManagerFactory.getManagerService(DispatchEventManager.class).getBus().unregister(this);
    }

    //這裡是WeexEventBean事件的接收,所有eros module post出來的WeexEventBean型別物件都在這裡接收之後進行轉發
    @Subscribe
    public void onWeexEvent(WeexEventBean weexEventBean) {
        if (weexEventBean == null) return;
        Context context = safeContext(weexEventBean.getContext());
        if (context == null) return;
        String params = weexEventBean.getJsParams();
        switch (weexEventBean.getKey()) {
            case WXEventCenter.EVENT_IMAGE_UPLOAD:
            //這裡就是我們要找的key為EVENT_FETCH的處理地方
            case WXEventCenter.EVENT_FETCH:
                //這裡我們直接傳遞了一個class絕對路徑的string,此處的做法值得商榷。下面我們繼續看reflectionClazzPerform方法
                reflectionClazzPerform("com.benmu.framework.event.http.EventFetch", context
                        , weexEventBean
                        , "", weexEventBean.getKey());
                break;

            ...中間省略
            default:
                reflectionClazzPerform(weexEventBean.getKey()
                        , context
                        , weexEventBean
                        , "");

                break;
        }
    }

    //真正反射建立物件的呼叫邏輯在這個函式裡
    private void reflectionClazzPerform(String clazzName, Context context, WeexEventBean weexEventBean, String errosMsg, String type) {
        //這裡EventGateFactory根據clazzName反射去建立物件,但是為啥物件是EventGate型別的呢,因為eros所有的Event都實現了EventGate介面,所以建立出來的物件都是EventGate實現類的物件。
        EventGate event = EventGateFactory.getEventGate(clazzName);
        String params = weexEventBean.getJsParams();
        if (null != event) {
            //根據上面呼叫邏輯,type為WXEventCenter.EVENT_FETCH,不為空,直接走else邏輯
            if (TextUtils.isEmpty(type)) {
                event.perform(context, weexEventBean);
            } else {
             //event.perform是所有實現EventGate介面的Event物件執行功能的入口,本文分析的是com.benmu.framework.event.http.EventFetch這個類,下面我去找EventFetch物件的perform方法。
                event.perform(context, weexEventBean, type);
            }
        } else {
            //如果找不到對應的EVENT處理類,則給js回撥失敗處理
            if (TextUtils.isEmpty(params)) {
                JsPoster.postFailed(weexEventBean.getJscallback());
            } else {
                JsPoster.postFailed(errosMsg, weexEventBean.getJscallback());
            }

        }
    }

}

複製程式碼
  1. 通過DispatchEventCenter 類,我們把js端的呼叫真正的分發到了對應的事件類上進行處理,每個事件類名對應著一個js呼叫模組的函式名。此處我們繼續分析:com.benmu.framework.event.http.EventFetch這個類:
package com.benmu.framework.event.http;

import android.content.Context;
import android.text.TextUtils;
import android.widget.Toast;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.benmu.framework.constant.Constant;
import com.benmu.framework.constant.WXEventCenter;
import com.benmu.framework.http.okhttp.OkHttpUtils;
import com.benmu.framework.http.okhttp.callback.StringCallback;
import com.benmu.framework.http.okhttp.exception.CancelException;
import com.benmu.framework.http.okhttp.exception.HttpException;
import com.benmu.framework.http.okhttp.exception.IrregularUrlException;
import com.benmu.framework.http.okhttp.utils.L;
import com.benmu.framework.manager.ManagerFactory;
import com.benmu.framework.manager.impl.AxiosManager;
import com.benmu.framework.manager.impl.ImageManager;
import com.benmu.framework.manager.impl.ModalManager;
import com.benmu.framework.manager.impl.ParseManager;
import com.benmu.framework.manager.impl.PersistentManager;
import com.benmu.framework.manager.impl.dispatcher.DispatchEventManager;
import com.benmu.framework.model.AxiosGet;
import com.benmu.framework.model.AxiosPost;
import com.benmu.framework.model.AxiosResultBean;
import com.benmu.framework.model.UploadImageBean;
import com.benmu.framework.model.UploadResultBean;
import com.benmu.framework.model.WeexEventBean;
import com.benmu.framework.utils.JsPoster;
import com.benmu.framework.utils.TextUtil;
import com.benmu.wxbase.EventGate;
import com.lzy.imagepicker.bean.ImageItem;
import com.squareup.otto.Subscribe;
import com.taobao.weex.bridge.JSCallback;

import java.util.ArrayList;

import okhttp3.Call;


/**
 * Created by Carry on 2017/8/16.
 */

public class EventFetch extends EventGate {
    private JSCallback mUploadAvatar;
    private Context mUploadContext;

    //根據上面分析執行到這裡
    @Override
    public void perform(Context context, WeexEventBean weexEventBean, String type) {
        String params = weexEventBean.getJsParams();pe
        //此處type為WXEventCenter.EVENT_FETCH,所以直接執行if裡面的fetch方法
        if (WXEventCenter.EVENT_FETCH.equals(type)) {
            fetch(params, context, weexEventBean.getJscallback());
        } else if (WXEventCenter.EVENT_IMAGE_UPLOAD.equals(type)) {
            uploadImage(params, context, weexEventBean.getJscallback());
        }
    }

    //js端fetch請求分發到這裡
    public void fetch(String params, final Context context, final JSCallback jscallback) {
        //建立引數解析管理類
        ParseManager parseManager = ManagerFactory.getManagerService(ParseManager.class);
        //建立Axios執行管理類
        AxiosManager axiosManager = ManagerFactory.getManagerService(AxiosManager.class);
        //把js 引數解析成Json物件
        JSONObject object = parseManager.parseObject(params);
        //獲取請求的Url
        final String mUrl = object.getString("url");

        Boolean noRepeat = object.getBoolean("noRepeat");
        //如果重複發了則取消上一個請求
        if (noRepeat != null && noRepeat) {
            axiosManager.cancel(mUrl);
        }
        //此處提供了所有的restful請求方法,說明js端可以發起所有的restful請求,根據對應的method進行分發處理
        switch (object.getString("method")) {

            case OkHttpUtils.METHOD.GET:
                AxiosGet axiosGet = parseManager.parseObject(params, AxiosGet.class);
                get(context, axiosManager, axiosGet, jscallback);
                break;
            case OkHttpUtils.METHOD.POST:
                AxiosPost axiosPost = parseManager.parseObject(params, AxiosPost.class);
                post(context, axiosManager, axiosPost, jscallback);
                break;
            case OkHttpUtils.METHOD.HEAD:
                AxiosGet axiosHead = parseManager.parseObject(params, AxiosGet.class);
                head(context, axiosManager, axiosHead, jscallback);
                break;
            case OkHttpUtils.METHOD.DELETE:
                AxiosPost axiosDelete = parseManager.parseObject(params, AxiosPost.class);
                delete(context, axiosManager, axiosDelete, jscallback);
                break;
            case OkHttpUtils.METHOD.PUT:
                AxiosPost axiosPut = parseManager.parseObject(params, AxiosPost.class);
                put(context, axiosManager, axiosPut, jscallback);
                break;
            case OkHttpUtils.METHOD.PATCH:
                AxiosPost axiosPatch = parseManager.parseObject(params, AxiosPost.class);
                patch(context, axiosManager, axiosPatch, jscallback);
                break;
        }
    }
    
    //暫時只分析get請求,其他是一樣的。
    private void get(final Context context, AxiosManager axiosManager, AxiosGet axiosGet, final
    JSCallback jscallback) {
        //此處只是呼叫axiosManager.get()方法,真正的http呼叫發起邏輯是在axiosManager類裡面,通過給該方法傳遞StringCallback()回撥獲取到http執行之後的結果,然後對結果進行解析之後回撥到Js端。
        axiosManager.get(axiosGet.url, axiosGet.data, axiosGet.header, new
                StringCallback() {

                    @Override
                    public void onError(Call call, Exception e, int id) {
                        //請求出現error之後的處理
                        parseError(context, e, jscallback);
                    }

                    @Override
                    public void onResponse(String response, int id) {
                        //請求成功之後的處理
                        parseResponse(response, jscallback, code);
                    }
                }, axiosGet.url, axiosGet.timeout);
    }

    //請求錯誤的解析
    private void parseError(Context context, Exception e, JSCallback callback) {
        if (e instanceof CancelException) {
            //request canceled
            ModalManager.BmLoading.dismissLoading(context);
            return;
        }
        AxiosResultBean bean = new AxiosResultBean();
        if (e instanceof HttpException) {
            HttpException httpException = (HttpException) e;
            bean.status = httpException.getmErrorCode();
            bean.errorMsg = httpException.getmErrorMessage();
        } else if (e instanceof IrregularUrlException) {
            IrregularUrlException irregularUrlException = (IrregularUrlException) e;
            bean.status = 9;
            bean.errorMsg = irregularUrlException.getmErrosMeeage();
        }
        //請求錯誤之後給js端回撥,回撥的data為AxiosResultBean,到這裡js——>java——>js 整個呼叫過程結束了
        if (callback != null) {
            callback.invoke(bean);
        }
    }
    
    //請求正常的解析
    private void parseResponse(String response, JSCallback callBack, int code) {
        try {
            AxiosResultBean bean = new AxiosResultBean();
            if (callBack != null && !TextUtils.isEmpty(response)) {
                bean.status = code;
                bean.errorMsg = "";
                bean.data = JSON.parse(response);
                //請求正常返回之後給js端回撥,回撥的data為AxiosResultBean,到這裡js——>java——>js 整個呼叫過程結束了
                callBack.invoke(bean);
            }
        } catch (Exception e) {
            e.printStackTrace();
            L.e("json 解析錯誤");
            AxiosResultBean bean = new AxiosResultBean();
            bean.status = -1;
            bean.errorMsg = "json 解析錯誤";
            if (callBack != null) {
            //請求錯誤之後給js端回撥,回撥的data為AxiosResultBean,到這裡js——>java——>js 整個呼叫過程結束了
                callBack.invoke(bean);
            }
        }
    }
}

複製程式碼
  1. 通過EventFetch原始碼,我們知道EventFetch只是做了請求引數的解析,根據請求http method進行對應分發處理,並且對http請求之後的結果進行封裝之後發起jsCallback處理,jsCallback呼叫之後,整個js——>java——>js呼叫過程結束。但是真正http執行邏輯還在AxiosManager裡面,下面我們繼續分析AxiosManager類:
package com.benmu.framework.manager.impl;

import android.content.Context;
import android.net.Uri;
import android.text.TextUtils;

import com.benmu.framework.BMWXEnvironment;
import com.benmu.framework.extend.adapter.WeexOkhttp3Interceptor;
import com.benmu.framework.http.Api;
import com.benmu.framework.http.BMPersistentCookieStore;
import com.benmu.framework.http.okhttp.OkHttpUtils;
import com.benmu.framework.http.okhttp.builder.GetBuilder;
import com.benmu.framework.http.okhttp.builder.OkHttpRequestBuilder;
import com.benmu.framework.http.okhttp.builder.OtherRequestBuilder;
import com.benmu.framework.http.okhttp.builder.PostFormBuilder;
import com.benmu.framework.http.okhttp.callback.FileCallBack;
import com.benmu.framework.http.okhttp.callback.StringCallback;
import com.benmu.framework.http.okhttp.cookie.CookieJarImpl;
import com.benmu.framework.http.okhttp.exception.IrregularUrlException;
import com.benmu.framework.http.okhttp.log.LoggerInterceptor;
import com.benmu.framework.manager.Manager;
import com.benmu.framework.manager.ManagerFactory;
import com.benmu.framework.manager.impl.dispatcher.DispatchEventManager;
import com.benmu.framework.model.UploadResultBean;
import com.benmu.framework.utils.AppUtils;
import com.benmu.framework.utils.DebugableUtil;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import okhttp3.Call;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;


/**
 * Created by Carry on 2017/8/7. default implement by okHttpUtils
 */

public class AxiosManager extends Manager {
    private static final String DEFAULT_MEDIATYPE = "application/json; charset=utf-8";
    private static final String DEFAULT_HOST = "http://app.weex-eros.com";

    //建立OkHttpClient物件
    public OkHttpClient createClient(Context context, long timeout) {
        CookieJarImpl cookieJar = new CookieJarImpl(new BMPersistentCookieStore
                (context));

        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .addInterceptor(new LoggerInterceptor("TAG"))
                //介面超時時間  預設3000毫秒
                .connectTimeout(timeout == 0 ? 3000L : timeout, TimeUnit.MILLISECONDS)
                .readTimeout(timeout == 0 ? 30000L : timeout, TimeUnit.MILLISECONDS).cookieJar
                        (cookieJar);
        if (DebugableUtil.isDebug()) {
            builder.addNetworkInterceptor(new WeexOkhttp3Interceptor());
        }
        return builder.build();
    }

    public void initClient(Context context) {
        OkHttpUtils.initClient(createClient(context, 0));
    }

    public void cancel(Object tag) {

    }
    
    //上面EventFetch get請求呼叫到這裡,這裡是真正發起請求的地方,最終通過okhttpUtil傳送的請求。
    public void get(String mUrl, HashMap<String, String> params, HashMap<String, String> header,
                    StringCallback stringCallback, Object tag, long timeout) {
        mUrl = safeUrl(mUrl);
        if (mUrl == null) {
            if (stringCallback != null) {
                stringCallback.onError(null, new IrregularUrlException("url不合法"), 0);
            }
            return;
        }
        if (header == null) {
            header = new HashMap<>();
        }
        setTimeout(timeout);
        //此處使用的是對okhttp進行封裝的OkHttpUtils框架傳送的get請求,並通過stringCallback回撥真正的請求結果。
        GetBuilder builder = OkHttpUtils.get().url(mUrl).tag(tag).headers(header);
        generateParams(params, builder);
        builder.build().execute(stringCallback);
    }

    private void setTimeout(long timeout) {
        if (timeout != 0) {
            OkHttpUtils.getInstance().updateHttpClient(createClient(BMWXEnvironment
                    .mApplicationContext, timeout));
        }
    }

    private RequestBody createRequestBodyByMediaType(Map<String, String> header, String content) {
        if (header != null && !TextUtils.isEmpty(header.get("Content-Type"))) {
            String s = header.get("Content-Type");
            MediaType mediaType = null;
            try {
                mediaType = MediaType.parse(s);
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (mediaType == null) {
                mediaType = MediaType.parse(DEFAULT_MEDIATYPE);
            }
            return RequestBody.create(mediaType, content);
        }
        return RequestBody.create(MediaType.parse(DEFAULT_MEDIATYPE), content);
    }

    private String safeUrl(String origin) {
        if (origin == null) return null;
        Uri parse = Uri.parse(origin);
        StringBuilder builder = new StringBuilder();
        Uri requestHost = Uri.parse(TextUtils.isEmpty(Api.BASE_URL) ? DEFAULT_HOST : Api.BASE_URL);
        if (TextUtils.isEmpty(parse.getScheme())) {
            builder.append(requestHost.getScheme());
        } else {
            builder.append(parse.getScheme());
        }
        builder.append("://");
        if (TextUtils.isEmpty(parse.getHost())) {
            builder.append(requestHost.getHost());
        } else {
            builder.append(parse.getHost());
        }
        if (parse.getPort() != -1) {
            builder.append(":").append(parse.getPort());
        }
        if (!TextUtils.isEmpty(parse.getPath())) {
            builder.append(origin.substring(origin.indexOf(parse.getPath())));
        }
        String finalUrl = builder.toString();

        return HttpUrl.parse(finalUrl) == null ? null : builder.toString();
    }


    private void generateParams(Map<String, String> params, OkHttpRequestBuilder builder) {
        if (params == null) {
            params = new HashMap<>();
        }
        if (builder instanceof GetBuilder) {
            GetBuilder getBuilder = (GetBuilder) builder;

            for (Map.Entry<String, String> entry : params.entrySet()) {
                getBuilder.addParams(entry.getKey().trim(), entry.getValue().trim());
            }
        }
    }

    /**
     * 組合返回給前端的Js 資料
     */
    public UploadResultBean resultBean(int code, String message, ArrayList<String> arrayList) {
        UploadResultBean uploadResultBean = new UploadResultBean();
        uploadResultBean.resCode = code;
        uploadResultBean.msg = message;
        uploadResultBean.setData(arrayList);
        return uploadResultBean;
    }
}

複製程式碼
  1. AxiosManager使用的是OkhttpUtils發起的http請求,它對okhttpclient進行的一些配置和攔截。至此,整個js端axios.fetch發起的呼叫分析結束。

總結

整個eros js端和原生功能封裝的比較完整,基本上是按照weex原生功能風格去擴充套件的。對於前端同學進行純weex專案開發來說,eros提供了一站式解決方案還是比原生weex開發方便很多。但是也有一些不足的地方,比如說在js 端在非Vue例項中呼叫widget功能(儲存功能可能js裡呼叫)還是稍微有點不方便,需要自己去擴充套件。還有提供的http模組可配置性和攔截器功能比原生js http框架弱很多,對於複雜的js請求處理起來很彆扭,需要自己擴充套件等等,android原生端實現模組功能切分的比較清晰,不過感覺整個呼叫邏輯有點繞,還有就是通過絕對路徑Key去反射建立物件這個做法有隱患。當然,能夠在weex原生的基礎上擴充套件出來這麼多功能,還是比較讚的,很多地方還是值得學習的!

後記

下次分析一下eros是如何在weex多頁應用中插入js widget功能,多頁應用中如何在js端進行全域性生命週期監聽和全域性配置處理。

相關文章