Skip to content

Commit 114251e

Browse files
sjuddglide-copybara-robot
authored andcommittedJan 13, 2020
Open source a Cronet integration for Glide.
PiperOrigin-RevId: 289467603
1 parent ff29849 commit 114251e

File tree

7 files changed

+121
-44
lines changed

7 files changed

+121
-44
lines changed
 

‎integration/cronet/build.gradle

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1-
package com.bumptech.glide.integration.cronet
2-
31
apply plugin: 'com.android.library'
42

53
dependencies {
64
implementation project(':library')
7-
annotationProcessor project(':annotation:compiler')
5+
implementation 'com.google.android.gms:play-services-cronet:17.0.0'
6+
implementation "com.google.guava:guava:${GUAVA_VERSION}"
7+
implementation project(':annotation')
88

99
api "androidx.annotation:annotation:${ANDROID_X_VERSION}"
10+
11+
testImplementation "com.google.truth:truth:${TRUTH_VERSION}"
12+
testImplementation "junit:junit:${JUNIT_VERSION}"
13+
testImplementation "org.robolectric:robolectric:${ROBOLECTRIC_VERSION}"
14+
testImplementation "org.mockito:mockito-core:${MOCKITO_VERSION}"
1015
}
1116

1217
android {

‎integration/cronet/src/main/AndroidManifest.xml

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
22
package="com.bumptech.glide.integration.cronet">
3-
4-
<uses-sdk android:minSdkVersion="14" />
53
<application>
64
<meta-data
75
android:name="com.bumptech.glide.integration.cronet.CronetGlideModule"

‎integration/cronet/src/main/java/com/bumptech/glide/integration/cronet/ChromiumRequestSerializer.java

+15-11
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,17 @@
3535
* <ul>
3636
* A new request is made to cronet if a url is requested and no cronet request for that url is
3737
* in progress
38-
* <ul>
39-
* Subsequent requests for in progress urls do not make new requests to cronet, but are
40-
* notified when the existing cronet request completes.
41-
* <ul>
42-
* Cancelling a single request does not cancel the cronet request if multiple requests for
43-
* the url have been made, but cancelling all requests for a url does cancel the cronet
44-
* request.
38+
* </ul>
39+
*
40+
* <ul>
41+
* Subsequent requests for in progress urls do not make new requests to cronet, but are notified
42+
* when the existing cronet request completes.
43+
* </ul>
44+
*
45+
* <ul>
46+
* Cancelling a single request does not cancel the cronet request if multiple requests for the url
47+
* have been made, but cancelling all requests for a url does cancel the cronet request.
48+
* </ul>
4549
*/
4650
final class ChromiumRequestSerializer {
4751
private static final String TAG = "ChromiumSerializer";
@@ -50,7 +54,7 @@ final class ChromiumRequestSerializer {
5054
new EnumMap<>(Priority.class);
5155
// Memoized so that all callers can share an instance.
5256
// Suppliers.memoize() is thread safe. See google3/java/com/google/common/base/Suppliers.java
53-
private static final Supplier<Executor> glideExecutorSupplier =
57+
private static final Supplier<Executor> GLIDE_EXECUTOR_SUPPLIER =
5458
Suppliers.memoize(
5559
new Supplier<Executor>() {
5660
@Override
@@ -229,7 +233,7 @@ public void onReadCompleted(
229233

230234
@Override
231235
public void onSucceeded(UrlRequest request, final UrlResponseInfo info) {
232-
glideExecutorSupplier
236+
GLIDE_EXECUTOR_SUPPLIER
233237
.get()
234238
.execute(
235239
new PriorityRunnable(priority) {
@@ -247,7 +251,7 @@ public void run() {
247251
@Override
248252
public void onFailed(
249253
UrlRequest urlRequest, final UrlResponseInfo urlResponseInfo, final CronetException e) {
250-
glideExecutorSupplier
254+
GLIDE_EXECUTOR_SUPPLIER
251255
.get()
252256
.execute(
253257
new PriorityRunnable(priority) {
@@ -260,7 +264,7 @@ public void run() {
260264

261265
@Override
262266
public void onCanceled(UrlRequest urlRequest, @Nullable final UrlResponseInfo urlResponseInfo) {
263-
glideExecutorSupplier
267+
GLIDE_EXECUTOR_SUPPLIER
264268
.get()
265269
.execute(
266270
new PriorityRunnable(priority) {

‎integration/cronet/src/main/java/com/bumptech/glide/integration/cronet/CronetGlideModule.java

+10-4
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,30 @@
66
import com.bumptech.glide.Registry;
77
import com.bumptech.glide.load.model.GlideUrl;
88
import com.bumptech.glide.module.GlideModule;
9-
import com.google.android.apps.common.proguard.UsedByReflection;
9+
import com.google.common.base.Supplier;
1010
import java.io.InputStream;
1111
import java.nio.ByteBuffer;
12+
import org.chromium.net.CronetEngine;
1213

1314
/**
1415
* A {@link GlideModule} that registers components allowing remote image fetching to be done using
1516
* Cronet.
1617
*/
17-
@UsedByReflection("meta-data")
1818
public final class CronetGlideModule implements GlideModule {
1919

2020
@Override
2121
public void applyOptions(Context context, GlideBuilder builder) {}
2222

2323
@Override
24-
public void registerComponents(Context context, Glide glide, Registry registry) {
24+
public void registerComponents(final Context context, Glide glide, Registry registry) {
2525
CronetRequestFactory factory =
26-
new CronetRequestFactoryImpl(() -> CronetEngineSingleton.getSingleton(context));
26+
new CronetRequestFactoryImpl(
27+
new Supplier<CronetEngine>() {
28+
@Override
29+
public CronetEngine get() {
30+
return CronetEngineSingleton.getSingleton(context);
31+
}
32+
});
2733
registry.replace(
2834
GlideUrl.class, InputStream.class, new ChromiumUrlLoader.StreamFactory(factory, null));
2935
registry.prepend(

‎integration/cronet/src/main/java/com/bumptech/glide/integration/cronet/CronetLibraryGlideModule.java

+10-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
import com.bumptech.glide.integration.cronet.ChromiumUrlLoader.StreamFactory;
1010
import com.bumptech.glide.load.model.GlideUrl;
1111
import com.bumptech.glide.module.LibraryGlideModule;
12+
import com.google.common.base.Supplier;
1213
import java.io.InputStream;
1314
import java.nio.ByteBuffer;
15+
import org.chromium.net.CronetEngine;
1416

1517
/**
1618
* A {@link LibraryGlideModule} that registers components allowing remote image fetching to be done
@@ -21,9 +23,15 @@ public final class CronetLibraryGlideModule extends LibraryGlideModule {
2123

2224
@Override
2325
public void registerComponents(
24-
@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
26+
final @NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
2527
CronetRequestFactory factory =
26-
new CronetRequestFactoryImpl(() -> CronetEngineSingleton.getSingleton(context));
28+
new CronetRequestFactoryImpl(
29+
new Supplier<CronetEngine>() {
30+
@Override
31+
public CronetEngine get() {
32+
return CronetEngineSingleton.getSingleton(context);
33+
}
34+
});
2735
registry.replace(
2836
GlideUrl.class, InputStream.class, new StreamFactory(factory, null /* dataLogger */));
2937
registry.prepend(

‎integration/cronet/src/test/java/com/bumptech/glide/integration/cronet/ChromiumUrlFetcherTest.java

+77-22
Original file line numberDiff line numberDiff line change
@@ -23,39 +23,46 @@
2323
import com.bumptech.glide.load.model.LazyHeaders;
2424
import com.bumptech.glide.load.model.LazyHeaders.Builder;
2525
import com.google.common.collect.ImmutableList;
26+
import com.google.common.collect.ImmutableMap;
2627
import com.google.common.util.concurrent.MoreExecutors;
2728
import java.net.HttpURLConnection;
2829
import java.nio.ByteBuffer;
2930
import java.util.AbstractMap.SimpleImmutableEntry;
31+
import java.util.List;
3032
import java.util.Map;
31-
import java.util.Map.Entry;
3233
import java.util.concurrent.Executor;
3334
import org.chromium.net.CronetEngine;
3435
import org.chromium.net.CronetException;
3536
import org.chromium.net.UrlRequest;
3637
import org.chromium.net.UrlRequest.Callback;
3738
import org.chromium.net.UrlResponseInfo;
38-
import org.chromium.net.impl.UrlResponseInfoImpl;
3939
import org.junit.Before;
40+
import org.junit.Rule;
4041
import org.junit.Test;
4142
import org.junit.runner.RunWith;
4243
import org.mockito.ArgumentCaptor;
44+
import org.mockito.ArgumentMatchers;
4345
import org.mockito.Matchers;
4446
import org.mockito.Mock;
45-
import org.mockito.MockitoAnnotations;
4647
import org.mockito.invocation.InvocationOnMock;
48+
import org.mockito.junit.MockitoJUnit;
49+
import org.mockito.junit.MockitoRule;
4750
import org.mockito.stubbing.Answer;
4851
import org.robolectric.RobolectricTestRunner;
4952

5053
/** Tests for {@link ChromiumUrlFetcher}. */
5154
@RunWith(RobolectricTestRunner.class)
5255
public class ChromiumUrlFetcherTest {
56+
57+
@Rule public final MockitoRule mocks = MockitoJUnit.rule();
5358
@Mock private DataCallback<ByteBuffer> callback;
5459
@Mock private CronetEngine cronetEngine;
5560
@Mock private UrlRequest request;
5661
@Mock private UrlRequest.Builder mockUrlRequestBuilder;
5762
@Mock private ByteBufferParser<ByteBuffer> parser;
5863
@Mock private CronetRequestFactory cronetRequestFactory;
64+
@Mock private DataCallback<ByteBuffer> firstCallback;
65+
@Mock private DataCallback<ByteBuffer> secondCallback;
5966

6067
private UrlRequest.Builder builder;
6168
private GlideUrl glideUrl;
@@ -65,7 +72,6 @@ public class ChromiumUrlFetcherTest {
6572

6673
@Before
6774
public void setUp() {
68-
MockitoAnnotations.initMocks(this);
6975
when(parser.getDataClass()).thenReturn(ByteBuffer.class);
7076
when(parser.parse(any(ByteBuffer.class)))
7177
.thenAnswer(
@@ -144,7 +150,7 @@ public void testLoadData_withInProgressRequest_doesNotStartNewRequest() {
144150
.newRequest(
145151
Matchers.eq(glideUrl.toStringUrl()),
146152
anyInt(),
147-
anyMap(),
153+
ArgumentMatchers.<String, String>anyMap(),
148154
any(UrlRequest.Callback.class));
149155
}
150156

@@ -155,28 +161,74 @@ public void testLoadData_withInProgressRequest_isNotifiedWhenRequestCompletes()
155161
ChromiumUrlFetcher<ByteBuffer> secondFetcher =
156162
new ChromiumUrlFetcher<>(serializer, parser, glideUrl);
157163

158-
DataCallback<ByteBuffer> firstCb = mock(DataCallback.class);
159-
DataCallback<ByteBuffer> secondCb = mock(DataCallback.class);
160-
firstFetcher.loadData(Priority.LOW, firstCb);
161-
secondFetcher.loadData(Priority.HIGH, secondCb);
164+
firstFetcher.loadData(Priority.LOW, firstCallback);
165+
secondFetcher.loadData(Priority.HIGH, secondCallback);
162166

163167
succeed(getInfo(10, 200), urlRequestListenerCaptor.getValue(), ByteBuffer.allocateDirect(10));
164168

165-
verify(firstCb, timeout(1000)).onDataReady(isA(ByteBuffer.class));
166-
verify(secondCb, timeout(1000)).onDataReady(isA(ByteBuffer.class));
169+
verify(firstCallback, timeout(1000)).onDataReady(isA(ByteBuffer.class));
170+
verify(secondCallback, timeout(1000)).onDataReady(isA(ByteBuffer.class));
167171
}
168172

169173
@NonNull
170-
private UrlResponseInfo getInfo(int contentLength, int statusCode) {
171-
return new UrlResponseInfoImpl(
172-
ImmutableList.of(glideUrl.toStringUrl()),
173-
statusCode,
174-
"OK",
175-
ImmutableList.<Entry<String, String>>of(
176-
new SimpleImmutableEntry<>("Content-Length", Integer.toString(contentLength))),
177-
false,
178-
"",
179-
"");
174+
private UrlResponseInfo getInfo(final int contentLength, final int statusCode) {
175+
return new UrlResponseInfo() {
176+
177+
@Override
178+
public String getUrl() {
179+
return glideUrl.toStringUrl();
180+
}
181+
182+
@Override
183+
public List<String> getUrlChain() {
184+
return ImmutableList.of(getUrl());
185+
}
186+
187+
@Override
188+
public int getHttpStatusCode() {
189+
return statusCode;
190+
}
191+
192+
@Override
193+
public String getHttpStatusText() {
194+
return "OK";
195+
}
196+
197+
@Override
198+
public List<Map.Entry<String, String>> getAllHeadersAsList() {
199+
return ImmutableList.<Map.Entry<String, String>>of(
200+
new SimpleImmutableEntry<>("Content-Length", Integer.toString(contentLength)));
201+
}
202+
203+
@Override
204+
public Map<String, List<String>> getAllHeaders() {
205+
ImmutableMap.Builder<String, List<String>> builder = ImmutableMap.builder();
206+
for (Map.Entry<String, String> entry : getAllHeadersAsList()) {
207+
builder.put(entry.getKey(), ImmutableList.copyOf(entry.getValue().split(",")));
208+
}
209+
return builder.build();
210+
}
211+
212+
@Override
213+
public boolean wasCached() {
214+
return false;
215+
}
216+
217+
@Override
218+
public String getNegotiatedProtocol() {
219+
return "";
220+
}
221+
222+
@Override
223+
public String getProxyServer() {
224+
return "";
225+
}
226+
227+
@Override
228+
public long getReceivedByteCount() {
229+
return 0;
230+
}
231+
};
180232
}
181233

182234
@Test
@@ -226,7 +278,10 @@ public void testCancel_withStartedRequest_cancelsRequest() {
226278

227279
@Test
228280
public void testRequestComplete_withNonNullException_callsCallbackWithException() {
229-
CronetException expected = new CronetException("test", /*cause=*/ null) {};
281+
CronetException expected =
282+
new CronetException("test", /*cause=*/ null) {
283+
static final long serialVersionUID = 1;
284+
};
230285
fetcher.loadData(Priority.LOW, callback);
231286
urlRequestListenerCaptor.getValue().onFailed(request, null, expected);
232287

‎settings.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ include ':samples:contacturi'
2121
include ':samples:imgur'
2222
include ':integration'
2323
include ':integration:concurrent'
24+
include ':integration:cronet'
2425
include ':integration:gifencoder'
2526
include ':integration:okhttp'
2627
include ':integration:okhttp3'

0 commit comments

Comments
 (0)
Please sign in to comment.