Eric's Blog

  • 首页

  • 归档

  • 标签

  • 分类

  • 关于

  • 搜索

OkHttp源码梳理

发表于 2019-06-05 | 更新于 2020-03-22 | 评论数:

添加依赖

1
implementation 'com.squareup.okhttp3:okhttp:3.10.0'

使用方法

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
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url("http://www.baidu.com")
.build();
try {
//同步流程
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
System.out.println("成功");
}
} catch (Throwable t) {
t.printStackTrace();
}

try {
//异步流程
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
System.out.println("失败");
}

@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println("成功");
}
});
} catch (Throwable t) {
t.printStackTrace();
}

发起请求

加入各种拦截器 用到责任链的思想

1.客户端自定义的拦截器 可以做一些日志的打印
2.重试与重定向拦截器 哪些请求需要重试 重试策略 哪些请求需要重定向之类
3.桥接拦截器 将客户端的request对象转化为一个服务器认识的请求 添加头信息 cookie等信息
4.缓存拦截器 判断哪些请求是不需要走服务器 可以直接走缓存的处理
5.连接拦截器 建立 获取一个有效的服务器连接
6.客户端自定义的拦截器 可以做一些向服务器传递之前的统一操作 添加公共参数 url签名校验 包体加密等
7.调用服务拦截器 真正的与服务器交互的拦截器

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
@Override 
public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
//加入到分发丢了
client.dispatcher().executed(this);
//这步才是真正的执行 前面是一些条件的判断
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
//从分发队列删除
client.dispatcher().finished(this);
}
}

Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
//加入各种拦截器 用到责任链的思想
//有客户端传进来的 有内置的拦截器
//客户端自定义的拦截器 可以做一些日志的打印
interceptors.addAll(client.interceptors());
//重试与重定向拦截器 哪些请求需要重试 重试策略 哪些请求需要重定向之类
interceptors.add(retryAndFollowUpInterceptor);
//桥接拦截器 将客户端的request对象转化为一个服务器认识的请求 添加头信息 cookie等信息
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//缓存拦截器 判断哪些请求是不需要走服务器 可以直接走缓存的处理
interceptors.add(new CacheInterceptor(client.internalCache()));
//连接拦截器 建立 获取一个有效的服务器连接
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
//客户端自定义的拦截器 可以做一些向服务器传递之前的统一操作 添加公共参数 url签名校验 包体加密等
interceptors.addAll(client.networkInterceptors());
}
//调用服务拦截器 真正的与服务器交互的拦截器
interceptors.add(new CallServerInterceptor(forWebSocket));

Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());

return chain.proceed(originalRequest);
}

具体执行每个拦截器

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
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();

calls++;

// If we already have a stream, confirm that the incoming request will use it.
if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}

// If we already have a stream, confirm that this is the only call to chain.proceed().
if (this.httpCodec != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}

// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
Interceptor interceptor = interceptors.get(index);
//执行这个拦截器的具体操作
Response response = interceptor.intercept(next);

// Confirm that the next interceptor made its required call to chain.proceed().
if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}

// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}

if (response.body() == null) {
throw new IllegalStateException(
"interceptor " + interceptor + " returned a response with no body");
}

return response;
}

RetryAndFollowUpInterceptor 重试与重定向拦截器

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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
@Override 
public Response intercept(Chain chain) throws IOException {
//chain是下一个拦截器对象
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Call call = realChain.call();
EventListener eventListener = realChain.eventListener();

//初始化一个request管理对象
StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(request.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;

int followUpCount = 0;
Response priorResponse = null;
while (true) {
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}

Response response;
boolean releaseConnection = true;
try {
//执行下一个拦截器做真正处理
response = realChain.proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), streamAllocation, false, request)) {
throw e.getLastConnectException();
}
releaseConnection = false;
continue;
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, streamAllocation, requestSendStarted, request)) throw e;
releaseConnection = false;
continue;
} finally {
// We're throwing an unchecked exception. Release any resources.
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
}

//如果上一次返回的结果不为空 对response做修改
// Attach the prior response if it exists. Such responses never have a body.
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}

//对返回结果进行处理 看是否需要重新请求
Request followUp = followUpRequest(response, streamAllocation.route());

//不需要重新请求 返回结果
if (followUp == null) {
if (!forWebSocket) {
streamAllocation.release();
}
return response;
}

closeQuietly(response.body());

if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}

if (followUp.body() instanceof UnrepeatableRequestBody) {
streamAllocation.release();
throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
}

//请求网址与返回的网址不一致 需要进行重新请求
if (!sameConnection(response, followUp.url())) {
streamAllocation.release();
streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(followUp.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;
} else if (streamAllocation.codec() != null) {
throw new IllegalStateException("Closing the body of " + response
+ " didn't close its backing stream. Bad interceptor?");
}

//记录下一次请求的request对象
request = followUp;
//记录上一次请求的请求结果
priorResponse = response;
}
}

/**
* Figures out the HTTP request to make in response to receiving {@code userResponse}. This will
* either add authentication headers, follow redirects or handle a client request timeout. If a
* follow-up is either unnecessary or not applicable, this returns null.
*/
private Request followUpRequest(Response userResponse, Route route) throws IOException {
if (userResponse == null) throw new IllegalStateException();
int responseCode = userResponse.code();

final String method = userResponse.request().method();
//根据responseCode进行处理
switch (responseCode) {
case HTTP_PROXY_AUTH:
Proxy selectedProxy = route != null
? route.proxy()
: client.proxy();
if (selectedProxy.type() != Proxy.Type.HTTP) {
throw new ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy");
}
return client.proxyAuthenticator().authenticate(route, userResponse);

case HTTP_UNAUTHORIZED:
return client.authenticator().authenticate(route, userResponse);

case HTTP_PERM_REDIRECT:
case HTTP_TEMP_REDIRECT:
// "If the 307 or 308 status code is received in response to a request other than GET
// or HEAD, the user agent MUST NOT automatically redirect the request"
if (!method.equals("GET") && !method.equals("HEAD")) {
return null;
}
// fall-through
case HTTP_MULT_CHOICE:
case HTTP_MOVED_PERM:
case HTTP_MOVED_TEMP:
case HTTP_SEE_OTHER:
//重定向处理
// Does the client allow redirects?
if (!client.followRedirects()) return null;

//获取重定向地址 构建请求对象
String location = userResponse.header("Location");
if (location == null) return null;
HttpUrl url = userResponse.request().url().resolve(location);

// Don't follow redirects to unsupported protocols.
if (url == null) return null;

// If configured, don't follow redirects between SSL and non-SSL.
boolean sameScheme = url.scheme().equals(userResponse.request().url().scheme());
if (!sameScheme && !client.followSslRedirects()) return null;

// Most redirects don't include a request body.
Request.Builder requestBuilder = userResponse.request().newBuilder();
if (HttpMethod.permitsRequestBody(method)) {
final boolean maintainBody = HttpMethod.redirectsWithBody(method);
if (HttpMethod.redirectsToGet(method)) {
requestBuilder.method("GET", null);
} else {
RequestBody requestBody = maintainBody ? userResponse.request().body() : null;
requestBuilder.method(method, requestBody);
}
if (!maintainBody) {
requestBuilder.removeHeader("Transfer-Encoding");
requestBuilder.removeHeader("Content-Length");
requestBuilder.removeHeader("Content-Type");
}
}

// When redirecting across hosts, drop all authentication headers. This
// is potentially annoying to the application layer since they have no
// way to retain them.
if (!sameConnection(userResponse, url)) {
requestBuilder.removeHeader("Authorization");
}

return requestBuilder.url(url).build();

case HTTP_CLIENT_TIMEOUT:
//请求超时处理
// 408's are rare in practice, but some servers like HAProxy use this response code. The
// spec says that we may repeat the request without modifications. Modern browsers also
// repeat the request (even non-idempotent ones.)
// 如果设定不处理超时 则跳过
if (!client.retryOnConnectionFailure()) {
// The application layer has directed us not to retry the request.
return null;
}

if (userResponse.request().body() instanceof UnrepeatableRequestBody) {
return null;
}

if (userResponse.priorResponse() != null
&& userResponse.priorResponse().code() == HTTP_CLIENT_TIMEOUT) {
// We attempted to retry and got another timeout. Give up.
return null;
}

if (retryAfter(userResponse, 0) > 0) {
return null;
}

return userResponse.request();

case HTTP_UNAVAILABLE:
if (userResponse.priorResponse() != null
&& userResponse.priorResponse().code() == HTTP_UNAVAILABLE) {
// We attempted to retry and got another timeout. Give up.
return null;
}

if (retryAfter(userResponse, Integer.MAX_VALUE) == 0) {
// specifically received an instruction to retry without delay
return userResponse.request();
}

return null;

default:
return null;
}
}

private int retryAfter(Response userResponse, int defaultDelay) {
String header = userResponse.header("Retry-After");

if (header == null) {
return defaultDelay;
}

// https://tools.ietf.org/html/rfc7231#section-7.1.3
// currently ignores a HTTP-date, and assumes any non int 0 is a delay
if (header.matches("\\d+")) {
return Integer.valueOf(header);
}

return Integer.MAX_VALUE;
}

BridgeInterceptor 桥梁拦截器(客户端代码和网络代码的桥接)

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
@Override 
public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();

//将客户端的一个request对象转化为服务器认识的对象 设置一些请求头 设置cookie等信息
RequestBody body = userRequest.body();
if (body != null) {
MediaType contentType = body.contentType();
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString());
}

long contentLength = body.contentLength();
if (contentLength != -1) {
requestBuilder.header("Content-Length", Long.toString(contentLength));
requestBuilder.removeHeader("Transfer-Encoding");
} else {
requestBuilder.header("Transfer-Encoding", "chunked");
requestBuilder.removeHeader("Content-Length");
}
}

if (userRequest.header("Host") == null) {
requestBuilder.header("Host", hostHeader(userRequest.url(), false));
}

//默认支持Keep-Alive
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive");
}

//默认支持gzip压缩
// If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
// the transfer stream.
boolean transparentGzip = false;
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true;
requestBuilder.header("Accept-Encoding", "gzip");
}

//设置cookie信息
List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
if (!cookies.isEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies));
}

if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", Version.userAgent());
}

//执行下一个拦截器 CacheInterceptor
Response networkResponse = chain.proceed(requestBuilder.build());

//存储cookie信息
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());

Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);

if (transparentGzip
&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
&& HttpHeaders.hasBody(networkResponse)) {
GzipSource responseBody = new GzipSource(networkResponse.body().source());
Headers strippedHeaders = networkResponse.headers().newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build();
responseBuilder.headers(strippedHeaders);
String contentType = networkResponse.header("Content-Type");
responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
}

return responseBuilder.build();
}

