您的位置:首页 > 理论基础 > 计算机网络

教你写Android网络框架之Http请求的分发与执行

2015-07-11 16:42 579 查看


教你写Android网络框架之Http请求的分发与执行


前言

在前两篇( 教你写Android网络框架之基本架构教你写Android网络框架之Request、Response类与请求队列 )博客中,我们已经介绍了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,由于比较简单我们直接上代码吧。

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778/** * 网络请求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函数,也就是执行请求。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

/**

* 执行网络请求的接口

*

* @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的工厂函数传递进来。

例如 :

123456789101112131415161718192021222324252627282930313233343536/** * @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的示例,最近比较忙,因此写的配置比较简单,有需要的同学自己优化了。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

/**

* 使用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等操作。

12345678910111213141516171819202122232425262728293031@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代码如下 :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

/**

* 请求结果投递类,将请求结果投递给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函数。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

/**

* 处理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线程。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: