weex eros框架源码解析

3,248 阅读12分钟

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创建项目之后,我们可以看到关于请求的配置有两个地方:

在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端进行全局生命周期监听和全局配置处理。