CacheInterceptor 缓存拦截器

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
@Override 
public Response intercept(Chain chain) throws IOException {
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;

long now = System.currentTimeMillis();

//从缓存中过滤 是否需要去网络拉取数据 这个逻辑比较重要 需要分析 看下如何判断是否需要走缓存的逻辑
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;

if (cache != null) {
cache.trackResponse(strategy);
}

if (cacheCandidate != null && cacheResponse == null) {
closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
}

//网络请求Request为空 则说明不需要去网络请求
//同时结果缓存中没数据 则直接返回504并说明是从缓存中获取
// If we're forbidden from using the network and the cache is insufficient, fail.
if (networkRequest == null && cacheResponse == null) {
return new Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(504)
.message("Unsatisfiable Request (only-if-cached)")
.body(Util.EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
}

//网络请求Request为空 同时缓存中有数据 则从缓存中取出数据 返回
// If we don't need the network, we're done.
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}

Response networkResponse = null;
try {
//执行下一个拦截器 ConnectInterceptor
networkResponse = chain.proceed(networkRequest);
} finally {
// If we're crashing on I/O or otherwise, don't leak the cache body.
if (networkResponse == null && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
}
}

// If we have a cache response too, then we're doing a conditional get.
if (cacheResponse != null) {
//对304错误码进行特殊处理
if (networkResponse.code() == HTTP_NOT_MODIFIED) {
Response response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers(), networkResponse.headers()))
.sentRequestAtMillis(networkResponse.sentRequestAtMillis())
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
networkResponse.body().close();

// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
cache.trackConditionalCacheHit();
cache.update(cacheResponse, response);
return response;
} else {
closeQuietly(cacheResponse.body());
}
}

//加入缓存请求结果与网络请求结果
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();

//对缓存进行更新
if (cache != null) {
if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
// Offer this request to the cache.
CacheRequest cacheRequest = cache.put(response);
return cacheWritingResponse(cacheRequest, response);
}

if (HttpMethod.invalidatesCache(networkRequest.method())) {
try {
cache.remove(networkRequest);
} catch (IOException ignored) {
// The cache cannot be written.
}
}
}

return response;
}

ConnectInterceptor 建立Socket连接拦截器

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
@Override 
public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
StreamAllocation streamAllocation = realChain.streamAllocation();

// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
//去创建一个HttpCodec(HTTP1 HTTP2)和RealConnection对象 赋予到下一个请求参数中
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
RealConnection connection = streamAllocation.connection();

return realChain.proceed(request, streamAllocation, httpCodec, connection);
}

public HttpCodec newStream(
OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
int connectTimeout = chain.connectTimeoutMillis();
int readTimeout = chain.readTimeoutMillis();
int writeTimeout = chain.writeTimeoutMillis();
int pingIntervalMillis = client.pingIntervalMillis();
boolean connectionRetryEnabled = client.retryOnConnectionFailure();

try {
//找到一个可用的RealConnection 并获取他的HTTP版本号
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);

synchronized (connectionPool) {
codec = resultCodec;
return resultCodec;
}
} catch (IOException e) {
throw new RouteException(e);
}
}

private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled,
boolean doExtensiveHealthChecks) throws IOException {
while (true) {
//无限循环直到找到一个真正可用的socket
RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
pingIntervalMillis, connectionRetryEnabled);

//这个连接是新建立的 则跳过Socket可用性检查
synchronized (connectionPool) {
if (candidate.successCount == 0) {
return candidate;
}
}

// Do a (potentially slow) check to confirm that the pooled connection is still good. If it
// isn't, take it out of the pool and start again.
//判断socket是否可用
if (!candidate.isHealthy(doExtensiveHealthChecks)) {
noNewStreams();
continue;
}

return candidate;
}
}

CallServerInterceptor 真正的与服务器交换数据的拦截器

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
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
HttpCodec httpCodec = realChain.httpStream();
StreamAllocation streamAllocation = realChain.streamAllocation();
RealConnection connection = (RealConnection) realChain.connection();
Request request = realChain.request();

long sentRequestMillis = System.currentTimeMillis();

realChain.eventListener().requestHeadersStart(realChain.call());
httpCodec.writeRequestHeaders(request);
realChain.eventListener().requestHeadersEnd(realChain.call(), request);

Response.Builder responseBuilder = null;
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
// If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
// Continue" response before transmitting the request body. If we don't get that, return
// what we did get (such as a 4xx response) without ever transmitting the request body.
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
httpCodec.flushRequest();
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(true);
}

if (responseBuilder == null) {
// Write the request body if the "Expect: 100-continue" expectation was met.
realChain.eventListener().requestBodyStart(realChain.call());
long contentLength = request.body().contentLength();
CountingSink requestBodyOut =
new CountingSink(httpCodec.createRequestBody(request, contentLength));
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
//将数据写入到对应的嗯连接中 使用的是okio
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
realChain.eventListener()
.requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
} else if (!connection.isMultiplexed()) {
// If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
// from being reused. Otherwise we're still obligated to transmit the request body to
// leave the connection in a consistent state.
streamAllocation.noNewStreams();
}
}

httpCodec.finishRequest();

//开始获取服务器返回数据
if (responseBuilder == null) {
//获取服务器返回的头信息
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(false);
}

Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();

int code = response.code();
if (code == 100) {
// server sent a 100-continue even though we did not request one.
// try again to read the actual response
responseBuilder = httpCodec.readResponseHeaders(false);

response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();

code = response.code();
}

realChain.eventListener()
.responseHeadersEnd(realChain.call(), response);

if (forWebSocket && code == 101) {
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
response = response.newBuilder()
.body(Util.EMPTY_RESPONSE)
.build();
} else {
response = response.newBuilder()
//获取服务器返回的body信息
.body(httpCodec.openResponseBody(response))
.build();
}

if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
streamAllocation.noNewStreams();
}

if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}

return response;
}

Android Vpn相关

发表于 2020-05-31 | 评论数:

简述

一篇搞懂TCP、HTTP、Socket、Socket连接池
TCP 协议简介
Android Proxy实现分析
安卓Vpn开发思路

linux相关

发表于 2020-05-31 | 评论数:

安装

CentOS 7 安装 JAVA环境(JDK 1.8)
搭建自己专属的vpn——Centos搭建vpn的几种办法
Ubuntu中安装FTP 服务器自己踩得坑
阿里云+Ubuntu+LAMP+WordPress搭建个人博客网站

Android 热修复整理

发表于 2020-05-31 | 评论数:

