cronet is a framework that using chromium net to send network request for android
November 23, 2019 ยท View on GitHub
Changelog
Current version 73.0.3653.5 released on 20th Jun 2019.
See details in CHANGELOG.
Examples
I have provided a sample.
See sample here on Github.
To run the sample application, simply clone this repository and use android studio to compile, install it on a connected device.
Feature
- Full platform supports the latest version of TLS.
- The platform supports the latest network protocols such as HTTP/2 and QUIC.
Usage
Maven
<dependency>
<groupId>io.github.lizhangqu</groupId>
<artifactId>cronet-native</artifactId>
<version>73.0.3653.0.6</version>
</dependency>
Gradle
compile 'io.github.lizhangqu:cronet-native:73.0.3653.0.6'
Remote so
The cronet's so file is big, you can use remote mode to reduce the apk size by exclude cronet-so module.
compile ('io.github.lizhangqu:cronet-native:73.0.3653.0.6'){
exclude group: 'io.github.lizhangqu', module: 'cronet-so'
}
And add custom library loader when init cronet.
try {
CronetEngine.Builder myBuilder = new CronetEngine.Builder(this);
myBuilder.enableHttpCache(CronetEngine.Builder.HTTP_CACHE_IN_MEMORY, 100 * 1024)
.setLibraryLoader(new ChromiumLibraryLoader(this)) //set library to such as ChromiumLibraryLoader impl
.enableHttp2(true)
.enableQuic(false);
Log.i(TAG, "setup");
CronetEngine cronetEngine = myBuilder.build();
} catch (Throwable e) {
}
You should use the httpurlconnection style api for downgrade
public HttpURLConnection createHttpURLConnection(CronetEngine cronetEngine String url) {
try {
return (HttpURLConnection) cronetEngine.openConnection(new URL(url));
} catch (Exception e) {
try {
return (HttpURLConnection) new URL(url).openConnection();
} catch (IOException ex) {
ex.printStackTrace();
}
}
return null;
}
private void sendHeadRequestByHurl() {
InputStream inputStream = null;
try {
HttpURLConnection urlConnection = createHttpURLConnection(cronetEngine, "url");
urlConnection.setDoInput(true);
urlConnection.setDoOutput(true);
urlConnection.setRequestMethod("HEAD");
urlConnection.getOutputStream().write("a=b&b=c".getBytes());
Map<String, List<String>> headerFields = urlConnection.getHeaderFields();
if (urlConnection.getResponseCode() >= HttpURLConnection.HTTP_BAD_REQUEST) {
InputStream errorStream = urlConnection.getErrorStream();
readInputStream(errorStream);
} else {
inputStream = urlConnection.getInputStream();
readInputStream(inputStream);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
NDK abiFilters
This library add all so default, if you need add only one, you should use ndk abiFilters yourself.
I suggest that you only add abiFilters "armeabi-v7a".
android {
defaultConfig {
ndk {
abiFilters "armeabi-v7a"
// default is no filters
// abiFilters "armeabi"
// abiFilters "armeabi-v7a"
// abiFilters "arm64-v8a"
// abiFilters "x86"
// abiFilters "x86_64"
// abiFilters "mips"
// abiFilters "mips64"
}
}
}
Create Engine
CronetEngine.Builder builder = new CronetEngine.Builder(context);
builder.
enableHttpCache(CronetEngine.Builder.HTTP_CACHE_IN_MEMORY,
100 * 1024) // cache
.enableHttp2(true) // Http/2.0 Supprot
.enableQuic(true) // Quic Supprot
.setHostResolver(new HostResolver() {
@Override
public List<InetAddress> resolve(String hostname) throws UnknownHostException {
if (hostname == null)
throw new UnknownHostException("hostname == null");
return Arrays.asList(InetAddress.getAllByName(hostname));
}
}) // custom dns, you can use httpdns here
.enableSDCH(true) // SDCH Supprot
.setLibraryName("cronet"); // lib so name
CronetEngine cronetEngine = builder.build();
//see more config in the code
Use For HttpUrlConnection
You can use the method like OkHttp
URL.setURLStreamHandlerFactory(new OkUrlFactory(new OkHttpClient()));
Cronet also support it.
CronetURLStreamHandlerFactory cronetURLStreamHandlerFactory = new CronetURLStreamHandlerFactory(cronetEngine);
URL.setURLStreamHandlerFactory(cronetURLStreamHandlerFactory);
And then you don't need to modify your java code like this.
try {
URL url = new URL(mEditTextUrl.getText().toString());
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
Log.e("TAG", "connection:" + connection);
connection.setDoInput(true);
connection.setConnectTimeout(10000);
connection.setReadTimeout(10000);
connection.setRequestMethod("GET");
connection.connect();
int responseCode = connection.getResponseCode();
InputStream inputStream = connection.getInputStream();
ByteArrayOutputStream output = new ByteArrayOutputStream();
copy(inputStream, output);
output.close();
inputStream.close();
byte[] bytes = output.toByteArray();
String response = new String(bytes);
Log.e("TAG", "responseCode:" + responseCode);
Log.e("TAG", "response body:" + response);
} catch (IOException e) {
e.printStackTrace();
}
public static long copy(InputStream input, OutputStream output) throws IOException {
return copyLarge(input, output, new byte[2048]);
}
public static long copyLarge(InputStream input, OutputStream output, byte[] buffer)
throws IOException {
long count = 0;
int n = 0;
while (-1 != (n = input.read(buffer))) {
output.write(buffer, 0, n);
count += n;
}
return count;
}
Attentation Please
If you use the HttpURLConnection style api, you must read the inputstream anyway.
static ByteArrayInputStream toByteArrayInputStream(InputStream inputStream) {
if (inputStream != null) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
byte[] buffer = new byte[1024];
int len = -1;
while ((len = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
}
return new ByteArrayInputStream(outputStream.toByteArray());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (outputStream != null) {
try {
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
return null;
}
static void readInputStream(InputStream inputStream) {
if (inputStream != null) {
try {
byte[] buffer = new byte[1024];
int len = -1;
while ((len = inputStream.read(buffer)) != -1) {
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
InputStream inputStream = null;
try {
inputStream = urlConnection.getInputStream();
} catch (IOException e) {
inputStream = toByteArrayInputStream(urlConnection.getErrorStream());
}
//you must read the inputStream
readInputStream(inputStream);
Send GET Request
UrlRequest.Builder builder = new UrlRequest.Builder(mEditTextUrl.getText().toString(), new UrlRequest.Callback() {
private ByteArrayOutputStream mBytesReceived = new ByteArrayOutputStream();
private WritableByteChannel mReceiveChannel = Channels.newChannel(mBytesReceived);
@Override
public void onRedirectReceived(UrlRequest urlRequest, UrlResponseInfo urlResponseInfo, String s) throws Exception {
Log.i("TAG", "onRedirectReceived");
urlRequest.followRedirect();
}
@Override
public void onResponseStarted(UrlRequest urlRequest, UrlResponseInfo urlResponseInfo) throws Exception {
Log.i("TAG", "onResponseStarted");
urlRequest.read(ByteBuffer.allocateDirect(32 * 1024));
}
@Override
public void onReadCompleted(UrlRequest urlRequest, UrlResponseInfo urlResponseInfo, ByteBuffer byteBuffer) throws Exception {
Log.i("TAG", "onReadCompleted");
byteBuffer.flip();
try {
mReceiveChannel.write(byteBuffer);
} catch (IOException e) {
e.printStackTrace();
}
byteBuffer.clear();
urlRequest.read(byteBuffer);
}
@Override
public void onSucceeded(UrlRequest urlRequest, UrlResponseInfo urlResponseInfo) {
Log.i("TAG", "onSucceeded");
Log.i("TAG", String.format("Request Completed, status code is %d, total received bytes is %d",
urlResponseInfo.getHttpStatusCode(), urlResponseInfo.getReceivedBytesCount()));
final String receivedData = mBytesReceived.toString();
final String url = urlResponseInfo.getUrl();
final String text = "Completed " + url + " (" + urlResponseInfo.getHttpStatusCode() + ")";
Log.i("TAG", "text:" + text);
Log.i("TAG", "receivedData:" + receivedData);
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "onSucceeded", Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onFailed(UrlRequest urlRequest, UrlResponseInfo urlResponseInfo, UrlRequestException e) {
Log.i("TAG", "onFailed");
Log.i("TAG", "error is: %s" + e.getMessage());
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "onFailed", Toast.LENGTH_SHORT).show();
}
});
}
}, executor, cronetEngine);
builder.build().start();
Send POST Request
public void startWithURL(String url, UrlRequest.Callback callback, Executor executor, String postData) {
UrlRequest.Builder builder = new UrlRequest.Builder(url, callback, executor, mCronetEngine);
applyPostDataToUrlRequestBuilder(builder, executor, postData);
builder.build().start();
}
private void applyPostDataToUrlRequestBuilder(
UrlRequest.Builder builder, Executor executor, String postData) {
if (postData != null && postData.length() > 0) {
builder.setHttpMethod("POST");
builder.addHeader("Content-Type", "application/x-www-form-urlencoded");
builder.setUploadDataProvider(
UploadDataProviders.create(postData.getBytes()), executor);
}
}
And then reuse the callback in Send GET Request
Thanks
- chromium-net-android-porting
- chromium-compile-guide-for-android
- lazy-chromium-net-android-porting-guide
- chromium-gn-build-tools
License
chromium net for android(cronet) is under the BSD license. See the LICENSE file for details.