zoukankan      html  css  js  c++  java
  • 谷歌Volley网络框架讲解——HttpStack及其实现类

    前两篇已经对网络请求流程已经梳理了个大概,这次我们着重看一下HttpStack和它的其实现类。我们之前在Network篇讲过它仅有一个实现类,而今天我们讲的HttpStack有两个实现类。

    其中HttpCliantStack是在2.3以下使用,Hurl是在2.3以上使用,这样分开的原因谷歌给了注释。

      // Prior to Gingerbread, HttpUrlConnection was unreliable.
      // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html

    2.3以下HttpUrlConnection 是不能用的,而2.3以上就是采用HttpUrlConnection 进行连接的,以下就是直接用的HttpClient。

    HttpStack

    先来看一下HttpStack接口

    public interface HttpStack {
        /**
         * Performs an HTTP request with the given parameters.
         *通过给定的参数执行一个http请求
         * <p>A GET request is sent if request.getPostBody() == null. A POST request is sent otherwise,
         * and the Content-Type header is set to request.getPostBodyContentType().</p>
         * 
         * @param request the request to perform//要执行的请求
         * @param additionalHeaders additional headers to be sent together with
         *         {@link Request#getHeaders()}
         * @return the HTTP response//执行一个请求返回一个结果
         */
        public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
            throws IOException, AuthFailureError;
    
    }

    HttpCliantStack

    这里区分Get和Post请求,我们先看下HttpCliantStack,注释已经写的非常清楚了,如果注释有误望大家指出。

    /**
     * An HttpStack that performs request over an {@link HttpClient}.
     * HttpStack:通过HttpClient执行请求
     * {@link Volley#newRequestQueue(android.content.Context, HttpStack)}
     */
    public class HttpClientStack implements HttpStack {
        
        protected final HttpClient mClient;//默认HttpClient
    
        /** The Constant HEADER_CONTENT_TYPE. */
        private final static String HEADER_CONTENT_TYPE = "Content-Type";
    
        
        /**
         * Instantiates a new http client stack.
         * Volley中HttpClient可是AndroidHttpClient.newInstance(userAgent)产生的
         * @param client the client
         */
        public HttpClientStack(HttpClient client) {
            mClient = client;
        }
    
        /**
         * Adds the headers.
         *
         * @param httpRequest the http request
         * @param headers the headers
         */
        private static void addHeaders(HttpUriRequest httpRequest, Map<String, String> headers) {
            for (String key : headers.keySet()) {
                httpRequest.setHeader(key, headers.get(key));
            }
        }
    
        /**
         * Gets the post parameter pairs.
         *
         * @param postParams the post params
         * @return the post parameter pairs
         */
        @SuppressWarnings("unused")
        private static List<NameValuePair> getPostParameterPairs(Map<String, String> postParams) {
            List<NameValuePair> result = new ArrayList<NameValuePair>(postParams.size());
            for (String key : postParams.keySet()) {
                result.add(new BasicNameValuePair(key, postParams.get(key)));
            }
            return result;
        }
    
        
        /* (non-Javadoc)
         * @see com.android.volley.toolbox.HttpStack#performRequest(com.android.volley.Request, java.util.Map)
         */
            
        @Override//中心方法
        public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
                throws IOException, AuthFailureError {
            HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);//设置请求方法
            addHeaders(httpRequest, additionalHeaders);//添加自定义头部
            addHeaders(httpRequest, request.getHeaders());//添加请求自带头部
            onPrepareRequest(httpRequest);//相当于onStart,子类扩展
            HttpParams httpParams = httpRequest.getParams();//获取配置类
            int timeoutMs = request.getTimeoutMs();
            // TODO: Reevaluate this connection timeout based on more wide-scale
            // data collection and possibly different for wifi vs. 3G.
            /** 如果有更大规模的数据在Wifi和3G网络下重新评估连接超时*/
            HttpConnectionParams.setConnectionTimeout(httpParams, 5000);//设置超时
            HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);//设置超时
            return mClient.execute(httpRequest);
        }
    
        /**
         * Creates the appropriate subclass of HttpUriRequest for passed in request.
         * 请求工厂类{GET,DELET,PUT,POST}
         * @param request the request
         * @param additionalHeaders the additional headers
         * @return the http uri request
         * @throws AuthFailureError the auth failure error
         */
        @SuppressWarnings("deprecation")
        /* protected */ static HttpUriRequest createHttpRequest(Request<?> request,
                Map<String, String> additionalHeaders) throws AuthFailureError {
            switch (request.getMethod()) {
                case Method.DEPRECATED_GET_OR_POST: {
                    // This is the deprecated way that needs to be handled for backwards compatibility.
                    // If the request's post body is null, then the assumption is that the request is
                    // GET.  Otherwise, it is assumed that the request is a POST.
                    byte[] postBody = request.getPostBody();
                    if (postBody != null) {
                        HttpPost postRequest = new HttpPost(request.getUrl());
                        postRequest.addHeader(HEADER_CONTENT_TYPE, request.getPostBodyContentType());
                        HttpEntity entity;
                        entity = new ByteArrayEntity(postBody);
                        postRequest.setEntity(entity);
                        return postRequest;
                    } else {
                        return new HttpGet(request.getUrl());
                    }
                }
                case Method.GET:
                    return new HttpGet(request.getUrl());
                case Method.DELETE:
                    return new HttpDelete(request.getUrl());
                case Method.POST: {
                    HttpPost postRequest = new HttpPost(request.getUrl());
                    postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
                    setEntityIfNonEmptyBody(postRequest, request);
                    return postRequest;
                }
                case Method.PUT: {
                    HttpPut putRequest = new HttpPut(request.getUrl());
                    putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
                    setEntityIfNonEmptyBody(putRequest, request);
                    return putRequest;
                }
                default:
                    throw new IllegalStateException("Unknown request method.");
            }
        }
    
        /**
         * Sets the entity if non empty body.
         * 非空体Entity
         * @param httpRequest the http request
         * @param request the request
         * @throws AuthFailureError the auth failure error
         */
        private static void setEntityIfNonEmptyBody(HttpEntityEnclosingRequestBase httpRequest,
                Request<?> request) throws AuthFailureError {
            byte[] body = request.getBody();
            if (body != null) {
                HttpEntity entity = new ByteArrayEntity(body);
                httpRequest.setEntity(entity);
            }
        }
    
        /**
         * Called before the request is executed using the underlying HttpClient.
         * 在请求之前调用
         * <p>Overwrite in subclasses to augment the request.</p>
         * 由子类覆写扩展
         * @param request the request
         * @throws IOException Signals that an I/O exception has occurred.
         */
        protected void onPrepareRequest(HttpUriRequest request) throws IOException {
            // Nothing.
        }
    }

    它的构造参数是一个HttpClient,Velloy是用AndroidHttpClient.newInstance(userAgent)建立一个HttpClient实例。

    再看这个类的最重要方法也就是对HttpStack接口的performRequest()方法进行具体实现。

    第一步:通过这个静态方法createHttpRequest()来获取一个HttpUriRequest请求实例,其中HttpGet,HttpPost,HttpPut,HttpDelete都实现了HttpUriRequest这个接口。

    这步就确定了请求类型和创立了Http实际请求。

    第二步:通过这两个方法添加Http头部,

    addHeaders(httpRequest, additionalHeaders);//添加自定义头部
    addHeaders(httpRequest, request.getHeaders());//添加请求自带头部

    第三步:这个方法

    onPrepareRequest()

    挺人性化的,相当于AsyncTask的onPreExecute(),不过要实现这个方法需要自行扩展此类,谷歌没有把她提出来。

    一开始我还以为没有类似的方法,像Afinal的方法名字取得很吸引人,叫onStart(),onSuccess(),onFailure()。其实在Volley与之相对应的都有,onResponse就相当于onSuccess(),onErrorResponse就相当于onFailure(),而onPrepareRequest()就对应onStart()。

    第四步:设置超时,这个超时设置值是在Request里封装进去了,Request封装了很多东西,比如请求的URL等。

    在此谷歌在超时这里做了很温馨的提示,设置连接超时考虑WIFI和3G网络设置不一样,这里应该可以留出一个缺口,根据实际网络设置不一样的时常。貌似现在很多应用会根据网络来进行内容排版,例如什么无图模式啊,仅在wifi下上传等。

    HurlStack

    先看下构造,看来大框架构造都是按这格式的。

     /**
         * 默认的构造器
         * {@link Volley#newRequestQueue(android.content.Context, HttpStack)}
         */
        public HurlStack() {
            this(null);
        }
    
        /**
         * @param urlRewriter Rewriter to use for request URLs
         */
        public HurlStack(UrlRewriter urlRewriter) {
            this(urlRewriter, null);
        }
    
        /**
         * 两个主要参数
         * @param urlRewriter Rewriter to use for request URLs//Url转换器
         * @param sslSocketFactory SSL factory to use for HTTPS connections//安全连接
         */
        public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {
            mUrlRewriter = urlRewriter;
            mSslSocketFactory = sslSocketFactory;
        }

    由此可见主要字段就是mUrlRewriter 和mSslSocketFactory 。前面的是URL转换接口,后面是安全连接。

    这个URL转换接口还是挺人性化的,可以过滤些非法字符和省略Http或者www.

     public interface UrlRewriter {
            /**
             * Returns a URL to use instead of the provided one, or null to indicate
             * this URL should not be used at all.
             */
            public String rewriteUrl(String originalUrl);
        }

    然后我们还是看HttpStack接口的performRequest()方法实现。

    @Override
        public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
                throws IOException, AuthFailureError {
            String url = request.getUrl();//获取这个请求的url
            HashMap<String, String> map = new HashMap<String, String>();
            map.putAll(request.getHeaders());
            map.putAll(additionalHeaders);
            if (mUrlRewriter != null) {//默认的不会对url转换
                String rewritten = mUrlRewriter.rewriteUrl(url);//实现UrlRewriter#rewriteUrl方法
                if (rewritten == null) {
                    throw new IOException("URL blocked by rewriter: " + url);
                }
                url = rewritten;
            }
            URL parsedUrl = new URL(url);//解析后Url
            HttpURLConnection connection = openConnection(parsedUrl, request);
            for (String headerName : map.keySet()) {//为connection添加属性
                connection.addRequestProperty(headerName, map.get(headerName));
            }
            setConnectionParametersForRequest(connection, request);//设置请求方法
            // Initialize HttpResponse with data from the HttpURLConnection.
            ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
            int responseCode = connection.getResponseCode();
            if (responseCode == -1) {//不能取回ResponseCode
                // -1 is returned by getResponseCode() if the response code could not be retrieved.
                // Signal to the caller that something was wrong with the connection.
                throw new IOException("Could not retrieve response code from HttpUrlConnection.");
            }
            StatusLine responseStatus = new BasicStatusLine(protocolVersion,
                    connection.getResponseCode(), connection.getResponseMessage());
            BasicHttpResponse response = new BasicHttpResponse(responseStatus);//通过responseStatus获得一个BasicHttpResponse
            response.setEntity(entityFromConnection(connection));//设置Entity
            for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {//header
                if (header.getKey() != null) {
                    Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
                    response.addHeader(h);
                }
            }
            return response;
        }

    第一步:获取请求地址url,如果写了UrlRewriter就会按照这个接口的规则更改URL。

    第二步:建立URL,通过createConnection()方法获得一个HttpURLConnection,通过openConnection()方法设置一些超时类的Http配置,然后添加头部,并且通过setConnectionParametersForRequest()方法设置HttpURLConnection的请求方法(PUT.GET,POST,DELETE...)。

    第三步:然后通过HttpURLConnection获取ResponseCode,ResponseMessage,ResponseStatus,BasicHttpResponse响应结果,并进行响应处理。

    第四步:通过entityFromConnection(),把HttpURLConnection获得的流转化为HttpEntity,并返回带HttpEntity的HttpResponse。

     /**
         * Initializes an {@link HttpEntity} from the given {@link HttpURLConnection}.
         * 从HttpURLConnection获取一个HttpEntity
         * @param connection
         * @return an HttpEntity populated with data from <code>connection</code>.
         */
        private static HttpEntity entityFromConnection(HttpURLConnection connection) {
            BasicHttpEntity entity = new BasicHttpEntity();
            InputStream inputStream;//首先从HttpURLConnection获取一个输入流
            try {
                inputStream = connection.getInputStream();
            } catch (IOException ioe) {
                inputStream = connection.getErrorStream();
            }
            entity.setContent(inputStream);//把流设置为HttpEntity的Content
            entity.setContentLength(connection.getContentLength());//设置内容长度
            entity.setContentEncoding(connection.getContentEncoding());//设置编码格式
            entity.setContentType(connection.getContentType());//设置内容类型
            return entity;
        }

    为什么HurlStack没有onPrepareRequest()方法,如果要的话那就只有知己加了。

    来看扩展HttpClientStack的一个类:

    public class IpadHttpStack extends  HttpClientStack{
    
        interface OnStartListener{
            void onStart(HttpUriRequest request);
        }
        
        OnStartListener mOnStartListener;
        static String ua = "ipad";
        
        public IpadHttpStack() {
            super(AndroidHttpClient.newInstance(ua));
            // TODO Auto-generated constructor stub
        }
    
        @Override
        protected void onPrepareRequest(HttpUriRequest request) throws IOException {
            // TODO Auto-generated method stub
            super.onPrepareRequest(request);
            if(mOnStartListener!=null)
            mOnStartListener.onStart(request);
        }
    
        public void setOnStartListener(OnStartListener listener) {
            this.mOnStartListener = listener;
        }
        
    }

    这是测试类,访问路由器网关。

    public class MainActivity extends Activity {
        RequestQueue mQueue;
        IpadHttpStack bvinHttp;
        private static HashSet<Class<?>> exeptionList = new HashSet<Class<?>>();
        static{
            exeptionList.add(AuthFailureError.class);
            exeptionList.add(NetworkError.class);
            exeptionList.add(NoConnectionError.class);
            exeptionList.add(ParseError.class);
            exeptionList.add(ServerError.class);
            exeptionList.add(TimeoutError.class);
        }
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            Log.e("onResponse","sdgdsg");
            bvinHttp = new IpadHttpStack();
            mQueue = Volley.newRequestQueue(getApplicationContext(),bvinHttp);
            //StringRequest四个构造参数分别是Request类型,url,网络请求响应监听器,错误监听器
            bvinHttp.setOnStartListener(new IpadHttpStack.OnStartListener() {
                
                @Override
                public void onStart(HttpUriRequest request) {
                    // TODO Auto-generated method stub
                    
                }
            });
            Authenticator.setDefault(new Authenticator() {
    
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    // TODO Auto-generated method stub
                    return new PasswordAuthentication("admin", "admin".toCharArray());
                }
                
            });
            mQueue.add(new StringRequest(Method.GET, "http://192.168.1.1", new Listener<String>(){
    
                @Override
                public void onResponse(String arg0) {
                    // TODO Auto-generated method stub
                    Log.e("onResponse", arg0);
                }
                
            }, new ErrorListener(){
    
                @Override
                public void onErrorResponse(VolleyError arg0) {
                    // TODO Auto-generated method stub
                    if (arg0 instanceof TimeoutError) {
                        Log.e("onErrorResponse", "超时");
                    }else if(arg0 instanceof AuthFailureError){
                        Log.e("AuthFailureError", arg0.toString());
                    }
                    Log.e("AuthFailureError", arg0.toString());
                    //exeptionList.contains(arg0)
                }
                
            }));
            mQueue.start();
        }
        
        
        
        @Override
        protected void onStop() {
            // TODO Auto-generated method stub
            super.onStop();
            mQueue.stop();
        }
    
    
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            // TODO Auto-generated method stub
            menu.add("取消");
            return super.onCreateOptionsMenu(menu);
        }
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            // TODO Auto-generated method stub
            mQueue.cancelAll(new RequestFilter(){
    
                @Override
                public boolean apply(Request<?> arg0) {
                    // TODO Auto-generated method stub
                    arg0.cancel();
                    return false;
                }});
            return super.onOptionsItemSelected(item);
        }
    
        
        
    }
  • 相关阅读:
    import cv2出现“ImportError: DLL load failed: 找不到指定的模块”
    Ubuntu 18.04 安装MySQL
    在Pycharm中自动添加时间日期作者等信息
    Ubuntu18.04安装Python虚拟环境
    Windows10远程报错:由于CredSSP加密Oracle修正
    Ubuntu 18.04LTS 更新镜像配置
    jetbrains的JetBrains PyCharm 2018.3.1破解激活到2100年(最新亲测可用)
    解决爬虫中遇到的js加密问题之有道登录js逆向解析
    利用远程服务器在docker容器搭建pyspider运行时出错的问题
    linux服务器安装pyspide关于rgnutls.h: No such file or directory 的解决方案
  • 原文地址:https://www.cnblogs.com/bvin/p/3291921.html
Copyright © 2011-2022 走看看