原始理论

安卓App热补丁动态修复技术介绍
1.ClassLoader优先加载问题
2.CLASS_ISPREVERIFIED问题

Tinker优化

Android 热修复 Tinker接入及源码浅析
Android 热修复 Tinker 源码分析之DexDiff / DexPatch
1.通过自己实现DexDiff逻辑 保证新老版本Class在同一个Dex中

Sophix 热更新

深入探索Android热修复技术原理 (手淘技术团队)
1.热更新 不用重启APP

Java数据结构-(一)

发表于 2020-03-22 | 评论数:

1.接口 Collection Set Map List Queue Stack
2.常用哪些 排序 常用方法
3.实现原理 各个Jdk版本优化
4.存储结构 链表 数组 初始化容量 扩容机制 优点
5.线程安全 线程安全实现方式

继承关系

Java数据结构继承关系

注意List和Set继承Collection Map没有继承Collection

List

1.ArrayList
2.LinkedList
3.CopyOnWriteArrayList

ArrayList

1.内部通过数据实现

1
transient Object[] array;

2.扩容机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private static final int MIN_CAPACITY_INCREMENT = 12;

@Override public boolean add(E object) {
Object[] a = array;
int s = size;
if (s == a.length) {
//当前容量 小于6个 增加12个位置 大于6个 增加一倍
Object[] newArray = new Object[s +
(s < (MIN_CAPACITY_INCREMENT / 2) ?
MIN_CAPACITY_INCREMENT : s >> 1)];
System.arraycopy(a, 0, newArray, 0, s);
array = a = newArray;
}
a[s] = object;
size = s + 1;
modCount++;
return true;
}

3.线程不安全 多线程会出现ConcurrentModificationException异常

LinkedList

1.内部通过链表来实现

1
2
3
4
5
6
7
8
9
10
11
12
13
transient Link<E> voidLink;

private static final class Link<ET> {
ET data;

Link<ET> previous, next;

Link(ET o, Link<ET> p, Link<ET> n) {
data = o;
previous = p;
next = n;
}
}

2.扩容机制 不存在扩容问题

1
2
3
4
5
6
7
8
9
private boolean addLastImpl(E object) {
Link<E> oldLast = voidLink.previous;
Link<E> newLink = new Link<E>(object, oldLast, voidLink);
voidLink.previous = newLink;
oldLast.next = newLink;
size++;
modCount++;
return true;
}

3.同样线程不安全 多线程会出现ConcurrentModificationException异常
4.比较ArrayList LinkedList
ArrayList 在中间插入数据比较负责 需要做拷贝工作 寻找数据比较简单 数组下标
LinkedList在任意位置插入数据比较简单 链表操作 查找数据比较复杂 遍历列表

CopyOnWriteArrayList

1.内部通过数组实现 初始化容量是0

1
private transient volatile Object[] elements;

2.扩容机制
不存在扩容问题 每次生成一个N+1的数组 将之前的数据拷过来 新的数据追加在后边

1
2
3
4
5
6
public synchronized boolean add(E e) {      Object[] newElements = new Object[elements.length + 1];
System.arraycopy(elements, 0, newElements, 0, elements.length);
newElements[elements.length] = e;
elements = newElements;
return true;
}

3.线程安全
线程安全 add addAll clear remove都是synchronized方法

Map

1.HashMap
2.HashTable
3.TreeMap
4.LinkedHashMap
5.ConcurrentHashMap
6.ConcurrentSkipListMap

HashMap

1.初始化

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
/**
* 负载因子 0.75
* The load factor used when none specified in constructor.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;

/**
* 初始化容量为16
* The default initial capacity - MUST be a power of two.
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

//内部第一层数据结构为数组
transient Node<K,V>[] table;

//内部第二层数据结构为链表
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
}

/**
* >>>为无符号左移 支持负数 -1是为了传进来的数组是4这种 生成一个比传进来的数大的2的幂
* 111111+1 这样的
* Returns a power of two size for the given target capacity.
*/
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}

2.Put方法

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
139
140
141
142
143
144
/**
* Implements Map.put and related methods.
*
* @param hash hash for key
* @param key the key
* @param value the value to put
* @param onlyIfAbsent if true, don't change existing value
* @param evict if false, the table is in creation mode.
* @return previous value, or null if none
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
//当前为容量0调用resize方法
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
//不存在Hash冲突 直接放进去
tab[i] = newNode(hash, key, value, null);
else {
//存在Hash冲突
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
//Value值相等 则直接替换
e = p;
else if (p instanceof TreeNode)
//Next已经为TreeNode 在数中插入
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
//放入到最后一个位置
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
//如果总数大于7了 转化为树状结构
treeifyBin(tab, hash);
break;
}
//key相等 做替换
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
//size大于推荐容量 需要调用resize
resize();
afterNodeInsertion(evict);
return null;
}

/**
* Initializes or doubles table size. If null, allocates in
* accord with initial capacity target held in field threshold.
* Otherwise, because we are using power-of-two expansion, the
* elements from each bin must either stay at same index, or move
* with a power of two offset in the new table.
*
* @return the table
*/
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}

3.线程不安全

HashTable

1.内部数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    /**
* 第一层 为数组
* The hash table data.
*/
private transient Entry<?,?>[] table;


