精华内容
下载资源
问答
  • 教你写Android网络框架之Request、Response类与请求队列

    万次阅读 多人点赞 2015-01-22 13:55:01
    在教你写Android网络框架之基本架构一文中我们已经介绍了SimpleNet网络框架的基本结构,今天我们就开始从代码的角度来开始切入该网络框架的实现,在剖析的同时我们会分析设计思路,以及为什么要这样做,这样做的好处...

      转载请注明出处,本文来自【 Mr.Simple的博客 】

    我正在参加博客之星,点击这里投我一票吧,谢谢~   

    前言

    教你写Android网络框架之基本架构一文中我们已经介绍了SimpleNet网络框架的基本结构,今天我们就开始从代码的角度来开始切入该网络框架的实现,在剖析的同时我们会分析设计思路,以及为什么要这样做,这样做的好处是什么。这样我们不仅学到了如何实现网络框架,也会学到设计一个通用的框架应该有哪些考虑,这就扩展到框架设计的范畴,通过这个简单的实例希望能给新人一些帮助。当然这只是一家之言,大家都可以有自己的实现思路。

    正如你所看到的,这系列博客是为新人准备的,如果你是高手,请忽略。

    在框架开发当中,很重要的一点就是抽象。也就是面向对象中重要的一条原则: 依赖倒置原则,简单来说就是要依赖抽象,而不依赖具体。这样就使得我们的框架具有可扩展性,同时也满足了开闭原则,即对扩展开放,对修改关闭。针对于我们的网络框架来说,最重要的抽象就是Reqeust类、Response类,因此今天我们就从两个类开始切入。最后我们再引入网络框架中的请求队列(RequestQueue),这是SimpleNet中的中枢神经,所有的请求都需要放到该队列,然后等待着被执行。请求队列就像工厂中的流水线一样,而网络请求就像流水线上的待加工的产品。执行网络请求的对象就类似工厂中的工人,在自己的岗位上等待流水线上传递过来的产品,然后对其加工,加工完就将产品放到其他的位置。它们角色对应关系参考图1,如对SimpleNet的一些角色不太清楚可参考教你写Android网络框架之基本架构一文。

    图1


    Request类

    既然网络框架,那么我们先从网络请求类开始。前文已经说过,既然是框架,那么就需要可扩展性。因此注定了Request是抽象,而不是具体。而对于网络请求来说,用户得到的请求结果格式是不确定,比如有的服务器返回的是json,有的返回的是xml,有的直接是字符串。但是对于Http Response来说,它的返回数据类型都是Stream,也就是我们得到的原始数据都是二进制的流。所以在Request基类中我们必须预留方法来解析Response返回的具体类型,虽然返回的类型不同,但是他们的处理逻辑是一样的,因此我们可把Request作为泛型类,它的泛型类型就是它的返回数据类型,比如Request<String>,那么它的返回数据类型就是String类型的。另外还有请求的优先级、可取消等,我们这里先给出核心代码,然后再继续分析。

    /**
     * 网络请求类. 注意GET和DELETE不能传递请求参数,因为其请求的性质所致,用户可以将参数构建到url后传递进来到Request中.
     * 
     * @author mrsimple
     * @param <T> T为请求返回的数据类型
     */
    public abstract class Request<T> implements Comparable<Request<T>> {
    
        /**
         * http请求方法枚举,这里我们只有GET, POST, PUT, DELETE四种
         * 
         * @author mrsimple
         */
        public static enum HttpMethod {
            GET("GET"),
            POST("POST"),
            PUT("PUT"),
            DELETE("DELETE");
    
            /** http request type */
            private String mHttpMethod = "";
    
            private HttpMethod(String method) {
                mHttpMethod = method;
            }
    
            @Override
            public String toString() {
                return mHttpMethod;
            }
        }
    
        /**
         * 优先级枚举
         * 
         * @author mrsimple
         */
        public static enum Priority {
            LOW,
            NORMAL,
            HIGN,
            IMMEDIATE
        }
    
        /**
         * Default encoding for POST or PUT parameters. See
         * {@link #getParamsEncoding()}.
         */
        private static final String DEFAULT_PARAMS_ENCODING = "UTF-8";
        /**
         * 请求序列号
         */
        protected int mSerialNum = 0;
        /**
         * 优先级默认设置为Normal
         */
        protected Priority mPriority = Priority.NORMAL;
        /**
         * 是否取消该请求
         */
        protected boolean isCancel = false;
    
        /** 该请求是否应该缓存 */
        private boolean mShouldCache = true;
        /**
         * 请求Listener
         */
        protected RequestListener<T> mRequestListener;
        /**
         * 请求的url
         */
        private String mUrl = "";
        /**
         * 请求的方法
         */
        HttpMethod mHttpMethod = HttpMethod.GET;
    
        /**
         * 请求的header
         */
        private Map<String, String> mHeaders = new HashMap<String, String>();
        /**
         * 请求参数
         */
        private Map<String, String> mBodyParams = new HashMap<String, String>();
    
        /**
         * @param method
         * @param url
         * @param listener
         */
        public Request(HttpMethod method, String url, RequestListener<T> listener) {
            mHttpMethod = method;
            mUrl = url;
            mRequestListener = listener;
        }
    
        /**
         * 从原生的网络请求中解析结果,子类覆写
         * 
         * @param response
         * @return
         */
        public abstract T parseResponse(Response response);
    
        /**
         * 处理Response,该方法运行在UI线程.
         * 
         * @param response
         */
        public final void deliveryResponse(Response response) {
            // 解析得到请求结果
            T result = parseResponse(response);
            if (mRequestListener != null) {
                int stCode = response != null ? response.getStatusCode() : -1;
                String msg = response != null ? response.getMessage() : "unkown error";
                mRequestListener.onComplete(stCode, result, msg);
            }
        }
    
        public String getUrl() {
            return mUrl;
        }
    
    
    
        public int getSerialNumber() {
            return mSerialNum;
        }
    
        public void setSerialNumber(int mSerialNum) {
            this.mSerialNum = mSerialNum;
        }
    
        public Priority getPriority() {
            return mPriority;
        }
    
        public void setPriority(Priority mPriority) {
            this.mPriority = mPriority;
        }
    
        protected String getParamsEncoding() {
            return DEFAULT_PARAMS_ENCODING;
        }
    
        public String getBodyContentType() {
            return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();
        }
    
        public HttpMethod getHttpMethod() {
            return mHttpMethod;
        }
    
        public Map<String, String> getHeaders() {
            return mHeaders;
        }
    
        public Map<String, String> getParams() {
            return mBodyParams;
        }
    
    
        public void cancel() {
            isCancel = true;
        }
    
        public boolean isCanceled() {
            return isCancel;
        }
    
        /**
         * 返回POST或者PUT请求时的Body参数字节数组
         *
         */
        public byte[] getBody() {
            Map<String, String> params = getParams();
            if (params != null && params.size() > 0) {
                return encodeParameters(params, getParamsEncoding());
            }
            return null;
        }
    
        /**
         * 将参数转换为Url编码的参数串
         */
        private byte[] encodeParameters(Map<String, String> params, String paramsEncoding) {
            StringBuilder encodedParams = new StringBuilder();
            try {
                for (Map.Entry<String, String> entry : params.entrySet()) {
                    encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding));
                    encodedParams.append('=');
                    encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding));
                    encodedParams.append('&');
                }
                return encodedParams.toString().getBytes(paramsEncoding);
            } catch (UnsupportedEncodingException uee) {
                throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee);
            }
        }
    
        // 用于对请求的排序处理,根据优先级和加入到队列的序号进行排序
        @Override
        public int compareTo(Request<T> another) {
            Priority myPriority = this.getPriority();
            Priority anotherPriority = another.getPriority();
            // 如果优先级相等,那么按照添加到队列的序列号顺序来执行
            return myPriority.equals(another) ? this.getSerialNumber() - another.getSerialNumber()
                    : myPriority.ordinal() - anotherPriority.ordinal();
        }
    
    
    
        /**
         * 网络请求Listener,会被执行在UI线程
         * 
         * @author mrsimple
         * @param <T> 请求的response类型
         */
        public static interface RequestListener<T> {
            /**
             * 请求完成的回调
             * 
             * @param response
             */
            public void onComplete(int stCode, T response, String errMsg);
        }
    }
    

    上述代码Request<T>为抽象类,T则为该请求Response的数据格式。这个T是请求类中的一个比较重要的点,不同的人有不同的需求,即请求Reponse的数据格式并不是都是一样的,我们必须考虑到请求返回类型的多样性,用泛型T来表示返回的数据格式类型,然后Request子类覆写对应的方法实现解析Response的数据格式,最后调用请求Listener将请求结果执行在UI线程,这样整个请求就完成了。

    每个Request都有一个序列号,该序列号由请求队列生成,标识该请求在队列中的序号,该序号和请求优先级决定了该请求在队列中的排序,即它在请求队列的执行顺序。每个请求有请求方式,例如"POST"、"GET",这里我们用枚举来代替,具名类型比单纯的字符串更易于使用。每个Request都可以添加Header、Body参数 ( 关于请求参数的格式可以参考 四种常见的 POST 提交数据方式),并且可以取消。抽象类封装了通用的代码,只有可变的部分是抽象函数,这里只有parseResponse这个函数。

    例如,我们返回的数据格式是Json,那么我们构建一个子类叫做JsonRequest,示例代码如下。

    /**
     * 返回的数据类型为Json的请求, Json对应的对象类型为JSONObject
     * 
     * @author mrsimple
     */
    public class JsonRequest extends Request<JSONObject> {
    
        public JsonRequest(HttpMethod method, String url, RequestListener<JSONObject> listener) {
            super(method, url, listener);
        }
    
        
        /**
         * 将Response的结果转换为JSONObject
         */
        @Override
        public JSONObject parseResponse(Response response) {
            String jsonString = new String(response.getRawData());
            try {
                return new JSONObject(jsonString);
            } catch (JSONException e) {
                e.printStackTrace();
            }
            return null;
        }
    }

    可以看到,实现一个请求类还是非常简单的,只需要覆写 parseResponse函数来解析你的请求返回的数据即可。这样就保证了可扩展性,比如后面如果我想使用这个框架来做一个ImageLoader,那么我可以创建一个ImageRequest,该请求返回的类型就是Bitmap,那么我们只需要覆写parseResponse函数,然后把结果转换成Bitmap即可。

    这里引入了Response类,这个Response类存储了请求的状态码、请求结果等内容,我们继续往下看。

    Response类

    每个请求都对应一个Response,但这里的问题是这个Response的数据格式我们是不知道的。我们写的是框架,不是应用。框架只是构建一个基本环境,并且附带一些比较常用的类,比如这里的JsonRequest。但是重要的一点是可以让用户自由、简单的扩展以实现他的需求。对于Response类来说,我们最重要的一点就是要确定请求结果的数据格式类型。我们都知道,HTTP实际上是基于TCP协议,而TCP协议又是基于Socket,Socket实际上操作的也就是输入、输出流,输出流是向服务器写数据,输入流自然是从服务器读取数据。因此我们在Response类中应该使用InputStream存储结果或者使用更为易于使用的字节数组,这里我们使用字节数组来存储。我们来看Response类。

    /**
     * 请求结果类,继承自BasicHttpResponse,将结果存储在rawData中.
     * @author mrsimple
     */
    public class Response extends BasicHttpResponse {
    
        public byte[] rawData = new byte[0];
    
        public Response(StatusLine statusLine) {
            super(statusLine);
        }
    
        public Response(ProtocolVersion ver, int code, String reason) {
            super(ver, code, reason);
        }
    
        @Override
        public void setEntity(HttpEntity entity) {
            super.setEntity(entity);
            rawData = entityToBytes(getEntity());
        }
    
        public byte[] getRawData() {
            return rawData;
        }
    
        public int getStatusCode() {
            return getStatusLine().getStatusCode();
        }
    
        public String getMessage() {
            return getStatusLine().getReasonPhrase();
        }
    
        /** Reads the contents of HttpEntity into a byte[]. */
        private byte[] entityToBytes(HttpEntity entity) {
            try {
                return EntityUtils.toByteArray(entity);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return new byte[0];
        }
    
    }

    这个类很简单,只是继承了BasicHttpResponse,然后将输入流转换成字节数组,然后包装了几个常用的方法,主要是为了使用简单吧。我们将结果存储为字节数组,这样可以用户可以很方便的将结果转换为String、bitmap等数据类型,如果直接存储的是InputStream,那么在很多时候用户需要在外围将InputStream先转换为字节数组,然后再转换为最终的格式,例如InputStream转为String类型。这也是为什么我们这里选用byte[]而不用InputStream的原因。


    请求队列

    网络请求队列也比较简单,实际上就是内部封装了一个优先级队列,在构建队列时会启动几个NetworkExecutor ( 子线程 )来从请求队列中获取请求,并且执行请求。请求队列会根据请求的优先级进行排序,这样就保证了一些优先级高的请求得到尽快的处理,这也就是为什么Request类中实现了Comparable接口的原因。如果优先级一致的情况下,则会根据请求加入到队列的顺序来排序,这个序号由请求队列生成,这样就保证了优先级一样的情况下按照FIFO的策略执行。

    /**
     * 请求队列, 使用优先队列,使得请求可以按照优先级进行处理. [ Thread Safe ]
     * 
     * @author mrsimple
     */
    public final class RequestQueue {
        /**
         * 请求队列 [ Thread-safe ]
         */
        private BlockingQueue<Request<?>> mRequestQueue = new PriorityBlockingQueue<Request<?>>();
        /**
         * 请求的序列化生成器
         */
        private AtomicInteger mSerialNumGenerator = new AtomicInteger(0);
    
        /**
         * 默认的核心数
         */
        public static int DEFAULT_CORE_NUMS = Runtime.getRuntime().availableProcessors() + 1;
        /**
         * CPU核心数 + 1个分发线程数
         */
        private int mDispatcherNums = DEFAULT_CORE_NUMS;
        /**
         * NetworkExecutor,执行网络请求的线程
         */
        private NetworkExecutor[] mDispatchers = null;
        /**
         * Http请求的真正执行者
         */
        private HttpStack mHttpStack;
    
        /**
         * @param coreNums 线程核心数
         */
        protected RequestQueue(int coreNums, HttpStack httpStack) {
            mDispatcherNums = coreNums;
            mHttpStack = httpStack != null ? httpStack : HttpStackFactory.createHttpStack();
        }
    
        /**
         * 启动NetworkExecutor
         */
        private final void startNetworkExecutors() {
            mDispatchers = new NetworkExecutor[mDispatcherNums];
            for (int i = 0; i < mDispatcherNums; i++) {
                mDispatchers[i] = new NetworkExecutor(mRequestQueue, mHttpStack);
                mDispatchers[i].start();
            }
        }
    
        public void start() {
            stop();
            startNetworkExecutors();
        }
    
        /**
         * 停止NetworkExecutor
         */
        public void stop() {
            if (mDispatchers != null && mDispatchers.length > 0) {
                for (int i = 0; i < mDispatchers.length; i++) {
                    mDispatchers[i].quit();
                }
            }
        }
    
        /**
         * 不能重复添加请求
         * 
         * @param request
         */
        public void addRequest(Request<?> request) {
            if (!mRequestQueue.contains(request)) {
                request.setSerialNumber(this.generateSerialNumber());
                mRequestQueue.add(request);
            } else {
                Log.d("", "### 请求队列中已经含有");
            }
        }
    
        public void clear() {
            mRequestQueue.clear();
        }
    
        public BlockingQueue<Request<?>> getAllRequests() {
            return mRequestQueue;
        }
    
        /**
         * 为每个请求生成一个系列号
         * 
         * @return 序列号
         */
        private int generateSerialNumber() {
            return mSerialNumGenerator.incrementAndGet();
        }
    }

    这里引入了一个HttpStack,这是一个接口,只有一个函数。该接口定义了执行网络请求的抽象,代码如下:

    /**
     * 执行网络请求的接口
     * 
     * @author mrsimple
     */
    public interface HttpStack {
        /**
         * 执行Http请求
         * 
         * @param request 待执行的请求
         * @return
         */
        public Response performRequest(Request<?> request);
    }
    

    今天就先到这里吧,关于HttpStack、NetworkExecutor、ResponseDelivery的介绍将在下一篇博客中更新,敬请期待。

    如果你看到这里都不给我投一篇,那简直太不够意思了!点击这里投我一票吧,谢谢~   

    Github地址

    SimpleNet网络框架地址

    展开全文
  • 教你写Android网络框架之Http请求的分发与执行

    千次阅读 多人点赞 2015-01-26 12:26:54
    前言 在《教你写Android网络框架》专栏的前两篇博客中,我们已经介绍了SimpleNet框架的基本结构,以及Request、Response、请求队列的实现,以及为什么要这么设计,这么设计的考虑是什么。前两篇博客中已经介绍了各个...


    前言

    《教你写Android网络框架》专栏的前两篇博客中,我们已经介绍了SimpleNet框架的基本结构,以及Request、Response、请求队列的实现,以及为什么要这么设计,这么设计的考虑是什么。前两篇博客中已经介绍了各个角色,今天我们就来剖析另外几个特别重要的角色,即NetworkExecutor、HttpStack以及ResponseDelivery,它们分别对应的功能是网络请求线程、Http执行器、Response分发,这三者是执行http请求和处理Response的核心。

    我们再来回顾一下,SimpleNet各个角色的分工合作。首先用户需要创建一个请求队列,然后将各个请求添加到请求队列中。多个NetworkExecutor ( 实质上是一个线程 )共享一个消息队列,在各个NetworkExecutor中循环的取请求队列中的请求,拿到一个请求,然后通过HttpStack来执行Http请求,请求完成后最终通过ResponseDelivery将Response结果分发到UI线程,保证请求回调执行在UI线程,这样用户就可以直接在回调中更新UI。执行流程如图1.


    图1 

    还有不太了解这幅架构图的可以参考专栏中的第一篇博客。

    NetworkExecutor

    作为SimpleNet中的“心脏”,NetworkExecutor起着非常重要的作用。之所以称之为“心脏”,是由于NetworkExecutor的功能是源源不断地从请求队列中获取请求,然后交给HttpStack来执行。它就像汽车中的发动机,人体中的心脏一样,带动着整个框架的运行。

    NetworkExecutor实质上是一个Thread,在run方法中我们会执行一个循环,不断地从请求队列中取得请求,然后交给HttpStack,由于比较简单我们直接上代码吧。

    /**
     * 网络请求Executor,继承自Thread,从网络请求队列中循环读取请求并且执行
     * 
     * @author mrsimple
     */
    final class NetworkExecutor extends Thread {
    
        /**
         * 网络请求队列
         */
        private BlockingQueue<Request<?>> mRequestQueue;
        /**
         * 网络请求栈
         */
        private HttpStack mHttpStack;
        /**
         * 结果分发器,将结果投递到主线程
         */
        private static ResponseDelivery mResponseDelivery = new ResponseDelivery();
        /**
         * 请求缓存
         */
        private static Cache<String, Response> mReqCache = new LruMemCache();
        /**
         * 是否停止
         */
        private boolean isStop = false;
    
        public NetworkExecutor(BlockingQueue<Request<?>> queue, HttpStack httpStack) {
            mRequestQueue = queue;
            mHttpStack = httpStack;
        }
    
        @Override
        public void run() {
            try {
                while (!isStop) {
                    final Request<?> request = mRequestQueue.take();
                    if (request.isCanceled()) {
                        Log.d("### ", "### 取消执行了");
                        continue;
                    }
                    Response response = null;
                    if (isUseCache(request)) {
                        // 从缓存中取
                        response = mReqCache.get(request.getUrl());
                    } else {
                        // 从网络上获取数据
                        response = mHttpStack.performRequest(request);
                        // 如果该请求需要缓存,那么请求成功则缓存到mResponseCache中
                        if (request.shouldCache() && isSuccess(response)) {
                            mReqCache.put(request.getUrl(), response);
                        }
                    }
    
                    // 分发请求结果
                    mResponseDelivery.deliveryResponse(request, response);
                }
            } catch (InterruptedException e) {
                Log.i("", "### 请求分发器退出");
            }
    
        }
    
        private boolean isSuccess(Response response) {
            return response != null && response.getStatusCode() == 200;
        }
    
        private boolean isUseCache(Request<?> request) {
            return request.shouldCache() && mReqCache.get(request.getUrl()) != null;
        }
    
        public void quit() {
            isStop = true;
            interrupt();
        }
    }
    在启动请求队列时,我们会启动指定数量的 NetworkExecutor ( 参考 教你写Android网络框架之Request、Response类与请求队列。在构造 NetworkExecutor时会将请求队列以及HttpStack注入进来,这样NetworkExecutor就具有了两大元素,即请求队列和HttpStack。然后在run函数的循环中不断地取出请求,并且交给HttpStack执行,其间还会判断该请求是否需要缓存、是否已经有缓存,如果使用缓存、并且已经含有缓存,那么则使用缓存的结果等。在run函数中执行http请求,这样就将网络请求执行在子线程中。执行Http需要HttpStack,但最终我们需要将结果分发到UI线程需要ResponseDelivery,下面我们挨个介绍。


    HttpStack

    HttpStack只是一个接口,只有一个performRequest函数,也就是执行请求。

    /**
     * 执行网络请求的接口
     * 
     * @author mrsimple
     */
    public interface HttpStack {
        /**
         * 执行Http请求
         * 
         * @param request 待执行的请求
         * @return
         */
        public Response performRequest(Request<?> request);
    }

    HttpStack是网络请求的真正执行者,有HttpClientStack和HttpUrlConnStack,两者分别为Apache的HttpClient和java的HttpURLConnection,关于这两者的区别请参考:Android访问网络,使用HttpURLConnection还是HttpClient? 默认情况下,我们会根据api版本来构建对应的HttpStack,当然用户也可以自己实现一个HttpStack,然后通过SimpleNet的工厂函数传递进来。

    例如 : 

        /**
         * @param coreNums 线程核心数
         * @param httpStack http执行器
         */
        protected RequestQueue(int coreNums, HttpStack httpStack) {
            mDispatcherNums = coreNums;
            mHttpStack = httpStack != null ? httpStack : HttpStackFactory.createHttpStack();
        }
    在购置请求队列时会传递HttpStack,如果httpStack为空,则由HttpStackFactory根据api版本生成对应的HttpStack。即api 9以下是HttpClientStack, api 9 及其以上则为HttpUrlConnStack。
    /**
     * 根据api版本选择HttpClient或者HttpURLConnection
     * 
     * @author mrsimple
     */
    public final class HttpStackFactory {
    
        private static final int GINGERBREAD_SDK_NUM = 9;
    
        /**
         * 根据SDK版本号来创建不同的Http执行器,即SDK 9之前使用HttpClient,之后则使用HttlUrlConnection,
         * 两者之间的差别请参考 :
         * http://android-developers.blogspot.com/2011/09/androids-http-clients.html
         * 
         * @return
         */
        public static HttpStack createHttpStack() {
            int runtimeSDKApi = Build.VERSION.SDK_INT;
            if (runtimeSDKApi >= GINGERBREAD_SDK_NUM) {
                return new HttpUrlConnStack();
            }
    
            return new HttpClientStack();
        }
    }

    HttpClientStack和HttpUrlConnStack分别就是封装了HttpClient和HttpURLConnection的http请求,构建请求、设置header、设置请求参数、解析Response等操作。针对于这一层,我们没有给出一个抽象类,原因是HttpClient和HttpURLConnection并不属于同一个类族,他们的行为虽然都很相似,但是其中涉及到的一些类型却是不同的。这里我们给出HttpUrlConnStack的示例,最近比较忙,因此写的配置比较简单,有需要的同学自己优化了。

    /**
     * 使用HttpURLConnection执行网络请求的HttpStack
     * 
     * @author mrsimple
     */
    public class HttpUrlConnStack implements HttpStack {
    
        /**
         * 配置Https
         */
        HttpUrlConnConfig mConfig = HttpUrlConnConfig.getConfig();
    
        @Override
        public Response performRequest(Request<?> request) {
            HttpURLConnection urlConnection = null;
            try {
                // 构建HttpURLConnection
                urlConnection = createUrlConnection(request.getUrl());
                // 设置headers
                setRequestHeaders(urlConnection, request);
                // 设置Body参数
                setRequestParams(urlConnection, request);
                // https 配置
                configHttps(request);
                return fetchResponse(urlConnection);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (urlConnection != null) {
                    urlConnection.disconnect();
                }
            }
            return null;
        }
    
        private HttpURLConnection createUrlConnection(String url) throws IOException {
            URL newURL = new URL(url);
            URLConnection urlConnection = newURL.openConnection();
            urlConnection.setConnectTimeout(mConfig.connTimeOut);
            urlConnection.setReadTimeout(mConfig.soTimeOut);
            urlConnection.setDoInput(true);
            urlConnection.setUseCaches(false);
            return (HttpURLConnection) urlConnection;
        }
    
        private void configHttps(Request<?> request) {
            if (request.isHttps()) {
                SSLSocketFactory sslFactory = mConfig.getSslSocketFactory();
                // 配置https
                if (sslFactory != null) {
                    HttpsURLConnection.setDefaultSSLSocketFactory(sslFactory);
                    HttpsURLConnection.setDefaultHostnameVerifier(mConfig.getHostnameVerifier());
                }
    
            }
        }
    
        private void setRequestHeaders(HttpURLConnection connection, Request<?> request) {
            Set<String> headersKeys = request.getHeaders().keySet();
            for (String headerName : headersKeys) {
                connection.addRequestProperty(headerName, request.getHeaders().get(headerName));
            }
        }
    
        protected void setRequestParams(HttpURLConnection connection, Request<?> request)
                throws ProtocolException, IOException {
            HttpMethod method = request.getHttpMethod();
            connection.setRequestMethod(method.toString());
            // add params
            byte[] body = request.getBody();
            if (body != null) {
                // enable output
                connection.setDoOutput(true);
                // set content type
                connection
                        .addRequestProperty(Request.HEADER_CONTENT_TYPE, request.getBodyContentType());
                // write params data to connection
                DataOutputStream dataOutputStream = new DataOutputStream(connection.getOutputStream());
                dataOutputStream.write(body);
                dataOutputStream.close();
            }
        }
    
        private Response fetchResponse(HttpURLConnection connection) throws IOException {
    
            // Initialize HttpResponse with data from the HttpURLConnection.
            ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
            int responseCode = connection.getResponseCode();
            if (responseCode == -1) {
                throw new IOException("Could not retrieve response code from HttpUrlConnection.");
            }
            // 状态行数据
            StatusLine responseStatus = new BasicStatusLine(protocolVersion,
                    connection.getResponseCode(), connection.getResponseMessage());
            // 构建response
            Response response = new Response(responseStatus);
            // 设置response数据
            response.setEntity(entityFromURLConnwction(connection));
            addHeadersToResponse(response, connection);
            return response;
        }
    
        /**
         * 执行HTTP请求之后获取到其数据流,即返回请求结果的流
         * 
         * @param connection
         * @return
         */
        private HttpEntity entityFromURLConnwction(HttpURLConnection connection) {
            BasicHttpEntity entity = new BasicHttpEntity();
            InputStream inputStream = null;
            try {
                inputStream = connection.getInputStream();
            } catch (IOException e) {
                e.printStackTrace();
                inputStream = connection.getErrorStream();
            }
    
            // TODO : GZIP 
            entity.setContent(inputStream);
            entity.setContentLength(connection.getContentLength());
            entity.setContentEncoding(connection.getContentEncoding());
            entity.setContentType(connection.getContentType());
    
            return entity;
        }
    
        private void addHeadersToResponse(BasicHttpResponse response, HttpURLConnection connection) {
            for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
                if (header.getKey() != null) {
                    Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
                    response.addHeader(h);
                }
            }
        }
    
    }
    代码很简单,就不多说了。

    ResponseDelivery

    在HttpStack的performRequest函数中,我们会返回一个Response对象,该对象包含了我们请求对应的Response。关于Response类你不太了解的可以参考教你写Android网络框架之Request、Response类与请求队列。我们在NetworkExecutor中执行http请求的最后一步会将结果分发给UI线程,主要工作其实就是将请求的回调执行到UI线程,以便用户可以更新UI等操作。

        @Override
        public void run() {
            try {
                while (!isStop) {
                    final Request<?> request = mRequestQueue.take();
                    if (request.isCanceled()) {
                        Log.d("### ", "### 取消执行了");
                        continue;
                    }
                    Response response = null;
                    if (isUseCache(request)) {
                        // 从缓存中取
                        response = mReqCache.get(request.getUrl());
                    } else {
                        // 从网络上获取数据
                        response = mHttpStack.performRequest(request);
                        // 如果该请求需要缓存,那么请求成功则缓存到mResponseCache中
                        if (request.shouldCache() && isSuccess(response)) {
                            mReqCache.put(request.getUrl(), response);
                        }
                    }
    
                    // 分发请求结果
                    mResponseDelivery.deliveryResponse(request, response);
                }
            } catch (InterruptedException e) {
                Log.i("", "### 请求分发器退出");
            }
    
        }
    不管是从缓存中获取还是从网络上获取,我们得到的都是一个Response对象,最后我们通过ResponseDelivery对象将结果分发给UI线程。

    ResponseDelivery其实就是封装了关联了UI线程消息队列的Handler,在deliveryResponse函数中将request的deliveryResponse执行在UI线程中。既然我们有了关联了UI线程的Handler对象,那么直接构建一个Runnable,在该Runnable中执行request的deliveryResponse函数即可。在Request类的deliveryResponse中,又会调用parseResponse解析Response结果,返回的结果类型就是Request<T>中的T,这个T是在Request子类中指定,例如JsonRequest,那么返回的Response的结果就是JSONObject。这样我们就得到了服务器返回的json数据,并且将这个json结果通过回调的形式传递给了UI线程。用户就可以在该回调中更新UI了。

    这其中主要就是抽象和泛型,写框架很多时候泛型是很重要的手段,因此熟悉使用抽象和泛型是面向对象开发的重要一步。

    ResponseDelivery代码如下 :

    /**
     * 请求结果投递类,将请求结果投递给UI线程
     * 
     * @author mrsimple
     */
    class ResponseDelivery implements Executor {
    
        /**
         * 主线程的hander
         */
        Handler mResponseHandler = new Handler(Looper.getMainLooper());
    
        /**
         * 处理请求结果,将其执行在UI线程
         * 
         * @param request
         * @param response
         */
        public void deliveryResponse(final Request<?> request, final Response response) {
            Runnable respRunnable = new Runnable() {
    
                @Override
                public void run() {
                    request.deliveryResponse(response);
                }
            };
    
            execute(respRunnable);
        }
    
        @Override
        public void execute(Runnable command) {
            mResponseHandler.post(command);
        }
    
    }

    Request类的deliveryResponse函数。

        /**
         * 处理Response,该方法运行在UI线程.
         * 
         * @param response
         */
        public final void deliveryResponse(Response response) {
            T result = parseResponse(response);
            if (mRequestListener != null) {
                int stCode = response != null ? response.getStatusCode() : -1;
                String msg = response != null ? response.getMessage() : "unkown error";
                mRequestListener.onComplete(stCode, result, msg);
            }
        }

    这样,整个请求过程就完成了。下面我们总结一下这个过程。

    不同用户的服务器返回的数据格式是不一致的,因此我们定义了Request<T>泛型基类,泛型T就是返回的数据格式类型。比如返回的数据格式为json,那对应的请求就是JsonRequest,泛型T为JSONObject,在JsonRequest中覆写parseResponse函数,将得到的Response中的原始数据转换成JSONObject。然后将请求放到队列中,NetworkExecutor将请求分发给HttpStack执行,执行完成之后得到Response对象,最终ResponseDelivery将结果通过请求回调投递到UI线程。

    Github链接

    https://github.com/bboyfeiyu/simple_net_framework



    展开全文
  • Retrofit网络框架的使用以及原理解析

    千次阅读 2016-09-21 11:46:08
    声明:本博客转载自网络,优秀...Retrofit是针对于Android/Java的、基于okHttp的、一种轻量级且安全的、并使用注解方式的网络请求框架。 2 . 我们为什么要使用Retrofit,它有哪些优势? 首先,Retrofit使用注解方式

    声明:本博客转载自网络,优秀博客值得多次转播。
    地址
    原文

    就目前来说Retrofit2使用的已相当的广泛,那么我们先来了解下两个问题:
    1 . 什么是Retrofit?
    Retrofit是针对于Android/Java的、基于okHttp的、一种轻量级且安全的、并使用注解方式的网络请求框架。
    2 . 我们为什么要使用Retrofit,它有哪些优势?
    首先,Retrofit使用注解方式,大大简化了我们的URL拼写形式,而且注解含义一目了然,简单易懂;
    其次,Retrofit使用简单,结构层次分明,每一步都能清晰的表达出之所以要使用的寓意;
    再者,Retrofit支持同步和异步执行,使得请求变得异常简单,只要调用enqueue/execute即可完成;
    最后,Retrofit更大自由度的支持我们自定义的业务逻辑,如自定义Converters。
    好,知道了Retrofit是什么,有了哪些优势,现在我们来学习下怎么使用。
    一 Retrofit2使用详解:
    在使用之前,你必须先导入必要的jar包,以androidStudio为例:
    添加依赖:

      compile 'com.squareup.retrofit2:retrofit:2.0.2'
        compile 'com.squareup.retrofit2:converter-gson:2.0.2'
    

    因为Retrofit2是依赖okHttp请求的,而且请查看它的META-INF->META-INF\maven\com.squareup.retrofit2\retrofit->pom.xml文件,

    <dependencies>
        <dependency>
          <groupId>com.squareup.okhttp3</groupId>
          <artifactId>okhttp</artifactId>
        </dependency>
        ...
    </dependencies>
    

    由此可见,它确实是依赖okHttp,okHttp有会依赖okio所以它会制动的把这两个包也导入进来。
    添加权限:
    既然要请求网络,在我们android手机上是必须要有访问网络的权限的,下面把权限添加进来

    <uses-permission android:name="android.permission.INTERNET"/>
    

    好了,下面开始介绍怎么使用Retrofit,既然它是使用注解的请求方式来完成请求URL的拼接,那么我们就按注解的不同来分别学习:
    首先,我们需要创建一个java接口,用于存放请求方法的:

    public interface GitHubService {
    }
    

    然后逐步在该方法中添加我们所需要的方法(按照请求方式):
    1 :Get : 是我们最常见的请求方法,它是用来获取数据请求的。
    ①:直接通过URL获取网络内容:

    public interface GitHubService {
        @GET("users/octocat/repos")
        Call<List<Repo>> listRepos();
    }
    

    在这里我们定义了一个listRepos()的方法,通过@GET注解标识为get请求,请求的URL为“users/octocat/repos”。
    然后看看Retrofit是怎么调用的,代码如下:

    Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://api.github.com/")
        .build();
    
    GitHubService service = retrofit.create(GitHubService.class);
    Call<List<Repo>> repos = service.listRepos();
    repos.enqueue(new Callback<List<Repo>>(){
        @Override
        public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response){
    
        }
    
        @Override
        public void onFailure(Call<List<Repo>> call, Throwable t){
    
        }
    });

    代码解释:首先获取Retrofit对象,然后通过动态代理获取到所定义的接口,通过调用接口里面的方法获取到Call类型返回值,最后进行网络请求操作(这里不详细说明Retrofit 实现原理,后面会对它进行源码解析),这里必须要说的是请求URL的拼接:在构建Retrofit对象时调用baseUrl所传入一个String类型的地址,这个地址在调用service.listRepos()时会把@GET(“users/octocat/repos”)的URL拼接在尾部。
    ok,这样就完成了,我们这次的请求,但是我们不能每次请求都要创建一个方法呀?这时我们就会想起动态的构建URL了
    ②:动态获取URL地址:@Path
    我们再上面的基础上进行修改,如下:

    public interface GitHubService {
      @GET("users/{user}/repos")
      Call<List<Repo>> listRepos(@Path("user") String user);
    

    这里在Get注解中包含{user},它所对应的是@Path注解中的“user”,它所标示的正是String user,而我们再使用Retrofit对象动态代理的获取到GitHubService,当调用listRepos时,我们就必须传入一个String类型的User,如:

    Call<List<Repo>> repos = service.listRepos("octocat");
    

    如上代码,其他的代码都是不变的,而我们只需要使用@Path注解就完全的实现了动态的URL地址了,是不是很方便呢,这还不算什么,通常情况下,我们去获取一些网络信息,因为信息量太大,我们会分类去获取,也就是携带一些必要的元素进行过滤,那我们该怎么实现呢?其实也很简单,因为Retrofit已经为我们封装好了注解,请看下面(官网实例):
    ③:动态指定条件获取信息:@Query
    我们只需要使用@Query注解即可完成我们的需求,在@Query(“sort”)中,short就好比是URL请求地址中的键,而它说对应的String sort中的sort则是它的值。

    @GET("group/{id}/users")
    Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);
    

    但是我们想,在网络请求中一般为了更精确的查找到我们所需要的数据,过滤更多不需要的无关的东西,我们往往需要携带多个请求参数,当然可以使用@Query注解,但是太麻烦,很长,容易遗漏和出错,那有没有更简单的方法呢,有,当然后,我们可以直接放到一个map键值对中:
    ④:动态指定条件组获取信息:@QueryMap

    @GET("group/{id}/users")
    Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);
    

    使用@QueryMap注解可以分别地从Map集合中获取到元素,然后进行逐个的拼接在一起。
    ok,到这里,我们使用@Get注解已经可以完成绝大部分的查询任务了,下面我们再来看看另一种常用的请求方式–post
    2 POST : 一种用于携带传输数据的请求方式
    稍微了解点Http的同学们,可能都会知道:相对于get请求方式把数据存放在uri地址栏中,post请求传输的数据时存放在请求体中,所以post才能做到对数据的大小无限制。而在Retrofit中,它又是怎么使用的呢?请看下面:
    ①:携带数据类型为对象时:@Body

    @POST("users/new")
    Call<User> createUser(@Body User user);
    

    当我们的请求数据为某对象时Retrofit是这么处理使用的:
    首先,Retrofit用@POST注解,标明这个是post的请求方式,里面是请求的url;
    其次,Retrofit仿照http直接提供了@Body注解,也就类似于直接把我们要传输的数据放在了body请求体中,这样应用可以更好的方便我们理解。
    来看下应用:

    Call<List<User>> repos = service.createUser(new User(1, "管满满", "28", "http://write.blog.csdn.net/postlist"));
    

    这样我们直接把一个新的User对象利用注解@Body存放在body请求体,并随着请求的执行传输过去了。
    但是有同学在这该有疑问了,Retrofit就只能传输的数据为对象吗?当然不是,下面请看
    ②:携带数据类型为表单键值对时:@Field

    @FormUrlEncoded
    @POST("user/edit")
    Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);
    

    当我们要携带的请求数据为表单时,通常会以键值对的方式呈现,那么Retrofit也为我们考虑了这种情况,它首先用到@FormUrlEncoded注解来标明这是一个表单请求,然后在我们的请求方法中使用@Field注解来标示所对应的String类型数据的键,从而组成一组键值对进行传递。
    那你是不是有该有疑问了,假如我是要上传一个文件呢?
    ③:单文件上传时:@Part

    @Multipart
    @PUT("user/photo")
    Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);
    

    此时在上传文件时,我们需要用@Multipart注解注明,它表示允许多个@Part,@Part则对应的一个RequestBody 对象,RequestBody 则是一个多类型的,当然也是包括文件的。下面看看使用

    File file = new File(Environment.getExternalStorageDirectory(), "ic_launcher.png");
    RequestBody photoRequestBody = RequestBody.create(MediaType.parse("image/png"), file);
    RequestBody descriptionRequestBody = RequestBody.create(null, "this is photo.");
    
    Call<User> call = service.updateUser(photoRequestBody, descriptionRequestBody);
    

    这里我们创建了两个RequestBody 对象,然后调用我们定义的updateUser方法,并把RequestBody传递进入,这样就实现了文件的上传。是不是很简单呢?
    相比单文件上传,Retrofit还进一步提供了多文件上传的方式:
    ④:多文件上传时:@PartMap

    @Multipart
    @PUT("user/photo")
    Call<User> updateUser(@PartMap Map<String, RequestBody> photos, @Part("description") RequestBody description);
    

    这里其实和单文件上传是差不多的,只是使用一个集合类型的Map封装了文件,并用@PartMap注解来标示起来。其他的都一样,这里就不多讲了。
    3 Header : 一种用于携带消息头的请求方式
    Http请求中,为了防止攻击或是过滤掉不安全的访问或是为添加特殊加密的访问等等以减轻服务器的压力和保证请求的安全,通常都会在消息头中携带一些特殊的消息头处理。Retrofit也为我们提供了该请求方式:

    @Headers("Cache-Control: max-age=640000")
    @GET("widget/list")
    Call<List<Widget>> widgetList();
    -----------------------------------------------------------
    @Headers({
        "Accept: application/vnd.github.v3.full+json",
        "User-Agent: Retrofit-Sample-App"
    })
    @GET("users/{username}")
    Call<User> getUser(@Path("username") String username);
    

    以上两种是静态的为Http请求添加消息头,只需要使用@Headers注解,以键值对的方式存放即可,如果需要添加多个消息头,则使用{}包含起来,如上所示。但要注意,即使有相同名字得消息头也不会被覆盖,并共同的存放在消息头中。
    当然有静态添加那相对的也就有动态的添加消息头了,方法如下:

    @GET("user")
    Call<User> getUser(@Header("Authorization") String authorization)
    

    使用@Header注解可以为一个请求动态的添加消息头,假如@Header对应的消息头为空的话,则会被忽略,否则会以它的.toString()方式输出。
    ok,到这里已基本讲解完Retrofit的使用,还有两个重要但简单的方法也必须在这里提一下:
    1 call.cancel();它可以终止正在进行的请求,程序只要一旦调用到它,不管请求是否在终止都会被停止掉。
    2 call.clone();当你想要多次请求一个接口的时候,直接用 clone 的方法来生产一个新的,否则将会报错,因为当你得到一个call实例,我们调用它的 execute 方法,但是这个方法只能调用一次。多次调用则发生异常。
    好了,关于Retrofit的使用我们就讲这么多,接下来我们从源码的角度简单的解析下它的实现原理。
    二 Retrofit2 从源码解析实现原理
    首先先看一下Retrofit2标准示例

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build();
    
    GitHubService service = retrofit.create(GitHubService.class);
    
    service.enqueue();
    service.execute();
    

    由上面我们基本可以看出,Retrofit是通过构造者模式创建出来的,那么我们就来看看Builder这个构造器的源码:

    public static final class Builder {
        ...
        Builder(Platform platform) {
          this.platform = platform;
          converterFactories.add(new BuiltInConverters());
        }
    
        public Builder() {
          this(Platform.get());
        }
    
        public Builder client(OkHttpClient client) {
          return callFactory(checkNotNull(client, "client == null"));
        }
    
        public Builder callFactory(okhttp3.Call.Factory factory) {
          this.callFactory = checkNotNull(factory, "factory == null");
          return this;
        }
        public Builder baseUrl(String baseUrl) {
          checkNotNull(baseUrl, "baseUrl == null");
          HttpUrl httpUrl = HttpUrl.parse(baseUrl);
          if (httpUrl == null) {
            throw new IllegalArgumentException("Illegal URL: " + baseUrl);
          }
          return baseUrl(httpUrl);
        }
    
        public Builder baseUrl(HttpUrl baseUrl) {
          checkNotNull(baseUrl, "baseUrl == null");
          List<String> pathSegments = baseUrl.pathSegments();
          if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
            throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
          }
          this.baseUrl = baseUrl;
          return this;
        }
    
        public Builder addConverterFactory(Converter.Factory factory) {
          converterFactories.add(checkNotNull(factory, "factory == null"));
          return this;
        }
    
        public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
          adapterFactories.add(checkNotNull(factory, "factory == null"));
          return this;
        }
    
        public Builder callbackExecutor(Executor executor) {
          this.callbackExecutor = checkNotNull(executor, "executor == null");
          return this;
        }
    
        public Builder validateEagerly(boolean validateEagerly) {
          this.validateEagerly = validateEagerly;
          return this;
        }
    
        public Retrofit build() {
          if (baseUrl == null) {
            throw new IllegalStateException("Base URL required.");
          }
    
          okhttp3.Call.Factory callFactory = this.callFactory;
          if (callFactory == null) {
            callFactory = new OkHttpClient();
          }
    
          Executor callbackExecutor = this.callbackExecutor;
          if (callbackExecutor == null) {
            callbackExecutor = platform.defaultCallbackExecutor();
          }
    
          List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
          adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
    
          List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
    
          return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
              callbackExecutor, validateEagerly);
        }
      }
    

    源码讲解:
    1:当我们使用new Retrofit.Builder()来创建时,在Builder构造器中,首先就获得当前的设备平台信息,并且把内置的转换器工厂(BuiltInConverters)加添到工厂集合中,它的主要作用就是当使用多种Converters的时候能够正确的引导并找到可以消耗该类型的转化器。
    2:从我们的基本示例中看到有调用到.baseUrl(BASE_URL)这个方法,实际上没当使用Retrofit时,该方法都是必须传入的,并且还不能为空,从源码中可以看出,当baseUrl方法传进的参数来看,如果为空的话将会抛出NullPointerException空指针异常。
    3:addConverterFactory该方法是传入一个转换器工厂,它主要是对数据转化用的,请网络请求获取的数据,将会在这里被转化成我们所需要的数据类型,比如通过Gson将json数据转化成对象类型。
    4 : 从源码中,我们看到还有一个client方法,这个是可选的,如果没有传入则就默认为OkHttpClient,在这里可以对OkHttpClient做一些操作,比如添加拦截器打印log等
    5:callbackExecutor该方法从名字上看可以得知应该是回调执行者,也就是Call对象从网络服务获取数据之后转换到UI主线程中。
    6:addCallAdapterFactory该方法主要是针对Call转换了,比如对Rxjava的支持,从返回的call对象转化为Observable对象。
    7:最后调用build()方法,通过new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
    callbackExecutor, validateEagerly);构造方法把所需要的对象传递到Retrofit对象中。
    ok,当我们通过Builder构造器构造出Retrofit对象时,然后通过Retrofit.create()方法是怎么把我们所定义的接口转化成接口实例的呢?来看下create()源码:

    public <T> T create(final Class<T> service) {
        Utils.validateServiceInterface(service);
        if (validateEagerly) {
          eagerlyValidateMethods(service);
        }
        return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
            new InvocationHandler() {
              private final Platform platform = Platform.get();
    
              @Override public Object invoke(Object proxy, Method method, Object... args)
                  throws Throwable {
                // If the method is a method from Object then defer to normal invocation.
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);
                }
                if (platform.isDefaultMethod(method)) {
                  return platform.invokeDefaultMethod(method, service, proxy, args);
                }
                ServiceMethod serviceMethod = loadServiceMethod(method);
                OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
                return serviceMethod.callAdapter.adapt(okHttpCall);
              }
            });
      }
    

    当看到Proxy时,是不是多少有点明悟了呢?没错就是动态代理,动态代理其实已经封装的很简单了,主要使用newProxyInstance()方法来返回一个类的代理实例,其中它内部需要传递一个类的加载器,类本身以及一个InvocationHandler处理器,主要的动作都是在InvocationHandler中进行的,它里面只有一个方法invoke()方法,每当我们调用代理类里面的方法时invoke()都会被执行,并且我们可以从该方法的参数中获取到所需要的一切信息,比如从method中获取到方法名,从args中获取到方法名中的参数信息等。
    而Retrofit在这里使用到动态代理也不会例外:
    首先,通过method把它转换成ServiceMethod ;
    然后,通过serviceMethod, args获取到okHttpCall 对象;
    最后,再把okHttpCall进一步封装并返回Call对象。
    下面来逐步详解。
    1:将method把它转换成ServiceMethod

    ServiceMethod serviceMethod = loadServiceMethod(method);
    
    ServiceMethod loadServiceMethod(Method method) {
        ServiceMethod result;
        synchronized (serviceMethodCache) {
          result = serviceMethodCache.get(method);
          if (result == null) {
            result = new ServiceMethod.Builder(this, method).build();
            serviceMethodCache.put(method, result);
          }
        }
        return result;
      }
    

    loadServiceMethod源码方法中非常的好理解,主要就是通过ServiceMethod.Builder()方法来构建ServiceMethod,并把它给缓存取来,以便下次可以直接回去ServiceMethod。那下面我们再来看看它是怎么构建ServiceMethod方法的:

    public Builder(Retrofit retrofit, Method method) {
          this.retrofit = retrofit;
          this.method = method;
          this.methodAnnotations = method.getAnnotations();
          this.parameterTypes = method.getGenericParameterTypes();
          this.parameterAnnotationsArray = method.getParameterAnnotations();
        }
    
    public ServiceMethod build() {
          callAdapter = createCallAdapter();
          responseType = callAdapter.responseType();
          if (responseType == Response.class || responseType == okhttp3.Response.class) {
            throw methodError("'"
                + Utils.getRawType(responseType).getName()
                + "' is not a valid response body type. Did you mean ResponseBody?");
          }
          responseConverter = createResponseConverter();
          ...
          return new ServiceMethod<>(this);
        }
    

    首先在Builder()中初始化一些参数,然后在build()中返回一个new ServiceMethod<>(this)对象。
    下面来详细的解释下build()方法,完全理解了该方法则便于理解下面的所有执行流程。
    ①:构建CallAdapter对象,该对象将会在第三步中起着至关重要的作用。
    现在我们先看看它是怎么构建CallAdapter对象的:createCallAdapter()方法源码如下:

    private CallAdapter<?> createCallAdapter() {
          Type returnType = method.getGenericReturnType();
          if (Utils.hasUnresolvableType(returnType)) {
            throw methodError(
                "Method return type must not include a type variable or wildcard: %s", returnType);
          }
          if (returnType == void.class) {
            throw methodError("Service methods cannot return void.");
          }
          Annotation[] annotations = method.getAnnotations();
          try {
            return retrofit.callAdapter(returnType, annotations);
          } catch (RuntimeException e) { // Wide exception range because factories are user code.
            throw methodError(e, "Unable to create call adapter for %s", returnType);
          }
        }
    

    在createCallAdapter方法中主要做的是事情就是获取到method的类型和注解,然后调用retrofit.callAdapter(returnType, annotations);方法:

    public CallAdapter<?> callAdapter(Type returnType, Annotation[] annotations) {
        return nextCallAdapter(null, returnType, annotations);
      }
    
    public CallAdapter<?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType,
          Annotation[] annotations) {
        checkNotNull(returnType, "returnType == null");
        checkNotNull(annotations, "annotations == null");
    
        int start = adapterFactories.indexOf(skipPast) + 1;
        for (int i = start, count = adapterFactories.size(); i < count; i++) {
          CallAdapter<?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
          if (adapter != null) {
            return adapter;
          }
        }
    
    }

    辗转到Retrofit中nextCallAdapter()中,在for 循环中分别从adapterFactories中来获取CallAdapter对象,但是adapterFactories中有哪些CallAdapter对象呢,这就需要返回到构建Retrofit对象中的Builder 构造器中查看了
    public static final class Builder {

    public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
    adapterFactories.add(checkNotNull(factory, “factory == null”));
    return this;
    }

    public Retrofit build() {
    List

    OkHttpCall(ServiceMethod<T> serviceMethod, Object[] args) {
        this.serviceMethod = serviceMethod;
        this.args = args;
      }
    

    3:把okHttpCall进一步封装并返回Call对象
    这一步也是一句话 return serviceMethod.callAdapter.adapt(okHttpCall);但是想理解清楚必须先把第一步理解透彻,通过第一步我们找得到serviceMethod.callAdapter就是ExecutorCallAdapterFactory对象,那么调用.adapt(okHttpCall)把okHttpCall怎么进行封装呢?看看源码:

    <R> T adapt(Call<R> call);
    

    一看,吓死宝宝了,就这么一句,这是嘛呀,但是经过第一步的分析,我们已知道serviceMethod.callAdapter就是ExecutorCallAdapterFactory,那么我们可以看看在ExecutorCallAdapterFactory类中有没有发现CallAdapter的另类应用呢,一看,果不其然在重写父类的get()方法中我们找到了答案:

     @Override
      public CallAdapter<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
        if (getRawType(returnType) != Call.class) {
          return null;
        }
        final Type responseType = Utils.getCallResponseType(returnType);
        return new CallAdapter<Call<?>>() {
          @Override public Type responseType() {
            return responseType;
          }
    
          @Override public <R> Call<R> adapt(Call<R> call) {
            return new ExecutorCallbackCall<>(callbackExecutor, call);
          }
        };
      }
    

    当看到return new CallAdapter中的adapt(Call call)我们就完全知其所以然了,至于ExecutorCallbackCall怎么应用的我们在发起网络请求的时候讲解。
    ok,当我们得到接口的代理实例之后,通过代理接口调用里面的方法,就会触发InvocationHandler对象中的invoke方法,从而完成上面的三个步骤并且返回一个Call对象,通过Call对象就可以去完成我们的请求了,Retrofit为我们提供两种请求方式,一种是同步,一种是异步。我们这里就以异步方式来讲解:
    service.enqueue(new Callback

    static final class ExecutorCallbackCall<T> implements Call<T> {
        final Executor callbackExecutor;
        final Call<T> delegate;
    
        ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
          this.callbackExecutor = callbackExecutor;
          this.delegate = delegate;
        }
    
        @Override public void enqueue(final Callback<T> callback) {
          if (callback == null) throw new NullPointerException("callback == null");
    
          delegate.enqueue(new Callback<T>() {
            @Override public void onResponse(Call<T> call, final Response<T> response) {
              callbackExecutor.execute(new Runnable() {
                @Override public void run() {
                  if (delegate.isCanceled()) {
                    callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
                  } else {
                    callback.onResponse(ExecutorCallbackCall.this, response);
                  }
                }
              });
            }
    
            @Override public void onFailure(Call<T> call, final Throwable t) {
              callbackExecutor.execute(new Runnable() {
                @Override public void run() {
                  callback.onFailure(ExecutorCallbackCall.this, t);
                }
              });
            }
          });
        }
    

    在ExecutorCallbackCall类中,封装了两个对象一个是callbackExecutor,它主要是把现在运行的线程切换到主线程中去,一个是delegate对象,这个对象就是真真正正的执行网络操作的对象,那么它的真身到底是什么呢?还记得我们在获取代理接口第三步执行的serviceMethod.callAdapter.adapt(okHttpCall)的分析吧,经过辗转几步终于把okHttpCall传递到了new ExecutorCallbackCall<>(callbackExecutor, call);中,然后看看ExecutorCallbackCall的构造方法:

    ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
          this.callbackExecutor = callbackExecutor;
          this.delegate = delegate;
        }
    

    由此可以明白delegate 就是okHttpCall对象,那么我们在看看okHttpCall是怎么执行异步网络请求的:

    @Override 
    public void enqueue(final Callback<T> callback) {
        if (callback == null) throw new NullPointerException("callback == null");
    
        okhttp3.Call call;
        Throwable failure;
    
        synchronized (this) {
          if (executed) throw new IllegalStateException("Already executed.");
          executed = true;
    
          call = rawCall;
          failure = creationFailure;
          if (call == null && failure == null) {
            try {
              call = rawCall = createRawCall();
            } catch (Throwable t) {
              failure = creationFailure = t;
            }
          }
        }
    
        if (failure != null) {
          callback.onFailure(this, failure);
          return;
        }
    
        if (canceled) {
          call.cancel();
        }
    
        call.enqueue(new okhttp3.Callback() {
          @Override 
          public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
              throws IOException {
            Response<T> response;
            try {
              response = parseResponse(rawResponse);
            } catch (Throwable e) {
              callFailure(e);
              return;
            }
            callSuccess(response);
          }
    
          @Override 
          public void onFailure(okhttp3.Call call, IOException e) {
            try {
              callback.onFailure(OkHttpCall.this, e);
            } catch (Throwable t) {
              t.printStackTrace();
            }
          }
    
          private void callFailure(Throwable e) {
            try {
              callback.onFailure(OkHttpCall.this, e);
            } catch (Throwable t) {
              t.printStackTrace();
            }
          }
    
          private void callSuccess(Response<T> response) {
            try {
              callback.onResponse(OkHttpCall.this, response);
            } catch (Throwable t) {
              t.printStackTrace();
            }
          }
        });
      }
    

    从上面代码中,我们很容易就看出,其实它就是这里面封装了一个okhttp3.Call,直接利用okhttp进行网络的异步操作,至于okhttp是怎么进行网络请求的我们就不再这里讲解了,感兴趣的朋友可以自己去查看源码。
    好了,到这里整个Retrofit的实现原理基本已解析完毕,相信大家学习过都能够很好的掌握了。ok,今天就讲到这里吧,本来还打算再写个实例放上来的,看看篇幅也就放弃了,实战部分会在下一篇和Rxjava一起放出来,Rxjava我自己学习的都心花怒放了。看完记住关注微信平台。

    展开全文
  • 如何书写网络请求框架(先知)

    千次阅读 2016-07-14 09:06:06
    在 教你写Android网络框架之基本架构 一文中我们已经介绍了SimpleNet网络框架的基本结构,今天我们就开始从代码的角度来开始切入该网络框架的实现,在剖析的同时我们会分析设计思路,以及为什么要这样做,这样做的...

    前言

    在 教你写Android网络框架之基本架构 一文中我们已经介绍了SimpleNet网络框架的基本结构,今天我们就开始从代码的角度来开始切入该网络框架的实现,在剖析的同时我们会分析设计思路,以及为什么要这样做,这样做的好处是什么。这样我们不仅学到了如何实现网络框架,也会学到设计一个通用的框架应该有哪些考虑,这就扩展到框架设计的范畴,通过这个简单的实例希望能给新人一些帮助。当然这只是一家之言,大家都可以有自己的实现思路。

    正如你所看到的,这系列博客是为新人准备的,如果你是高手,请忽略。

    在框架开发当中,很重要的一点就是抽象。也就是面向对象中重要的一条原则: 依赖倒置原则,简单来说就是要依赖抽象,而不依赖具体。这样就使得我们的框架具有可扩展性,同时也满足了开闭原则,即对扩展开放,对修改关闭。针对于我们的网络框架来说,最重要的抽象就是Reqeust类、Response类,因此今天我们就从两个类开始切入。最后我们再引入网络框架中的请求队列(RequestQueue),这是SimpleNet中的中枢神经,所有的请求都需要放到该队列,然后等待着被执行。请求队列就像工厂中的流水线一样,而网络请求就像流水线上的待加工的产品。执行网络请求的对象就类似工厂中的工人,在自己的岗位上等待流水线上传递过来的产品,然后对其加工,加工完就将产品放到其他的位置。它们角色对应关系参考图1,如对SimpleNet的一些角色不太清楚可参考 教你写Android网络框架之基本架构 一文。

    图1

    Request类

    既然网络框架,那么我们先从网络请求类开始。前文已经说过,既然是框架,那么就需要可扩展性。因此注定了Request是抽象,而不是具体。而对于网络请求来说,用户得到的请求结果格式是不确定,比如有的服务器返回的是json,有的返回的是xml,有的直接是字符串。但是对于Http Response来说,它的返回数据类型都是Stream,也就是我们得到的原始数据都是二进制的流。所以在Request基类中我们必须预留方法来解析Response返回的具体类型,虽然返回的类型不同,但是他们的处理逻辑是一样的,因此我们可把Request作为泛型类,它的泛型类型就是它的返回数据类型,比如Request ,那么它的返回数据类型就是String类型的。另外还有请求的优先级、可取消等,我们这里先给出核心代码,然后再继续分析。

    /** 
     * 网络请求类. 注意GET和DELETE不能传递请求参数,因为其请求的性质所致,用户可以将参数构建到url后传递进来到Request中. 
     *  
     * @author mrsimple 
     * @param <T> T为请求返回的数据类型 
     */  
    public abstract class Request<T> implements Comparable<Request<T>> {  
    
        /** 
         * http请求方法枚举,这里我们只有GET, POST, PUT, DELETE四种 
         * @author mrsimple 
         */  
        public static enum HttpMethod {  
            GET("GET"),  
            POST("POST"),  
            PUT("PUT"),  
            DELETE("DELETE");  
    
            /** http request type */  
            private String mHttpMethod = "";  
    
            private HttpMethod(String method) {  
                mHttpMethod = method;  
            }  
    
            @Override  
            public String toString() {  
                return mHttpMethod;  
            }  
        }  
    
        /** 
         * 优先级枚举 
         * @author mrsimple 
         */  
        public static enum Priority {  
            LOW,  
            NORMAL,  
            HIGN,  
            IMMEDIATE  
        }  
    
        /** 
         * Default encoding for POST or PUT parameters. See 
         * {@link #getParamsEncoding()}. 
         */  
        private static final String DEFAULT_PARAMS_ENCODING = "UTF-8";  
        /** 
         * 请求序列号 
         */  
        protected int mSerialNum = 0;  
        /** 
         * 优先级默认设置为Normal 
         */  
        protected Priority mPriority = Priority.NORMAL;  
        /** 
         * 是否取消该请求 
         */  
        protected boolean isCancel = false;  
    
        /** 该请求是否应该缓存 */  
        private boolean mShouldCache = true;  
        /** 
         * 请求Listener 
         */  
        protected RequestListener<T> mRequestListener;  
        /** 
         * 请求的url 
         */  
        private String mUrl = "";  
        /** 
         * 请求的方法 
         */  
        HttpMethod mHttpMethod = HttpMethod.GET;  
    
        /** 
         * 请求的header 
         */  
        private Map<String, String> mHeaders = new HashMap<String, String>();  
        /** 
         * 请求参数 
         */  
        private Map<String, String> mBodyParams = new HashMap<String, String>();  
    
        public Request(HttpMethod method, String url, RequestListener<T> listener) {  
            mHttpMethod = method;  
            mUrl = url;  
            mRequestListener = listener;  
        }  
    
        /** 
         * 从原生的网络请求中解析结果,子类覆写 
         *  
         * @param response 
         * @return 
         */  
        public abstract T parseResponse(Response response);  
    
        /** 
         * 处理Response,该方法运行在UI线程. 
         *  
         * @param response 
         */  
        public final void deliveryResponse(Response response) {  
            // 解析得到请求结果  
            T result = parseResponse(response);  
            if (mRequestListener != null) {  
                int stCode = response != null ? response.getStatusCode() : -1;  
                String msg = response != null ? response.getMessage() : "unkown error";  
                mRequestListener.onComplete(stCode, result, msg);  
            }  
        }  
    
        public int getSerialNumber() {  
            return mSerialNum;  
        }  
    
        protected String getParamsEncoding() {  
            return DEFAULT_PARAMS_ENCODING;  
        }  
    
        public String getBodyContentType() {  
            return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();  
        }  
    
        public HttpMethod getHttpMethod() {  
            return mHttpMethod;  
        }  
    
        public Map<String, String> getHeaders() {  
            return mHeaders;  
        }  
    
        public Map<String, String> getParams() {  
            return mBodyParams;  
        }  
    
        /** 
         * 返回POST或者PUT请求时的Body参数字节数组 
         */  
        public byte[] getBody() {  
            Map<String, String> params = getParams();  
            if (params != null && params.size() > 0) {  
                return encodeParameters(params, getParamsEncoding());  
            }  
            return null;  
        }  
    
        /** 
         * 将参数转换为Url编码的参数串 
         */  
        private byte[] encodeParameters(Map<String, String> params, String paramsEncoding) {  
            StringBuilder encodedParams = new StringBuilder();  
            try {  
                for (Map.Entry<String, String> entry : params.entrySet()) {  
                    encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding));  
                    encodedParams.append('=');  
                    encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding));  
                    encodedParams.append('&');  
                }  
                return encodedParams.toString().getBytes(paramsEncoding);  
            } catch (UnsupportedEncodingException uee) {  
                throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee);  
            }  
        }  
    
        // 用于对请求的排序处理,根据优先级和加入到队列的序号进行排序  
        @Override  
        public int compareTo(Request<T> another) {  
            Priority myPriority = this.getPriority();  
            Priority anotherPriority = another.getPriority();  
            // 如果优先级相等,那么按照添加到队列的序列号顺序来执行  
            return myPriority.equals(another) ? this.getSerialNumber() - another.getSerialNumber()  
                    : myPriority.ordinal() - anotherPriority.ordinal();  
        }  
    
        /** 
         * 网络请求Listener,会被执行在UI线程 
         * @author mrsimple 
         * @param <T> 请求的response类型 
         */  
        public static interface RequestListener<T> {  
            /** 
             * 请求完成的回调 
             * @param response 
             */  
            public void onComplete(int stCode, T response, String errMsg);  
        }  
    }

    上述代码Request 为抽象类,T则为该请求Response的数据格式。这个T是请求类中的一个比较重要的点,不同的人有不同的需求,即请求Reponse的数据格式并不是都是一样的,我们必须考虑到请求返回类型的多样性,用泛型T来表示返回的数据格式类型,然后Request子类覆写对应的方法实现解析Response的数据格式,最后调用请求Listener将请求结果执行在UI线程,这样整个请求就完成了。

    每个Request都有一个序列号,该序列号由请求队列生成,标识该请求在队列中的序号,该序号和请求优先级决定了该请求在队列中的排序,即它在请求队列的执行顺序。每个请求有请求方式,例如”POST”、”GET”,这里我们用枚举来代替,具名类型比单纯的字符串更易于使用。每个Request都可以添加Header、Body参数 ( 关于请求参数的格式可以参考 四种常见的 POST 提交数据方式),并且可以取消。抽象类封装了通用的代码,只有可变的部分是抽象函数,这里只有parseResponse这个函数。例如,我们返回的数据格式是Json,那么我们构建一个子类叫做JsonRequest,示例代码如下。

    /** 
     * 返回的数据类型为Json的请求, Json对应的对象类型为JSONObject 
     *  
     * @author mrsimple 
     */  
    public class JsonRequest extends Request<JSONObject> {  
    
        public JsonRequest(HttpMethod method, String url, RequestListener<JSONObject> listener) {  
            super(method, url, listener);  
        }  
    
    
        /** 
         * 将Response的结果转换为JSONObject 
         */  
        @Override  
        public JSONObject parseResponse(Response response) {  
            String jsonString = new String(response.getRawData());  
            try {  
                return new JSONObject(jsonString);  
            } catch (JSONException e) {  
                e.printStackTrace();  
            }  
            return null;  
        }  
    }

    可以看到,实现一个请求类还是非常简单的,只需要覆写parseResponse函数来解析你的请求返回的数据即可。这样就保证了可扩展性,比如后面如果我想使用这个框架来做一个ImageLoader,那么我可以创建一个ImageRequest,该请求返回的类型就是Bitmap,那么我们只需要覆写parseResponse函数,然后把结果转换成Bitmap即可。

    这里引入了Response类,这个Response类存储了请求的状态码、请求结果等内容,我们继续往下看。

    Response类

    每个请求都对应一个Response,但这里的问题是这个Response的数据格式我们是不知道的。我们写的是框架,不是应用。框架只是构建一个基本环境,并且附带一些比较常用的类,比如这里的JsonRequest。但是重要的一点是可以让用户自由、简单的扩展以实现他的需求。对于Response类来说,我们最重要的一点就是要确定请求结果的数据格式类型。我们都知道,HTTP实际上是基于TCP协议,而TCP协议又是基于Socket,Socket实际上操作的也就是输入、输出流,输出流是向服务器写数据,输入流自然是从服务器读取数据。因此我们在Response类中应该使用InputStream存储结果或者使用更为易于使用的字节数组,这里我们使用字节数组来存储。我们来看Response类。

    /** 
     * 请求结果类,继承自BasicHttpResponse,将结果存储在rawData中. 
     * @author mrsimple 
     */  
    public class Response extends BasicHttpResponse {  
    
        public byte[] rawData = new byte[0];  
    
        public Response(StatusLine statusLine) {  
            super(statusLine);  
        }  
    
        public Response(ProtocolVersion ver, int code, String reason) {  
            super(ver, code, reason);  
        }  
    
        @Override  
        public void setEntity(HttpEntity entity) {  
            super.setEntity(entity);  
            rawData = entityToBytes(getEntity());  
        }  
    
        public byte[] getRawData() {  
            return rawData;  
        }  
    
        public int getStatusCode() {  
            return getStatusLine().getStatusCode();  
        }  
    
        public String getMessage() {  
            return getStatusLine().getReasonPhrase();  
        }  
    
        /** Reads the contents of HttpEntity into a byte[]. */  
        private byte[] entityToBytes(HttpEntity entity) {  
            try {  
                return EntityUtils.toByteArray(entity);  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
            return new byte[0];  
        }  
    
    }

    这个类很简单,只是继承了BasicHttpResponse,然后将输入流转换成字节数组,然后包装了几个常用的方法,主要是为了使用简单吧。我们将结果存储为字节数组,这样可以用户可以很方便的将结果转换为String、bitmap等数据类型,如果直接存储的是InputStream,那么在很多时候用户需要在外围将InputStream先转换为字节数组,然后再转换为最终的格式,例如InputStream转为String类型。这也是为什么我们这里选用byte[]而不用InputStream的原因。

    请求队列

    网络请求队列也比较简单,实际上就是内部封装了一个优先级队列,在构建队列时会启动几个NetworkExecutor ( 子线程 )来从请求队列中获取请求,并且执行请求。请求队列会根据请求的优先级进行排序,这样就保证了一些优先级高的请求得到尽快的处理,这也就是为什么Request类中实现了Comparable接口的原因。如果优先级一致的情况下,则会根据请求加入到队列的顺序来排序,这个序号由请求队列生成,这样就保证了优先级一样的情况下按照FIFO的策略执行。
    /** 
     * 请求队列, 使用优先队列,使得请求可以按照优先级进行处理.
     *  
     * @author mrsimple 
     */  
    public final class RequestQueue {  
        /** 
         * 请求队列 [ Thread-safe ] 
         */  
        private BlockingQueue<Request<?>> mRequestQueue = new PriorityBlockingQueue<Request<?>>();  
        /** 
         * 请求的序列化生成器 
         */  
        private AtomicInteger mSerialNumGenerator = new AtomicInteger(0);  
    
        /** 
         * 默认的核心数 
         */  
        public static int DEFAULT_CORE_NUMS = Runtime.getRuntime().availableProcessors() + 1;  
        /** 
         * CPU核心数 + 1个分发线程数 
         */  
        private int mDispatcherNums = DEFAULT_CORE_NUMS;  
        /** 
         * NetworkExecutor,执行网络请求的线程 
         */  
        private NetworkExecutor[] mDispatchers = null;  
        /** 
         * Http请求的真正执行者 
         */  
        private HttpStack mHttpStack;  
    
        /** 
         * @param coreNums 线程核心数 
         */  
        protected RequestQueue(int coreNums, HttpStack httpStack) {  
            mDispatcherNums = coreNums;  
            mHttpStack = httpStack != null ? httpStack : HttpStackFactory.createHttpStack();  
        }  
    
        /** 
         * 启动NetworkExecutor 
         */  
        private final void startNetworkExecutors() {  
            mDispatchers = new NetworkExecutor[mDispatcherNums];  
            for (int i = 0; i < mDispatcherNums; i++) {  
                mDispatchers[i] = new NetworkExecutor(mRequestQueue, mHttpStack);  
                mDispatchers[i].start();  
            }  
        }  
    
        public void start() {  
            stop();  
            startNetworkExecutors();  
        }  
    
        /** 
         * 停止NetworkExecutor 
         */  
        public void stop() {  
            if (mDispatchers != null && mDispatchers.length > 0) {  
                for (int i = 0; i < mDispatchers.length; i++) {  
                    mDispatchers[i].quit();  
                }  
            }  
        }  
    
        /** 
         * 不能重复添加请求 
         *  
         * @param request 
         */  
        public void addRequest(Request<?> request) {  
            if (!mRequestQueue.contains(request)) {  
                request.setSerialNumber(this.generateSerialNumber());  
                mRequestQueue.add(request);  
            } else {  
                Log.d("", "### 请求队列中已经含有");  
            }  
        }  
    
        public void clear() {  
            mRequestQueue.clear();  
        }  
    
        public BlockingQueue<Request<?>> getAllRequests() {  
            return mRequestQueue;  
        }  
    
        /** 
         * 为每个请求生成一个系列号 
         *  
         * @return 序列号 
         */  
        private int generateSerialNumber() {  
            return mSerialNumGenerator.incrementAndGet();  
        }  
    }

    这里引入了一个HttpStack,这是一个接口,只有一个函数。该接口定义了执行网络请求的抽象,代码如下:

    /** 
     * 执行网络请求的接口 
     *  
     * @author mrsimple 
     */  
    public interface HttpStack {  
        /** 
         * 执行Http请求 
         *  
         * @param request 待执行的请求 
         * @return 
         */  
        public Response performRequest(Request<?> request);  
    }

    展开全文
  • 介绍配置大于编码 ...冠以 Retrofit 这个名字的这个家伙,应该是某某某的 『Plus』 版本了。...注意这里并没有说它是网络请求框架,主要原因在于网络请求的工作并不是 Retrofit 来完成的。Retrofit 2.0 开始
  • 数字化工厂正在闪现迷人的色彩,制造业正在被其光芒所吸引... 数字化工厂的定义虽然国内外对数字化工厂的研究越来越多,但是对于数字化工厂的定义确没有统一的定论。就目前而言,存在两种数字化工厂的定义,一种是广义
  • 网络请求框架封装(NetWorkUtils)

    千次阅读 2017-05-09 13:48:24
    效果图:项目描述:网络请求框架二次封装,目前完成了基于OkHttp的get/post/postJson/uploadFile/downloadFile等功能开发,支持扩展,底层实现可自由切换; 扩展请继承ExecutorFactory实现具体的IExecutor即可,底层...
  • 网络开源框架OkHttp和Retrofit总结分享

    万次阅读 2016-11-16 13:52:50
    网络开源框架OkHttp和Retrofit总结分享   一、OkHttp框架 ⒈ OkHttp框架简介 Android为我们提供了两种HTTP交互的方式:HttpURLConnection和 Apache HTTP Client,虽然两者都支持H
  • Twisted Matrix 是一种受欢迎的纯 Python 框架,用来进行网络服务和应用程序的编程。能够编写高性能的异步服务器程序和其他网络应用程序。
  • Retrofit是针对于Android/Java的、基于okHttp的、一种轻量级且安全的、并使用注解方式的网络请求框架。2 . 我们为什么要使用Retrofit,它有哪些优势?首先,Retrofit使用注解方式,大大简化了我们的URL拼写形式,...
  • 一个好的网络框架需要有那些特点呢? 请求 当然这个请求不单单是发送请求这么简单,它包括请求相关的一系列配置是否简易、发送模式是否灵活切换、请求头信息是否易处理、请求参数是否易操作等等 响应 一个好的网络...
  • 《android网络请求框架》一个轻量级的异步网络请求框架MyHttpUtils(新版更新记录,版本号:2.X)一、前言基本的用法在《Android网络请求篇》MyHttpUtils一个非常好用的异步网络请求框架中有过详细的介绍。...
  • android http——网络请求二次封装的框架设计

    万次阅读 热门讨论 2016-05-17 18:51:08
    android app中少不了要用到网络请求。网上已经有很多优秀的网络请求框架,谷歌推荐的...但是我要说的并不是重新设计网络请求框架,而是基于所有优秀网络请求框架进行的二次封装,成为一个为自己项目所用的网络请求。
  • 文章目录一.Android实现网络请求的主流方法1.1 HttpClient(1)简介(2)实现1.2 HttpURLConnection(1)简介(2)实现1.3 对比二.主流网络请求库2.1 简介2.2 对比(Android-Async-Http、Volley、OkHttp、Retrofit)...
  • 网络框架分析-全是套路

    千次阅读 2016-12-15 18:57:34
    一般网络框架是不需要处理内存缓存的,但是图片加载框架需要。 在Android3.1以后,Android推出了 LruCache 这个内存缓存类,LruCache中的对象是强引用的。Picasso的内存缓存就是使用的LruCache实现的。对于磁盘缓存...
  • 简单工厂和抽象工厂有什么区别?

    万次阅读 2019-07-09 09:40:09
    可以生产结构中的任意产品,不能增加新的产品 抽象工厂模式 提供一个创建一系列相关或相互依赖对象的接口,而无需制定他们具体的类,生产多个系列产品 生产不同产品族的全部产品,不能新增产品,可以新增产品族 ...
  • 是一个网络应用框架,在不牺牲性能和可扩展性的前提下用于解决上面的所有问题。 1.3   几个接口 IoAcceptor 执行所有底层 IO ,将他们翻译成抽象的 IO 事件,并把翻译过的事件和关联的 IoSession 发送给 ...
  • 之前公司的项目用到了MVP+Retrofit+RxJava的框架进行网络请求,所以今天特此写一篇文章以做总结。相信很多人都听说过MVP、Retrofit、以及RxJava,有的人已经开始用了,有的人可能还不知道这是什么,以及到底怎么用。...
  • 无论是网络、缓存、特效,占位图,Glide团队都为开发者考虑的非常细致,也正因为这个原因,Glide框架的源代码变得极其复杂,生涩难懂,我观察到网上有写源码分析的某大神光写主体结构的代码分析都写了两个礼拜,可见...
  • 以物联网框架结构为基础,安装感知层、网络层和应用层的硬件设备,并在此基础上建立智能工厂供应链共享平台数据库。最终通过对各个智能工厂供应链环节的信息传递,实现平台的信息共享功能。通过测试实验发现,与传统...
  • 很悲催,我们在《Android也架构之二:单例模式访问网络》 用httpConnect的方式去访问网络,而且能够正常获取数据了,可是老板能,技术出生,他要求我重新用httpClient去获取获取网络数据,童鞋们,是不是头快爆炸了...
  • 之前公司的项目用到了MVP+Retrofit+RxJava的框架进行网络请求,所以今天特此写一篇文章以做总结。相信很多人都听说过MVP、Retrofit、以及RxJava,有的人已经开始用了,有的人可能还不知道这是什么,以及到底怎么用。...
  • (三)简单工厂模式详解

    千次阅读 多人点赞 2013-06-08 23:55:43
    作者:zuoxiaolong8810(左潇龙),转载请注明... 本章我们讨论简单工厂模式,LZ当初不小心夸下海口说不和网络上传播的教学式模式讲解雷同,所以现在感觉写一篇博文压力颇大。  如何来介绍简单工厂呢,LZ着实费了不
  • 来源:机器人创新生态概要: 针对我国网络协同制造和智能工厂发展模式创新不足、技术能力尚未形成、融合新生态发展不足、核心技术/软件支撑能力薄弱等问题。征求意见时间为2018年2月8日至2018年2月22日。电子邮箱:...
  • 工厂方法模式 工厂方法模式(Factory Method Pattern):定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。 工厂方法模式又简称为工厂模式(Factory Pattern),...
  • 万能游戏框架

    千次阅读 2016-04-16 21:37:08
     代码组织架构, 资源,网络, 3D数学 ,热更新  拖拽式和 查找式  单例式  常用设计模式讲解:  1,工厂模式  2,策略模式  3,观察者模式  4,单例模式  5,代理模式  6,多例模式
  • 软件框架

    千次阅读 2011-12-13 19:18:28
    软件框架 -转 ( 本文源自《.NET通信框架的设计、实现与应用》书稿第一章内容,未经许可,不得转载。)  转自zhuweisky  框架和类库等概念的出现都是源于人们对复用的渴望。“不要重复发明轮子”,成了...
  • SSM框架

    千次阅读 2020-04-08 11:54:44
    “控制反转(IoC)”也称为“依赖注入(DI)”,是一个定义对象依赖的过程,对象只和构造参数,工厂方法参数,对象实例属性或工厂方法返回相关。容器在创建这些 bean 的时候注入这些依赖。这个过程是一个反向的过程...
  • 工厂供电系统的安全运行对工业企业来说至关重要,特别是对于化工企业,如何快速判断和切除故障非常关键。介绍了利用专家系统技术开发...该系统是用VC++开发成功的,并对用 VC++实现知识的框架结构表达做了详细的说明。
  • ssm框架

    千次阅读 2019-03-27 14:46:11
    基于ssm框架 随着互联网技术的迅速发展,Internet的普及,以及IT技术的迅猛发展,我们的社会已经进入了一个信息化时代。随着信息化时代的到来,商城以及商城的服务随之产生。商城以及商城的服务指利用简单、快捷、低...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 36,899
精华内容 14,759
关键字:

工厂网络框架结构