专业 靠谱 的软件外包伙伴

您的位置:首页 > 新闻动态 > Android网络软件开发框架之Http请求的分发与执行

Android网络软件开发框架之Http请求的分发与执行

2016-09-18 16:40:14

前言

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

 

教你写Android网络框架之Http请求的分发与执行 图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。
[java] view plaincopy在CODE上查看代码片派生到我的代码片
/** 
 * 根据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是在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就是返回的数据格式类型。比如返回的数据格式为json,那对应的请求就是JsonRequest,泛型T为JSONObject,在 JsonRequest中覆写parseResponse函数,将得到的Response中的原始数据转换成JSONObject。然后将请求放到队列 中,NetworkExecutor将请求分发给HttpStack执行,执行完成之后得到Response对象,最终ResponseDelivery 将结果通过请求回调投递到UI线程。

关于:中科研拓
深圳市中科研拓科技有限公司专注提供软件外包、app开发、智能硬件开发、O2O电商平台、手机应用程序、大数据系统、物联网项目等开发外包服务,十年研发经验,上百成功案例,中科院软件外包合作企业。通过IT技术实现创造客户和社会的价值,致力于为用户提供很好的软件解决方案。联系电话400-0316-532,邮箱sales@zhongkerd.com,网址www.zhongkerd.com


  上一篇   [返回首页] [打印] [返回上页]   下一篇