//第二层 为链表
private static class Entry<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Entry<K,V> next;
}
  1. put方法
    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
    public synchronized V put(K key, V value) {
    // Make sure the value is not null
    if (value == null) {
    //遇到valuse是空 跑抛出异常
    throw new NullPointerException();
    }

    // Makes sure the key is not already in the hashtable.
    Entry<?,?> tab[] = table;
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    @SuppressWarnings("unchecked")
    Entry<K,V> entry = (Entry<K,V>)tab[index];
    for(; entry != null ; entry = entry.next) {
    //当前位置存在Hash冲突
    if ((entry.hash == hash) && entry.key.equals(key)) {
    遇到相等的 则替换
    V old = entry.value;
    entry.value = value;
    return old;
    }
    }
    //当前位置不存在Hash冲突
    addEntry(hash, key, value, index);
    return null;
    }

    private void addEntry(int hash, K key, V value, int index) {
    modCount++;

    Entry<?,?> tab[] = table;
    if (count >= threshold) {
    // Rehash the table if the threshold is exceeded
    //重新计算容量
    rehash();

    tab = table;
    hash = key.hashCode();
    index = (hash & 0x7FFFFFFF) % tab.length;
    }

    // Creates the new entry.
    @SuppressWarnings("unchecked")
    Entry<K,V> e = (Entry<K,V>) tab[index];

    tab[index] = new Entry<>(hash, key, value, e);
    count++;
    }

3.HashMap和HashTable区别
HashMap是允许key和value为null值的,用containsValue和containsKey方法判断是否包含对应键值对;
HashTable键值对都不能为空,否则报空指针异常。

TreeMap

1.内部实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//通过红黑树实现
private transient Entry<K,V> root;

static final class Entry<K,V> implements Map.Entry<K,V> {
K key;
V value;
Entry<K,V> left;
Entry<K,V> right;
Entry<K,V> parent;
boolean color = BLACK;
}

//Key需要实现Comparator方法
public TreeMap() {
comparator = null;
}

2.不存在扩容问题
3.线程不安全

LinkedHashMap

1.内部实现 继承HashMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 通过链表记录顺序
* The head (eldest) of the doubly linked list.
*/
transient LinkedHashMap.Entry<K,V> head;

static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}

/**
* The iteration ordering method for this linked hash map: <tt>true</tt>
* for access-order, <tt>false</tt> for insertion-order.
* 支持按照插入或者访问排序
* @serial
*/
final boolean accessOrder;

2.put方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//put方法使用HashMap的put方法 重写了newNode方法 记录了插入顺序
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
LinkedHashMap.Entry<K,V> p =
new LinkedHashMap.Entry<K,V>(hash, key, value, e);
linkNodeLast(p);
return p;
}

private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
LinkedHashMap.Entry<K,V> last = tail;
tail = p;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
}

ConcurrentHashMap

一个线程安全且高效的HashMap实现 采用了 CAS + synchronized 来保证并发安全性

ConcurrentHashMap内部实现

1.内部实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* The array of bins. Lazily initialized upon first insertion.
* Size is always a power of two. Accessed directly by iterators.
* 第一层数据格式为数组
*/
transient volatile Node<K,V>[] table;

//第二层数据格式为链表
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
volatile V val;
volatile Node<K,V> next;
}

2.put方法

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
/** Implementation for put and putIfAbsent */
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
//计算HashKey
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
//初始化table
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
//tab中索引为i的位置的元素为null,则直接使用CAS将值插入即可
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
V oldVal = null;
//锁住当前Node进行操作
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
K ek;
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
if (!onlyIfAbsent)
e.val = value;
break;
}
Node<K,V> pred = e;
if ((e = e.next) == null) {
pred.next = new Node<K,V>(hash, key,
value, null);
break;
}
}
}
else if (f instanceof TreeBin) {
Node<K,V> p;
binCount = 2;
if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
value)) != null) {
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}

Android-Vpn总结

发表于 2020-03-22 | 评论数:

Android Vpn实现分类

iptables

Android平台的透明代理早期都是基于NAT来实现的,netfilter模块内部有三张表,mangle, filter和nat。 修改地址转换或者端口转发 实现Vpn

VpnService

早期的Android版本只支持内置的VPN连接,VPN服务器必须是标准的PPTP或者L2TP/IPSec实现,
从Android 4.0开始,SDK提供了VpnService系列的接口,允许开发者实现自定义的VPN协议。这套API提供了拦截IP包的能力,给开发者提供了高度的可定制性。

socks代理

将接受到的数据库转发到一个Socket服务器 实现代理功能

VpnService原理

如图所示 VpnService启动后 会生成一个新的Tun虚拟网卡 手机上所有流量会经过这个虚拟网卡

Android Vpn原理

根据协议 Tun虚拟网卡接受到Ip层的包

TcpIp传输协议

需要特殊解析SYN FIN等特殊协议字命令

Tcp握手

VpnService应用

网址检测

  1. 建立类继承VpnService
  2. 从虚拟网卡读取Byte数据 此数据为Ip层数据
  3. Ip层可以判断是IpV4 IpV6 TCP UDP 做不同处理
  4. 通过Tcp头解析出Ip地址与端口与data
  5. 通过Tcp头判断是不是Http请求 如果是Http请求 获取Host
  6. 检测Ip和Host 判断是否合法 允许访问 不需要则返回503 允许则继续
  7. 允许访问 将data封装为改短口发出去的数据 Tun虚拟网卡与真正服务器通信
  8. 真正服务器返回的数据到虚拟网卡 修改Ip与Tcp头后 从虚拟网卡写入到VpnService的FileOutputStream(mVpnFileDescriptor)中

注意点

1.避免发生循环问题 与真正服务器连接的Socket连接需要是用protect
2.提高效率 需要4个优先队列LinkedBlockingQueue Tcp2Net Udp2Net Net2Device CurrentTcp2Net(优先处理当前应用的数据)
3.需要特别关注Host和端口号 写入和写出的需要对换 计算校验和
4.步骤7需要处理Tcp特殊字段 遇到Syn的字段不需要处理 需要发起连接 当连接建立成功的时候 给客户端发送SYN+ACK的特殊信息
5.需要FIN 需要关闭客户端建立的连接
6.是用Netty Nio包 提高效率 Bootstrap ChannelOutboundHandlerAdapter ChannelInboundHandlerAdapter
7.addDisallowedApplication方法 过滤哪些包不经过Vpn可以直接发给服务器 21以下没有这个方法 需要解析/proc/net/tcp文件 有Uid和端口的对应关系 可代替实现
8.需要是用Lru管理缓存 防止有些连接长时间过期 不处理
9.需要使用Lru管理缓存 遇到已处理的网址不需要额外再去服务器检查

