Use internal library for networking

This commit is contained in:
topjohnwu
2018-12-12 05:51:45 -05:00
parent eab74ef06b
commit 59b1e63bdf
29 changed files with 552 additions and 365 deletions

1
net/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

29
net/build.gradle Normal file
View File

@@ -0,0 +1,29 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
minSdkVersion 16
targetSdkVersion 28
versionCode 1
versionName "1.0"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
}

21
net/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.topjohnwu.net" />

View File

@@ -0,0 +1,5 @@
package com.topjohnwu.net;
public interface DownloadProgressListener {
void onProgress(long bytesDownloaded, long totalBytes);
}

View File

@@ -0,0 +1,7 @@
package com.topjohnwu.net;
import java.net.HttpURLConnection;
public interface ErrorHandler {
void onError(HttpURLConnection conn, Exception e);
}

View File

@@ -0,0 +1,33 @@
package com.topjohnwu.net;
import android.os.Handler;
import android.os.Looper;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutorService;
public class Networking {
private static final int READ_TIMEOUT = 15000;
private static final int CONNECT_TIMEOUT = 15000;
static Handler mainHandler = new Handler(Looper.getMainLooper());
private static Request request(String url, String method) {
try {
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setRequestMethod(method);
conn.setReadTimeout(READ_TIMEOUT);
conn.setConnectTimeout(CONNECT_TIMEOUT);
return new Request(conn);
} catch (IOException e) {
return new StubRequest();
}
}
public static Request get(String url) {
return request(url, "GET");
}
}

View File

@@ -0,0 +1,47 @@
package com.topjohnwu.net;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
public class ProgressInputStream extends FilterInputStream {
private long totalBytes;
private long bytesDownloaded;
public ProgressInputStream(InputStream in, long total) {
super(in);
totalBytes = total;
}
protected void updateProgress(long bytesDownloaded, long totalBytes) {}
private void update() {
Networking.mainHandler.post(() -> updateProgress(bytesDownloaded, totalBytes));
}
@Override
public int read() throws IOException {
int b = super.read();
if (b >= 0) {
bytesDownloaded++;
update();
}
return b;
}
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int sz = super.read(b, off, len);
if (sz > 0) {
bytesDownloaded += sz;
update();
}
return sz;
}
}

View File

@@ -0,0 +1,193 @@
package com.topjohnwu.net;
import android.os.AsyncTask;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.util.concurrent.Executor;
public class Request {
private HttpURLConnection conn;
private Executor executor = null;
private DownloadProgressListener progress = null;
private ErrorHandler err = null;
private int code;
private interface Requestor<T> {
T request() throws Exception;
}
public class Result<T> {
T result;
public T getResult() {
return result;
}
public int getCode() {
return code;
}
public HttpURLConnection getConnection() {
return conn;
}
}
Request(HttpURLConnection c) {
conn = c;
}
public Request addHeaders(String key, String value) {
conn.setRequestProperty(key, value);
return this;
}
public Request setDownloadProgressListener(DownloadProgressListener listener) {
progress = listener;
return this;
}
public Request setErrorHandler(ErrorHandler handler) {
err = handler;
return this;
}
public Request setExecutor(Executor e) {
executor = e;
return this;
}
public Result<InputStream> execForInputStream() {
return exec(this::getInputStream);
}
public void getAsFile(ResponseListener<File> rs, File out) {
submit(() -> dlFile(out), rs);
}
public void execForFile(File out) {
exec(() -> dlFile(out));
}
public void getAsString(ResponseListener<String> rs) {
submit(this::dlString, rs);
}
public Result<String> execForString() {
return exec(this::dlString);
}
public void getAsJSONObject(ResponseListener<JSONObject> rs) {
submit(this::dlJSONObject, rs);
}
public Result<JSONObject> execForJSONObject() {
return exec(this::dlJSONObject);
}
public void getAsJSONArray(ResponseListener<JSONArray> rs) {
submit(this::dlJSONArray, rs);
}
public Result<JSONArray> execForJSONArray() {
return exec(this::dlJSONArray);
}
private <T> Result<T> exec(Requestor<T> req) {
Result<T> res = new Result<>();
try {
res.result = req.request();
} catch (Exception ignored) {}
return res;
}
private <T> void submit(Requestor<T> req, ResponseListener<T> rs) {
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
try {
T t = req.request();
Runnable cb = () -> rs.onResponse(t);
if (executor == null)
Networking.mainHandler.post(cb);
else
executor.execute(cb);
} catch (Exception e) {
if (err != null)
err.onError(conn, e);
}
});
}
private BufferedInputStream getInputStream() throws IOException {
conn.connect();
code = conn.getResponseCode();
InputStream in = conn.getInputStream();
if (progress != null) {
in = new ProgressInputStream(in, conn.getContentLength()) {
@Override
protected void updateProgress(long bytesDownloaded, long totalBytes) {
progress.onProgress(bytesDownloaded, totalBytes);
}
@Override
public void close() throws IOException {
super.close();
conn.disconnect();
}
};
} else {
in = new FilterInputStream(in) {
@Override
public void close() throws IOException {
super.close();
conn.disconnect();
}
};
}
return new BufferedInputStream(in);
}
private String dlString() throws IOException {
StringBuilder builder = new StringBuilder();
try (Reader reader = new InputStreamReader(getInputStream())) {
int len;
char buf[] = new char[4096];
while ((len = reader.read(buf)) != -1) {
builder.append(buf, 0, len);
}
}
return builder.toString();
}
private JSONObject dlJSONObject() throws IOException, JSONException {
return new JSONObject(dlString());
}
private JSONArray dlJSONArray() throws IOException, JSONException {
return new JSONArray(dlString());
}
private File dlFile(File f) throws IOException {
try (InputStream in = getInputStream();
OutputStream out = new BufferedOutputStream(new FileOutputStream(f))) {
int len;
byte buf[] = new byte[4096];
while ((len = in.read(buf)) != -1) {
out.write(buf, 0, len);
}
}
return f;
}
}

View File

@@ -0,0 +1,5 @@
package com.topjohnwu.net;
public interface ResponseListener<T> {
void onResponse(T response);
}

View File

@@ -0,0 +1,49 @@
package com.topjohnwu.net;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.File;
import java.io.InputStream;
import java.util.concurrent.Executor;
class StubRequest extends Request {
StubRequest() { super(null); }
@Override
public Request addHeaders(String key, String value) { return this; }
@Override
public Request setDownloadProgressListener(DownloadProgressListener listener) { return this; }
@Override
public Request setErrorHandler(ErrorHandler handler) { return this; }
@Override
public Request setExecutor(Executor e) { return this; }
@Override
public Result<InputStream> execForInputStream() { return new Result<>(); }
@Override
public void getAsFile(ResponseListener<File> rs, File out) {}
@Override
public void getAsString(ResponseListener<String> rs) {}
@Override
public Result<String> execForString() { return new Result<>(); }
@Override
public void getAsJSONObject(ResponseListener<JSONObject> rs){}
@Override
public Result<JSONObject> execForJSONObject() { return new Result<>(); }
@Override
public void getAsJSONArray(ResponseListener<JSONArray> rs){}
@Override
public Result<JSONArray> execForJSONArray() { return new Result<>(); }
}