weex eros框架源碼解析

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背後實現原理,實現功能徹底不同!因此你們不要誤解了!vue

咱們先從Js調用端開始分析:

  1. 經過eros-cli建立項目以後,咱們能夠看到關於請求的配置有兩個地方:

在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代碼:java

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模塊去調用。android

下面分析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原生的基礎上擴展出來這麼多功能,仍是比較讚的,不少地方仍是值得學習的!ios

後記

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

相關文章
相關標籤/搜索