Picasso源码梳理

发表于 2019-07-01 | 更新于 2020-03-15 | 评论数:

添加依赖

1
implementation "com.squareup.picasso:picasso:2.4.0"

简单使用

1
2
3
Picasso.with(this)
.load("http://****.jpg")
.into(mImageView);

初始化梳理

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
public static Picasso with(Context context) {
if (singleton == null) {
synchronized (Picasso.class) {
if (singleton == null) {
singleton = new Builder(context).build();
}
}
}
return singleton;
}

public Picasso build() {
Context context = this.context;

//初始化图片下载器
if (downloader == null) {
downloader = Utils.createDefaultDownloader(context);
}
//初始化Lru缓存 使用LinkedHashMap实现
if (cache == null) {
cache = new LruCache(context);
}
//初始化线程池
if (service == null) {
service = new PicassoExecutorService();
}
if (transformer == null) {
transformer = RequestTransformer.IDENTITY;
}

Stats stats = new Stats(cache);

//初始化一个分发器
Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);

return new Picasso(context, dispatcher, cache, listener, transformer,
requestHandlers, stats, indicatorsEnabled, loggingEnabled);
}
}

Load流程梳理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public RequestCreator load(String path) {
if (path == null) {
return new RequestCreator(this, null, 0);
}
if (path.trim().length() == 0) {
throw new IllegalArgumentException("Path must not be empty.");
}
//将String转化为Uri对象
return load(Uri.parse(path));
}

//返回一个请求创造对象
public RequestCreator load(Uri uri) {
return new RequestCreator(this, uri, 0);
}

init流程梳理

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
public void into(ImageView target, Callback callback) {
long started = System.nanoTime();
//在主线程调用直接拋出异常
checkMain();

//判断View是否存在
if (target == null) {
throw new IllegalArgumentException("Target must not be null.");
}

//判断请求对象
if (!data.hasImage()) {
picasso.cancelRequest(target);
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}
return;
}

if (deferred) {
if (data.hasSize()) {
throw new IllegalStateException("Fit cannot be used with resize.");
}
int width = target.getWidth();
int height = target.getHeight();
if (width == 0 || height == 0) {
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}
picasso.defer(target, new DeferredRequestCreator(this, target, callback));
return;
}
data.resize(width, height);
}

//生成请求对象与Key作为唯一标识
Request request = createRequest(started);
String requestKey = createKey(request);

//从内存Cache中找一遍
if (!skipMemoryCache) {
Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
if (bitmap != null) {
picasso.cancelRequest(target);
setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);
if (picasso.loggingEnabled) {
log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);
}
if (callback != null) {
callback.onSuccess();
}
return;
}
}

//是否设置了占位图片
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}

//新建一个请求Action
Action action =
new ImageViewAction(picasso, target, request, skipMemoryCache, noFade, errorResId,
errorDrawable, requestKey, tag, callback);

picasso.enqueueAndSubmit(action);
}

void enqueueAndSubmit(Action action) {
Object target = action.getTarget();
if (target != null && targetToAction.get(target) != action) {
// This will also check we are on the main thread.
cancelExistingRequest(target);
targetToAction.put(target, action);
}
submit(action);
}

多类型资源处理流程

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
//有下面这些资源的处理能力 他们回依次执行
List<RequestHandler> allRequestHandlers =
new ArrayList<RequestHandler>(builtInHandlers + extraCount);

// ResourceRequestHandler needs to be the first in the list to avoid
// forcing other RequestHandlers to perform null checks on request.uri
// to cover the (request.resourceId != 0) case.
allRequestHandlers.add(new ResourceRequestHandler(context));
if (extraRequestHandlers != null) {
//客户端自己实现的RequestHandlers 默认为空
allRequestHandlers.addAll(extraRequestHandlers);
}
allRequestHandlers.add(new ContactsPhotoRequestHandler(context));
allRequestHandlers.add(new MediaStoreRequestHandler(context));
allRequestHandlers.add(new ContentStreamRequestHandler(context));
allRequestHandlers.add(new AssetRequestHandler(context));
allRequestHandlers.add(new FileRequestHandler(context));
allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats));
requestHandlers = Collections.unmodifiableList(allRequestHandlers);


//依次去上边几个RequestHandler中去请求
for (int i = 0, count = requestHandlers.size(); i < count; i++) {
RequestHandler requestHandler = requestHandlers.get(i);
if (requestHandler.canHandleRequest(request)) {
return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler);
}
}

//ResourceRequestHandler 本地资源文件 判断scheme
@Override
public boolean canHandleRequest(Request data) {
if (data.resourceId != 0) {
return true;
}

return SCHEME_ANDROID_RESOURCE.equals(data.uri.getScheme());
}

//ContactsPhotoRequestHandler 联系人
@Override
public boolean canHandleRequest(Request data) {
final Uri uri = data.uri;
return (SCHEME_CONTENT.equals(uri.getScheme())
&& ContactsContract.Contacts.CONTENT_URI.getHost().equals(uri.getHost())
&& !uri.getPathSegments().contains(ContactsContract.Contacts.Photo.CONTENT_DIRECTORY));
}

//MediaStoreRequestHandler 媒体库
@Override
public boolean canHandleRequest(Request data) {
final Uri uri = data.uri;
return (SCHEME_CONTENT.equals(uri.getScheme())
&& MediaStore.AUTHORITY.equals(uri.getAuthority()));
}

