HttpStack及其实现类


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);
    }

    
    
}

长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号





    联系我们

    欢迎来到TinyMind。

    关于TinyMind的内容或商务合作、网站建议,举报不良信息等均可联系我们。

    TinyMind客服邮箱:support@tinymind.net.cn