三篇比较好的文章,分别简述RxJava、Retrofit2、RxJava&Retrofit2
给 Android 开发者的 RxJava 详解 作者:扔物线
Retrofit2 完全解析 探索与okhttp之间的关系 作者:Hongyang
RxJava 与 Retrofit 结合的最佳实践 作者:tough1985
这上面是我学习的时候主要看的一些博客,已经很全了,而且讲的也很好,这篇文章主要是记录开发中你可能会用到的,已经自己开发中的具体使用以及遇到的一些问题。
compile 'io.reactivex:rxandroid:1.2.0'
compile 'io.reactivex:rxjava:1.1.5'
compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
compile 'com.squareup.okhttp3:okhttp:3.3.1'
compile 'com.squareup.okio:okio:1.8.0'
compile 'com.github.orhanobut:logger:1.12'
这里我用compile 'com.github.orhanobut:logger:1.12’代替了OKhttp的compile ‘com.squareup.okhttp3:logging-interceptor:(insert latest version)’,是为了方便看JSON,需要注意的是如果你使用logging-interceptor,那么你的版本号就需要和OKhttp的版本号一致。另外logger的拦截代码在后面会给出。
第一步 缓存
private static final File cacheFile = new File(AppApplication.sInstance.getCacheDir().getAbsolutePath() + File.separator + "data" + File.separator + "NetCache");
private static final Cache cache = new Cache(cacheFile, 1024 * 1024 * 50);
private static final Interceptor cacheInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (!NetUtil.isConnected()) {
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
}
Response response = chain.proceed(request);
if (NetUtil.isConnected()) {
int maxAge = 0;
// 有网络时, 不缓存, 最大保存时长为0
response.newBuilder()
.header("Cache-Control", "public, max-age=" + maxAge)
.removeHeader("Pragma")
.build();
} else {
// 无网络时,设置超时为4周
int maxStale = 60 * 60 * 24 * 28;
response.newBuilder()
.header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
.removeHeader("Pragma")
.build();
}
return response;
}
};
第二步 创建 okHttpClient & Header 加密处理 & Request 参数获取
大部分项目都会在header里面放一些验证签名的机制,所以你就需要一个共同的OKhttp,这里直接在创建的时候 addHeader就行了。
另外这里你每次调用网络请求的时候,Okhttp的addHeader等的方法每次都会调用,所以不用担心拿到的TimeStamp不是最新的。
当然,如果不想在Retrofit的接口里面动态添加@Header,那就直接放进addHeader里面就行,没有的情况下,给一个空的字符串就行了。
private static final OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(120, TimeUnit.SECONDS)
.connectTimeout(120, TimeUnit.SECONDS)
.writeTimeout(120,TimeUnit.SECONDS)
.addNetworkInterceptor(cacheInterceptor)
.addInterceptor(cacheInterceptor)
.cache(cache)
.addInterceptor(new MyLogInterceptor().setLevel(AppApplication.debug ? MyLogInterceptor.Level.BODY : MyLogInterceptor.Level.NONE))
.addInterceptor(chain -> {
Request request = chain.request();
String sign_request_body = getOriginalSign(request);
request = request
.newBuilder()
.addHeader("Version", getHeader(VERSION, ""))
.addHeader("Timestamp", getHeader(TIMESTAMP, ""))
.addHeader("Token", getHeader(TOKEN, ""))
.addHeader("Sign", getHeader(SIGN, sign_request_body))
.build();
return chain.proceed(request);
})
.build();
Request 请求参数的获取:
private static String getOriginalSign(Request request) {
String url = request.url() + "";
String sign_request_body = "";
String method = request.method();
if ("POST".equals(method))
sign_request_body = bodyToString(request);
else {
String[] body = url.split("\\?");
if (body.length > 1)
sign_request_body = body[1];
}
return sign_request_body;
}
private static String bodyToString(final Request request) {
try {
final Request copy = request.newBuilder().build();
final Buffer buffer = new Buffer();
copy.body().writeTo(buffer);
return buffer.readUtf8();
} catch (final IOException e) {
return "did not work";
}
}
Retrofit 的创建 & Service 的创建
private static final CallAdapter.Factory rxJavaCallAdapterFactory = RxJavaCallAdapterFactory.create();
public static Retrofit getRetrofit() {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.callFactory(okHttpClient)
.baseUrl(ApiUrls.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(rxJavaCallAdapterFactory)
.build();
}
return retrofit;
}
public static <S> S createService(Class<S> serviceClass) {
return getRetrofit().create(serviceClass);
}
NetWork 的完整代码
import com.facebook.stetho.okhttp3.StethoInterceptor;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import okhttp3.Cache;
import okhttp3.CacheControl;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;
import okio.Buffer;
import retrofit2.CallAdapter;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
public class NetWork {
private static final String OKHTTP = "okhttp";
private NetWork() {
}
public static void init() {
getRetrofit();
}
private static final File cacheFile = new File(AppApplication.sInstance.getCacheDir().getAbsolutePath() + File.separator + "data" + File.separator + "NetCache");
private static final Cache cache = new Cache(cacheFile, 1024 * 1024 * 50);
private static final Interceptor cacheInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (!NetUtil.isConnected()) {
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
}
Response response = chain.proceed(request);
if (NetUtil.isConnected()) {
int maxAge = 0;
// 有网络时, 不缓存, 最大保存时长为0
response.newBuilder()
.header("Cache-Control", "public, max-age=" + maxAge)
.removeHeader("Pragma")
.build();
} else {
// 无网络时,设置超时为4周
int maxStale = 60 * 60 * 24 * 28;
response.newBuilder()
.header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
.removeHeader("Pragma")
.build();
}
return response;
}
};
private static final int VERSION = 1;
private static final int TIMESTAMP = VERSION + 1;
private static final int TOKEN = TIMESTAMP + 1;
private static final int SIGN = TOKEN + 1;
private static Retrofit retrofit;
private static final CallAdapter.Factory rxJavaCallAdapterFactory = RxJavaCallAdapterFactory.create();
private static final OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(120, TimeUnit.SECONDS)
.connectTimeout(120, TimeUnit.SECONDS)
.writeTimeout(120,TimeUnit.SECONDS)
.addNetworkInterceptor(cacheInterceptor)
.addNetworkInterceptor(new StethoInterceptor())
.addInterceptor(cacheInterceptor)
.cache(cache)
.addInterceptor(new MyLogInterceptor().setLevel(Application.debug ? MyLogInterceptor.Level.BODY : MyLogInterceptor.Level.NONE))
.addInterceptor(chain -> {
Request request = chain.request();
String sign_request_body = getOriginalSign(request);
request = request
.newBuilder()
.addHeader("Version", getHeader(VERSION, ""))
.addHeader("Timestamp", getHeader(TIMESTAMP, ""))
.addHeader("Token", getHeader(TOKEN, ""))
.addHeader("Sign", getHeader(SIGN, sign_request_body))
.build();
return chain.proceed(request);
})
.build();
private static String getOriginalSign(Request request) {
String url = request.url() + "";
String sign_request_body = "";
String method = request.method();
if ("POST".equals(method))
sign_request_body = bodyToString(request);
else {
String[] body = url.split("\\?");
if (body.length > 1)
sign_request_body = body[1];
}
// sign_request_body = sortRequestBody(sign_request_body);
return sign_request_body;
}
private static String sortRequestBody(String sign_request_body) {
TLog.d(OKHTTP + " SORTREQUESTBODY ", sign_request_body);
String[] bodys = sign_request_body.split("&");
sign_request_body = "";
if (bodys.length > 0) {
Arrays.sort(bodys);
TLog.d(OKHTTP + " sort ", Arrays.toString(bodys));
for (int i = 0; i < bodys.length; i++) {
if (!TextUtils.isEmpty(bodys[i]))
sign_request_body += bodys[i] + ":";
}
}
TLog.d(OKHTTP + " sign_request_body ", sign_request_body);
return sign_request_body;
}
private static String bodyToString(final Request request) {
try {
final Request copy = request.newBuilder().build();
final Buffer buffer = new Buffer();
copy.body().writeTo(buffer);
return buffer.readUtf8();
} catch (final IOException e) {
return "did not work";
}
}
private static String getHeader(int type, String sign_request_body) {
String s = "";
switch (type) {
case VERSION:
s = CommonUtil.getVersion();
TLog.d(OKHTTP, "header Version: " + s);
break;
case TIMESTAMP:
s = TimeUtils.getTime(System.currentTimeMillis());
TLog.d(OKHTTP, "header Timestamp: " + s);
break;
case TOKEN:
s = SPUtils.getInstance().getString(SPUtils.TOKEN,"");
// s = "77b23a227a86cdde4a29d1ace10bec34";
TLog.d(OKHTTP, "header Token: " + s);
break;
case SIGN:
s = MD5.md5(sign_request_body + "salt=" + SPUtils.getInstance().getString(SPUtils.SALT));
// TLog.d("md5 test", MD5.md5("gender=0:position=2:sort=0:type_id=1:salt=04fe9cac95").equals("9cfe7c434f0ec54b1a853731a1c7ca11") + "");
TLog.d(OKHTTP, "header Sign: " + s);
break;
}
return s;
}
public static Retrofit getRetrofit() {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.callFactory(okHttpClient)
.baseUrl(ApiUrls.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(rxJavaCallAdapterFactory)
.build();
}
return retrofit;
}
public static <S> S createService(Class<S> serviceClass) {
return getRetrofit().create(serviceClass);
}
}
Warning
这里说一个比较蠢的事情:
看下这段代码:
public static Gson buildGson() {
if (gson == null) {
gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd HH:mm:ss")
.setLenient()
.create();
}
return gson;
}
我是用的是 google 的 gson,然后出了这个错误:com.google.gson.stream.MalformedJsonException,然后我在上面代码中设置了时间的解析和规定宽松的json格式,但是还是不行,最后找出来是我为了偷懒,想着要上传文件,于是在 header里面添加了addHeader("Content-type", "gizp")
,好吧传过去倒是没什么,问题来了,返回值它默认也是这个格式,解析出来一堆乱码,坑了自己一把。引以为戒!!!
import android.util.Log;
import com.orhanobut.logger.Logger;
import java.io.EOFException;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.concurrent.TimeUnit;
import okhttp3.Connection;
import okhttp3.Headers;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.Protocol;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.internal.Platform;
import okhttp3.internal.http.HttpEngine;
import okio.Buffer;
import okio.BufferedSource;
import static okhttp3.internal.Platform.INFO;
/**
* Created by chenjiahuan on 16/6/16.
*/
public class MyLogInterceptor implements Interceptor {
private static final String TAG = "OKhttp";
private static final Charset UTF8 = Charset.forName("UTF-8");
public enum Level {
/**
* No logs.
*/
NONE,
/**
* Logs request and response lines.
*
* Example:
*
{@code
* --> POST /greeting http/1.1 (3-byte body)
*
* <-- 200 OK (22ms, 6-byte body)
* }
*/
BASIC,
/**
* Logs request and response lines and their respective headers.
*
* Example:
*
{@code
* --> POST /greeting http/1.1
* Host: example.com
* Content-Type: plain/text
* Content-Length: 3
* --> END POST
*
* <-- 200 OK (22ms)
* Content-Type: plain/text
* Content-Length: 6
* <-- END HTTP
* }
*/
HEADERS,
/**
* Logs request and response lines and their respective headers and bodies (if present).
*
* Example:
*
{@code
* --> POST /greeting http/1.1
* Host: example.com
* Content-Type: plain/text
* Content-Length: 3
*
* Hi?
* --> END GET
*
* <-- 200 OK (22ms)
* Content-Type: plain/text
* Content-Length: 6
*
* Hello!
* <-- END HTTP
* }
*/
BODY
}
public interface Logger {
void log(String message);
/**
* A {@link Logger} defaults output appropriate for the current platform.
*/
Logger DEFAULT = new Logger() {
@Override
public void log(String message) {
Platform.get().log(INFO, message, null);
}
};
}
public MyLogInterceptor() {
this(Logger.DEFAULT);
}
public MyLogInterceptor(Logger logger) {
this.logger = logger;
}
private final Logger logger;
private volatile Level level = Level.NONE;
/**
* Change the level at which this interceptor logs.
*/
public MyLogInterceptor setLevel(Level level) {
if (level == null) throw new NullPointerException("level == null. Use Level.NONE instead.");
this.level = level;
return this;
}
public Level getLevel() {
return level;
}
@Override
public Response intercept(Chain chain) throws IOException {
Level level = this.level;
Request request = chain.request();
if (level == Level.NONE) {
return chain.proceed(request);
}
boolean logBody = level == Level.BODY;
boolean logHeaders = logBody || level == Level.HEADERS;
RequestBody requestBody = request.body();
boolean hasRequestBody = requestBody != null;
Connection connection = chain.connection();
Protocol protocol = connection != null ? connection.protocol() : Protocol.HTTP_1_1;
String requestStartMessage = "--> " + request.method() + ' ' + request.url() + ' ' + protocol;
if (!logHeaders && hasRequestBody) {
requestStartMessage += " (" + requestBody.contentLength() + "-byte body)";
}
logger.log(requestStartMessage);
if (logHeaders) {
if (hasRequestBody) {
// Request body headers are only present when installed as a network interceptor. Force
// them to be included (when available) so there values are known.
if (requestBody.contentType() != null) {
logger.log("Content-Type: " + requestBody.contentType());
}
if (requestBody.contentLength() != -1) {
logger.log("Content-Length: " + requestBody.contentLength());
}
}
Headers headers = request.headers();
for (int i = 0, count = headers.size(); i < count; i++) {
String name = headers.name(i);
// Skip headers from the request body as they are explicitly logged above.
if (!"Content-Type".equalsIgnoreCase(name) && !"Content-Length".equalsIgnoreCase(name)) {
logger.log(name + ": " + headers.value(i));
}
}
if (!logBody || !hasRequestBody) {
logger.log("--> END " + request.method());
} else if (bodyEncoded(request.headers())) {
logger.log("--> END " + request.method() + " (encoded body omitted)");
} else {
Buffer buffer = new Buffer();
requestBody.writeTo(buffer);
Charset charset = UTF8;
MediaType contentType = requestBody.contentType();
if (contentType != null) {
charset = contentType.charset(UTF8);
}
logger.log("");
if (isPlaintext(buffer)) {
logger.log(buffer.readString(charset));
logger.log("--> END " + request.method()
+ " (" + requestBody.contentLength() + "-byte body)");
} else {
logger.log("--> END " + request.method() + " (binary "
+ requestBody.contentLength() + "-byte body omitted)");
}
}
}
long startNs = System.nanoTime();
Response response;
try {
response = chain.proceed(request);
} catch (Exception e) {
logger.log("<-- HTTP FAILED: " + e);
throw e;
}
long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
ResponseBody responseBody = response.body();
long contentLength = responseBody.contentLength();
String bodySize = contentLength != -1 ? contentLength + "-byte" : "unknown-length";
logger.log("<-- " + response.code() + ' ' + response.message() + ' '
+ response.request().url() + " (" + tookMs + "ms" + (!logHeaders ? ", "
+ bodySize + " body" : "") + ')');
if (logHeaders) {
Headers headers = response.headers();
for (int i = 0, count = headers.size(); i < count; i++) {
logger.log(headers.name(i) + ": " + headers.value(i));
}
if (!logBody || !HttpEngine.hasBody(response)) {
logger.log("<-- END HTTP");
} else if (bodyEncoded(response.headers())) {
logger.log("<-- END HTTP (encoded body omitted)");
} else {
BufferedSource source = responseBody.source();
source.request(Long.MAX_VALUE); // Buffer the entire body.
Buffer buffer = source.buffer();
Charset charset = UTF8;
MediaType contentType = responseBody.contentType();
if (contentType != null) {
try {
charset = contentType.charset(UTF8);
} catch (UnsupportedCharsetException e) {
logger.log("");
logger.log("Couldn't decode the response body; charset is likely malformed.");
logger.log("<-- END HTTP");
return response;
}
}
if (!isPlaintext(buffer)) {
logger.log("");
logger.log("<-- END HTTP (binary " + buffer.size() + "-byte body omitted)");
return response;
}
if (contentLength != 0) {
logger.log("");
logger.log(buffer.clone().readString(charset));
com.orhanobut.logger.Logger.json(buffer.clone().readString(charset));
}
logger.log("<-- END HTTP (" + buffer.size() + "-byte body)");
}
}
return response;
}
/**
* Returns true if the body in question probably contains human readable text. Uses a small sample
* of code points to detect unicode control characters commonly used in binary file signatures.
*/
static boolean isPlaintext(Buffer buffer) throws EOFException {
try {
Buffer prefix = new Buffer();
long byteCount = buffer.size() < 64 ? buffer.size() : 64;
buffer.copyTo(prefix, 0, byteCount);
for (int i = 0; i < 16; i++) {
if (prefix.exhausted()) {
break;
}
int codePoint = prefix.readUtf8CodePoint();
if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
return false;
}
}
return true;
} catch (EOFException e) {
return false; // Truncated UTF-8 sequence.
}
}
private boolean bodyEncoded(Headers headers) {
String contentEncoding = headers.get("Content-Encoding");
return contentEncoding != null && !contentEncoding.equalsIgnoreCase("identity");
}
}
这个log的代码全部都是COPY的 HttpLoggingInterceptor这个类的代码,大家可以把我上面的代码copy下来,在260行我添加了Logger的代码,当然最好也保留原来的打印,原因很简单,Logger.json()这个方法只会打印json,如果是错误的字符串,就没有log了。
至此,使用前的准备步骤完毕:创建&扩展&调试Log,接下来,就是使用了,当然,是经过封装加工过的
HttpResult的定义 & SuccessCallBack & ErrorCallBack
Json的开发标准而言,基本格式如下:
{
“status”: // 接口访问成功或者失败的状态码
“message”:// 接口访问错误的时候返回的错误提示文字,访问成功的时候为空字符串
“result”:{ // 服务端实际返回的数据
}
}
这里我们定义泛型,不然用的时候太累赘了
HttpResult
public class HttpResult<T> {
public int code;
public String message;
public T result;
}
ISuccessCallBack
public interface ISuccessCallBack<T> {
void success(T data);
}
NetUtil
import android.app.Activity;
import android.app.Application;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import com.google.gson.JsonSyntaxException;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
public class NetUtil {
public static void checkHttpException(Throwable mThrowable) {
if (!NetUtil.isConnected()) {
ToastUtil.showToast("没有网络");
return;
}
if ((mThrowable instanceof UnknownHostException)) {
ToastUtil.showToast("网络异常");
} else if (mThrowable instanceof JsonSyntaxException) {
ToastUtil.showToast("数据异常");
} else if (mThrowable instanceof SocketTimeoutException) {
ToastUtil.showToast("连接超时");
} else if (mThrowable instanceof ConnectException) {
ToastUtil.showToast("连接服务器失败");
} else if (mThrowable instanceof TipException) {
ToastUtil.showToast(((TipException) mThrowable).tip);
} else {
ToastUtil.showToast("操作失败");
}/* else if (mThrowable instanceof RuntimeException) {
ToastUtil.showToast(mContext,"程序出错");
} else {
ToastUtil.showToast(mContext,"网络异常");
}*/
TLog.e("okhttp", mThrowable.toString());
}
}
第一步 创建接口
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Query;
import rx.Observable;
public interface CommonApi {
/**
* 获取新版本
* @param version
* @return
*/
@GET(ApiUrls.CORE_APPVERSION_GETVERSION)
Observable<HttpResult<UpdateAppBean>> getNewVersion(@Query("version") String version);
/**
* 上报地理位置信息
* @param longitude
* @param latitude
* @return
*/
@FormUrlEncoded
@POST(ApiUrls.COMMON_LOCATION_REPORT)
Observable<HttpResult> updateLocation(@Field("longitude") String longitude,
@Field("latitude") String latitude);
}
第二步 调用接口
public class CommonApiHelper {
private static final CommonApi commonApi = NetWork.createService(CommonApi.class);
public void getVersion(String version, ISuccessCallBack callBack){
commonApi.getNewVersion(CommonUtil.getVersion())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(updateAppBean ->
callBack.success(updateAppBean)
, NetUtil::checkHttpException));
}
}
基本使用就到这里吧,对于RxJava和Retrofit的基本使用我就不介绍了,网上一搜一片,而且开篇我就介绍了很好的文章,很详细了,接下来是封装的部分了。
在开发时,定然需要对服务器返回的某个值做特殊处理,或者是退出,或者是补全个人信息,大部分时候很多接口都需要做相同的处理,于是,统一的回调就显得尤为重要,不单单是减少代码量的问题,也需要考虑维护的成本,以及为了优雅的编码~
HttpFun
public class HttpFunc<T> implements Func1<HttpResult<T>, T> {
@Override
public T call(HttpResult<T> httpResult) {
if (httpResult.code == HttpResult.SUCCESS) {
return httpResult.result;
} else if (httpResult.code == ApiErrorCode.FORCE_UPDATE) {
EventBus.getDefault().post(new ForceUpdateEvent());
throw new TipException("请更新app后操作");
} else {
throw new TipException(httpResult.message);
}
}
}
上面的代码是一个例子,HttpFun 继承自 RxJava 的 Func1,在 call() 方法中,对特殊的CODE做特殊的处理。
HttpFun 的实际使用
就以之前的简单使用为例:
public void getVersion(String version, ISuccessCallBack callBack){
commonApi.getNewVersion(CommonUtil.getVersion())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(new HttpFunc<>())
.subscribe(updateAppBean ->
callBack.success(updateAppBean)
, NetUtil::checkHttpException));
}
所有的请求都需要调用 map(new HttpFunc<>()),那么,必然就会有统一的处理。
RxUtil
就上面而言,统一的处理已经完成,然而,代码还有优化的余地,所有的请求都会有这三行代码:
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(new HttpFunc<>())
这个时候,就是RxJava大显神威的时候了,尽管你可以自定义方法,把Observeable传进去,但是那样就打断了流式API,工具类 :
public class RxUtil {
public interface OnClick {
void onClick();
}
/**
* 统一线程处理
*
* @param
* @return
*/
public static <T> Observable.Transformer<T, T> rxSchedulerHelper() { //compose简化线程
return observable -> observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.unsubscribeOn(Schedulers.io());
}
public static <T> Observable.Transformer<HttpResult, T> applySchedulers() { //compose简化线程
return observable -> observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(new HttpFunc())
.unsubscribeOn(Schedulers.io());
}
}
于是上面的请求就变成了:
public void getVersion(String version, ISuccessCallBack callBack){
commonApi.getNewVersion(CommonUtil.getVersion())
.compose(RxUtil.<T>applySchedulers())
.subscribe(updateAppBean ->
callBack.success(updateAppBean)
, NetUtil::checkHttpException));
}
从RxJava + Retrofit 的使用上而言,其实这篇文章该结束了,但是想了想,还是把一个参数传递的类贡献出来,我个人觉得方便了,见仁见智吧。
我们正常开发的时候,有 get/post,有普通的请求,有上传的操作,就我个人而言还是挺乱的,不管怎么先把代码贴出来:
import android.text.TextUtils;
import java.io.File;
import java.net.FileNameMap;
import java.net.URLConnection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import okhttp3.MediaType;
import okhttp3.RequestBody;
public class FileMapHelper {
private static final String TAG = "fileMapHelper";
private Map<String, RequestBody> map;
public FileMapHelper() {
this.map = new TreeMap<>(new FileComparator());
}
public Map<String, RequestBody> getMap() {
return map;
}
public void putText(String key, String value) {
if (!TextUtils.isEmpty(value)) {
RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain"), value);
map.put(key, requestBody);
}
}
public void putAudio(String key, String path) {
File file = new File(path);
FileNameMap fileNameMap = URLConnection.getFileNameMap();
String contentTypeFor = fileNameMap.getContentTypeFor(path);
RequestBody requestBody =
RequestBody.create(MediaType.parse(contentTypeFor), file);
map.put(key + "\";filename=\"" + file.getName(), requestBody);
}
public void putPic(String key, List<File> fileList) {
for (int i = 0; i < fileList.size(); i++) {
RequestBody requestBody = RequestBody.create(MediaType.parse("image/jpg"), fileList.get(i));
map.put(key + "[" + i + "]" + "\";filename=\"" + fileList.get(i).getName(), requestBody);
}
}
public void putSinglePic(String key, File file) {
RequestBody requestBody = RequestBody.create(MediaType.parse("image/jpg"), file);
map.put(key + "\";filename=\"" + file.getName(), requestBody);
}
public void putGif(String key, File file) {
RequestBody requestBody = RequestBody.create(MediaType.parse("image/gif"), file);
map.put(key + "\";filename=\"" + file.getName(), requestBody);
}
public void putVideo(String key, String path) {
File file = new File(path);
RequestBody requestBody = RequestBody.create(MediaType.parse("video/mp4"), file);
map.put(key + "\";filename=\"" + file.getName(), requestBody);
}
class FileComparator implements Comparator<String> {
@Override
public int compare(String lhs, String rhs) {
if (lhs.contains("image") && rhs.contains("image")) {
int start = lhs.indexOf("[");
int end = lhs.indexOf("]", start);
String substring = lhs.substring(start + 1, end);
String substring1 = rhs.substring(start + 1, end);
if (isNumeric(substring)) {
Integer integer = Integer.valueOf(substring);
Integer integer1 = Integer.valueOf(substring1);
return integer - integer1;
}
}
return -1;
}
private boolean isNumeric(String str) {
for (int i = str.length(); --i >= 0; ) {
if (!Character.isDigit(str.charAt(i))) {
return false;
}
}
return true;
}
}
}
我现在使用的代码就变成了这样:
Interface
public interface ChatApi {
/**
* 聊天里发送视频
* video binary 视频
* image binary 视频封面
*
* @return
*/
@Multipart
@POST(ApiUrls.CHAT_UPLOAD_VIDE)
Observable<HttpResult<ChatVideoResult>> uploadChatVideo(@PartMap Map<String, RequestBody> map);
/**
* 聊天里发送图片
* video binary 视频
* image binary 视频封面
*
* @return
*/
@Multipart
@POST(ApiUrls.CHAT_UPLOAD_IMAGE)
Observable<HttpResult<ChatImageResult>> uploadChatImage(@PartMap Map<String, RequestBody> map);
/**
* 漂流瓶-随机获取一个用户的环信id
*
* @return
*/
@FormUrlEncoded
@GET(ApiUrls.DRIFTING_BOTTLE_THORW)
Observable<HttpResult<DriftBottleResult>> throwBottle();
@Multipart
@POST(ApiUrls.CHAT_UPLOAD_AUDIO)
Observable<HttpResult<ChatAudioInfo>> uploadChatAudio(@PartMap Map<String, RequestBody> map);
}
Helper
public class ChatApiHelper {
private static final ChatApi chatApi = NetWork.createService(ChatApi.class);
public static void uploadChatVideo(String video_key, String video_path, String image_key, String image_path, ISuccessCallBack<ChatVideoResult> iSuccessCallBack) {
FileMapHelper fileMapHelper = new FileMapHelper();
fileMapHelper.putVideo(video_key, video_path);
fileMapHelper.putSinglePic(image_key, new File(image_path));
chatApi.uploadChatVideo(fileMapHelper.getMap())
.compose(RxUtil.<ChatVideoResult>applySchedulers())
.subscribe(chatVideoResult -> iSuccessCallBack.success(chatVideoResult), NetUtil::checkHttpException);
}
public static void uploadChatImage(String key, String path, ISuccessCallBack<ChatImageResult> iSuccessCallBack) {
FileMapHelper fileMapHelper = new FileMapHelper();
fileMapHelper.putSinglePic(key, new File(path));
chatApi.uploadChatImage(fileMapHelper.getMap())
.compose(RxUtil.<ChatImageResult>applySchedulers())
.subscribe(chatImageResult -> iSuccessCallBack.success(chatImageResult), NetUtil::checkHttpException);
}
public static void uploadChatAudio(String key, String path, ISuccessCallBack<ChatAudioInfo> iSuccessCallBack) {
FileMapHelper fileMapHelper = new FileMapHelper();
fileMapHelper.putAudio(key, path);
chatApi.uploadChatAudio(fileMapHelper.getMap())
.compose(RxUtil.<ChatAudioInfo>applySchedulers())
.subscribe(chatAudioInfo -> iSuccessCallBack.success(chatAudioInfo), NetUtil::checkHttpException);
}
public static void getRandomHXId(ISuccessCallBack<DriftBottleResult> iSuccessCallBack) {
chatApi.throwBottle()
.compose(RxUtil.<DriftBottleResult>applySchedulers())
.subscribe(driftbottleResult -> iSuccessCallBack.success(driftbottleResult), NetUtil::checkHttpException);
}
}
上面的例子中基本都是要么纯上传,要么就是纯字段(或者没有字段),当然也可以同时都有,只需要直接调用方法就行了。
结束语:
花了一天的时间把之前的博客推倒重来,一个是补充,其实主要的就是,之前写的博客写的太乱了,完全不知道在讲什么,至少我现在能看懂这篇文章在说什么了。当然,也有缺陷,在阅读文章之前,你需要有 lamda 和 RxJava 的基础,不然,代码读起来有点懵,希望对大家有所帮助。