//ContentStreamRequestHandler Content
@Override
public boolean canHandleRequest(Request data) {
return SCHEME_CONTENT.equals(data.uri.getScheme());
}

//AssetRequestHandler assets中资源
@Override
public boolean canHandleRequest(Request data) {
Uri uri = data.uri;
return (SCHEME_FILE.equals(uri.getScheme())
&& !uri.getPathSegments().isEmpty() && ANDROID_ASSET.equals(uri.getPathSegments().get(0)));
}

//FileRequestHandler 文件资源
@Override
public boolean canHandleRequest(Request data) {
return SCHEME_FILE.equals(data.uri.getScheme());
}

//NetworkRequestHandler 最后一个网络资源
@Override
public boolean canHandleRequest(Request data) {
String scheme = data.uri.getScheme();
return (SCHEME_HTTP.equals(scheme) || SCHEME_HTTPS.equals(scheme));
}

网络请求流程梳理

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
@Override 
public Result load(Request data) throws IOException {
//加入到下载器中 支持Okhttp和UrlConnection 有OKhttp优先使用Okhttp
//Picsso没有自己实现硬盘缓存 他的硬盘缓存通过Http的头文件来控制的
//extraRequestHandlers 可以自己实现
Response response = downloader.load(data.uri, data.loadFromLocalCacheOnly);
if (response == null) {
return null;
}

Picasso.LoadedFrom loadedFrom = response.cached ? DISK : NETWORK;

Bitmap bitmap = response.getBitmap();
if (bitmap != null) {
return new Result(bitmap, loadedFrom);
}

InputStream is = response.getInputStream();
if (is == null) {
return null;
}
// Sometimes response content length is zero when requests are being replayed. Haven't found
// root cause to this but retrying the request seems safe to do so.
if (response.getContentLength() == 0) {
Utils.closeQuietly(is);
throw new IOException("Received response with 0 content-length header.");
}
if (loadedFrom == NETWORK && response.getContentLength() > 0) {
stats.dispatchDownloadFinished(response.getContentLength());
}
try {
return new Result(decodeStream(is, data), loadedFrom);
} finally {
Utils.closeQuietly(is);
}
}

//OkHttp网络请求流程
@Override
public Response load(Uri uri, boolean localCacheOnly) throws IOException {
HttpURLConnection connection = openConnection(uri);
connection.setUseCaches(true);
//网络层的缓存
if (localCacheOnly) {
connection.setRequestProperty("Cache-Control", "only-if-cached,max-age=" + Integer.MAX_VALUE);
}

int responseCode = connection.getResponseCode();
if (responseCode >= 300) {
connection.disconnect();
throw new ResponseException(responseCode + " " + connection.getResponseMessage());
}

String responseSource = connection.getHeaderField(RESPONSE_SOURCE_OKHTTP);
if (responseSource == null) {
responseSource = connection.getHeaderField(RESPONSE_SOURCE_ANDROID);
}

long contentLength = connection.getHeaderFieldInt("Content-Length", -1);
boolean fromCache = parseResponseSourceHeader(responseSource);

return new Response(connection.getInputStream(), fromCache, contentLength);
}

反编译APK

发表于 2019-07-01 | 更新于 2020-03-15 | 评论数:

APK反编译脚本

同目录下放一个bin文件夹 里边放dex2jar apktool相关文件

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
# coding=utf-8
import os
import shutil
import sys
import zipfile

reload(sys)
sys.setdefaultencoding('utf8')


def extract_to(z_file, path):
for p in z_file.namelist():
extract(z_file, p, path)


def extract(z_file, filename, path):
try:
if not filename.endswith('/'):
f = os.path.join(path, filename)
dir = os.path.dirname(f)
if not os.path.exists(dir):
os.makedirs(dir)
file(f, 'wb').write(z_file.read(filename))
except BaseException:
pass


file_name_postfix = ["_Jar", "_Res", "_Rar"]
file_name_dict = {}


def reverse_apk():
# 1.反编译APK 获取res AndroidManifest.xml assets资源
# 2.读取APK 解压缩获取相关签名信息
# 3.反编译APK 获取Jar资源文件
if os.path.exists("out"):
shutil.rmtree("out")
os.mkdir("out")
for file_name in os.listdir(os.getcwd()):
if file_name.endswith("apk"):
print file_name

# 创建该项目的输出目录
parent_dir = "out\\" + file_name.replace(".apk", "")
if os.path.exists(parent_dir):
shutil.rmtree(parent_dir)
os.mkdir(parent_dir)

# 创建该项目各个资源的输出路径
for postfix in file_name_postfix:
file_dir = parent_dir + "\\" + file_name.replace(".apk", "") + postfix
if os.path.exists(file_dir):
shutil.rmtree(file_dir)
os.mkdir(file_dir)
file_name_dict[postfix] = parent_dir + "\\" + file_name.replace(".apk",
"") + postfix

# 反编译APK 获取res AndroidManifest.xml assets资源
os.system("bin\\apktool\\apktool.bat d -f " + file_name)
shutil.rmtree(file_name_dict["_Res"])
os.rename(file_name.replace(".apk", ""), file_name_dict["_Res"])

# 读取APK 解压缩获取相关签名信息
try:
z_file = zipfile.ZipFile(file_name, mode='r')
extract_to(z_file, file_name_dict["_Rar"])
for rsa_file_name in os.listdir(file_name_dict["_Rar"] + "\\META-INF"):
if rsa_file_name.endswith("RSA") or rsa_file_name.endswith("rsa"):
os.rename(file_name_dict["_Rar"] + "\\META-INF\\" + rsa_file_name,
file_name_dict["_Rar"] + "\\META-INF\\CERT.p7b")
except BaseException, e:
print e
continue

# 反编译APK 获取Jar资源文件
for dex_file in os.listdir(file_name_dict["_Rar"]):
# 多dex处理
if dex_file.endswith("dex"):
cmd = "bin\\dex2jar\\d2j-dex2jar.bat --force " + file_name_dict[
"_Rar"] + "\\" + dex_file + " -o " + file_name_dict[
"_Jar"] + "\\" + dex_file.replace(".dex", ".jar")
print cmd
os.system(cmd)


# androi反编译
if __name__ == "__main__":
try:
reverse_apk()
except BaseException, e:
print e
raw_input('Press enter to exit...')

EventBus源码梳理

发表于 2019-06-05 | 更新于 2020-03-22 | 评论数:

梳理下EventBus
源码Git地址

添加依赖

1
implementation 'org.greenrobot:eventbus:3.1.1'

使用方法

  1. 定义消息实体:
1
public static class MessageEvent { /* Additional fields if needed */ }
  1. 订阅类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 //回调方法
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {/* Do something */};

//注册与反注册
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}

//反注册
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
  1. 发送消息:
1
EventBus.getDefault().post(new MessageEvent());

注册逻辑

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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//注册的入口
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}

public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//查看这个类是不是被遍历过 如果之前遍历过 则直接返回缓存中的数据
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}

if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
//将注册方法缓存 key是类 value是回调方法List 并返回所有回调方法
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
//遍历类中方法寻找回调方法
findUsingReflectionInSingleClass(findState);
}
//移到父类位置
findState.moveToSuperclass();
}
//将所有的注册方法返回 释放findState对象
return getMethodsAndRelease(findState);
}

private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// This is faster than getMethods, especially when subscribers are fat classes like Activities
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
methods = findState.clazz.getMethods();
findState.skipSuperClasses = true;
}
for (Method method : methods) {
int modifiers = method.getModifiers();
// private static final int BRIDGE = 0x40;
// private static final int SYNTHETIC = 0x1000;
// private static final int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC;
// public方法 不是静态 抽象 桥接 不在源代码的方法
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class<?>[] parameterTypes = method.getParameterTypes();
//参数长度必须是1
if (parameterTypes.length == 1) {
//存在Subscribe的注解
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
Class<?> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {
//获取回调方法的运行线程
ThreadMode threadMode = subscribeAnnotation.threadMode();
//将回调方法放入到List中
//方法 参数类型 运行线程 运行优先级 是否为sticky方法
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException("@Subscribe method " + methodName +
"must have exactly 1 parameter but has " + parameterTypes.length);
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException(methodName +
" is a illegal @Subscribe method: must be public, non-static, and non-abstract");
}
}
}

// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//这个是关键 将消息实体作为KEY 回调方法作为Value加入Map 以后发送消息要用到
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}

//按照优先级插入
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}

List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);

//粘性事件处理
if (subscriberMethod.sticky) {
if (eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {
Class<?> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}

发送逻辑

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
public void post(Object event) {
//获取当前线程的发送List 并将改发送事件加入到List中
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);

if (!postingState.isPosting) {
postingState.isMainThread = isMainThread();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
//遍历发送
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
//根据消息实体类型获取父类消息实体 这个还需要看下
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
//进行消息发送
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
if (!subscriptionFound) {
if (logNoSubscriberMessages) {
logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
//根据消息实体类型找到回调方法的List
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
//发现消息
postToSubscription(subscription, event, postingState.isMainThread);
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
break;
}
}
return true;
}
return false;
}

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
//在不同线程回调方法
case POSTING:
//发送线程
invokeSubscriber(subscription, event);
break;
case MAIN:
//当前在主线程直接回调 不在主线程添加到主线程发送队列通过Handler实现
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case MAIN_ORDERED:
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
// temporary: technically not correct as poster not decoupled from subscriber
invokeSubscriber(subscription, event);
}
break;
case BACKGROUND:
if (isMainThread) {
//通过Executors.newCachedThreadPool()+列表实现
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case ASYNC:
//每次启动一个新的线程去执行的
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}

stickyEvents处理

在Android开发中,Sticky事件只指事件消费者在事件发布之后才注册的也能接收到该事件的特殊类型。
在注册的时候要查一遍 以往发送的粘性事件map

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
//粘性事件处理
if (subscriberMethod.sticky) {
if (eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {
Class<?> candidateEventType = entry.getKey();
//遍历Map找到已经注册的粘性事件(包括父类) 然后激励发送
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}

//发送的时候加入到粘性事件map中
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
// Should be posted after it is putted, in case the subscriber wants to remove immediately
post(event);
}
}

Gradle(Android Studio) 生成jar包

发表于 2018-08-30 | 更新于 2020-03-15 | 分类于 开发 | 评论数:

AS默认生成aar格式的文件 有时候需要生成jar包 可以通过grable命令去生成

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
//拷贝代码
task copyClass(type: Copy) {
delete('build/libs/')
from('build/intermediates/classes/debug')
into('build/libs/class/')
}

//拷贝并解压缩libs资源
task copyUnzipLibClass() {
ant.unzip(src: "libs/jar_1.jar", dest: "build/libs/class/")
ant.unzip(src: "libs/jar_2.jar", dest: "build/libs/class/")
ant.unzip(src: "libs/jar_3.jar", dest: "build/libs/class/")
}

//编译 Jar
task buildJar(type: Jar) {
//最终的 Jar包名
archiveName = "jar_name.jar"
//初始化资源路径集
from("build/libs/class/")
//去除路径集下部分的资源
exclude "**/R.class"
//集成下面的资源
include "/com/**"
include "/org/**"
}

copyClass.dependsOn(build)
copyUnzipLibClass.dependsOn(copyClass)
buildJar.dependsOn(copyUnzipLibClass)

//运行 gradlew buildJar
12
Eric

Eric

18 日志
1 分类
7 标签
RSS
GitHub E-Mail
© 2020 Eric
|