From 388c839cf49bc9e6f61604ccc8fcf265d0ebc5d3 Mon Sep 17 00:00:00 2001 From: heyongrui Date: Wed, 22 Apr 2020 12:06:32 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/heyongrui/base/utils/UiUtil.java | 31 +++-- .../heyongrui/base/utils/UpdateCheckUtil.java | 83 ++++++++----- .../module/data/service/KaiYanService.java | 8 ++ .../network/configure/FactoryException.java | 8 +- .../network/configure/ResponseDisposable.java | 1 + .../network/configure/ResponseSubscriber.java | 110 ++++++++++++++++++ .../heyongrui/network/configure/RxHelper.java | 64 ++++++++++ .../convert/CustomConverterFactory.java | 59 ++++++++++ .../network/convert/CustomIOException.java | 41 +++++++ .../convert/CustomRequestBodyConverter.java | 43 +++++++ .../convert/CustomResponseBodyConverter.java | 69 +++++++++++ .../heyongrui/network/core/CoreHeader.java | 4 + .../heyongrui/network/core/CoreResponse.java | 6 +- .../com/heyongrui/network/core/Optional.java | 37 ++++++ .../network/interceptor/HttpLogger.java | 85 ++++++++++++-- .../heyongrui/network/service/ApiService.java | 19 +-- 16 files changed, 604 insertions(+), 64 deletions(-) create mode 100644 network/src/main/java/com/heyongrui/network/configure/ResponseSubscriber.java create mode 100644 network/src/main/java/com/heyongrui/network/convert/CustomConverterFactory.java create mode 100644 network/src/main/java/com/heyongrui/network/convert/CustomIOException.java create mode 100644 network/src/main/java/com/heyongrui/network/convert/CustomRequestBodyConverter.java create mode 100644 network/src/main/java/com/heyongrui/network/convert/CustomResponseBodyConverter.java create mode 100644 network/src/main/java/com/heyongrui/network/core/Optional.java diff --git a/base/src/main/java/com/heyongrui/base/utils/UiUtil.java b/base/src/main/java/com/heyongrui/base/utils/UiUtil.java index d8153d0..920fcf9 100644 --- a/base/src/main/java/com/heyongrui/base/utils/UiUtil.java +++ b/base/src/main/java/com/heyongrui/base/utils/UiUtil.java @@ -12,6 +12,7 @@ import android.graphics.drawable.RippleDrawable; import android.graphics.drawable.StateListDrawable; import android.os.Build; +import android.os.SystemClock; import android.text.TextUtils; import android.view.View; import android.view.ViewGroup; @@ -292,19 +293,29 @@ public static void setOnclickFeedBack(@ColorInt int normalColor, @ColorInt int p } } - private static long lastClickTime;//标识最后一次点击的时间 + private static long[] mHits = new long[2]; + private static String mLastMark; /** - * 是否可以执行点击事件(防止按钮多次点击导致事件重复的方法判断) + * 每两次点击时间间隔在一定范围{@intervals}内,且点击达到指定次数{@clickCount}视为一次有效点击(适用于频繁点击只响应一次事件或者特殊需求) + * + * @return true-此次为有效点击 false-此次为无效点击 */ - public static boolean isPerformClickEvent() { - boolean flag = false; - long curClickTime = System.currentTimeMillis(); - if ((curClickTime - lastClickTime) >= 1000) { - //两次点击按钮之间的点击间隔不能少于1000毫秒 - flag = true; + public static boolean isMultipleClickValid(int clickCount, long intervals) { + StackTraceElement ste = new Throwable().getStackTrace()[1]; + String key = ste.getFileName() + ste.getLineNumber(); + + if (!TextUtils.equals(mLastMark, key)) { + mHits = new long[clickCount]; + } + mLastMark = key; + + System.arraycopy(mHits, 1, mHits, 0, mHits.length - 1); + mHits[mHits.length - 1] = SystemClock.uptimeMillis(); + + if ((mHits[mHits.length - 1] - mHits[0] <= intervals)) { + return true; } - lastClickTime = curClickTime; - return flag; + return false; } } diff --git a/base/src/main/java/com/heyongrui/base/utils/UpdateCheckUtil.java b/base/src/main/java/com/heyongrui/base/utils/UpdateCheckUtil.java index b8fcf00..d4c0d54 100644 --- a/base/src/main/java/com/heyongrui/base/utils/UpdateCheckUtil.java +++ b/base/src/main/java/com/heyongrui/base/utils/UpdateCheckUtil.java @@ -45,19 +45,24 @@ import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.schedulers.Schedulers; +import okhttp3.CacheControl; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; public class UpdateCheckUtil { + + private final String TAG = "UpdateCheckUtil"; public static final int REQUEST_CODE_APP_INSTALL = 1000; private AppCompatActivity mActivity; private CompositeDisposable mCompositeDisposable; - private NotificationManager notificationManager; - private NotificationCompat.Builder builder; - private ProgressDialog progressDialog; + private NotificationManager mNotificationManager; + private NotificationCompat.Builder mBuilder; + private ProgressDialog mProgressDialog; + private boolean mIsDownLoadSucess; + private int NOTIFY_ID = 0x3; public UpdateCheckUtil(AppCompatActivity activity) { mActivity = activity; @@ -179,12 +184,14 @@ public void onNo(Dialog dialog) { } private void startDownload(@NonNull String downloadUrl, @NonNull String authority, @NonNull String appName) { + mIsDownLoadSucess = false; initNotification(); mCompositeDisposable.add(Observable.create((ObservableOnSubscribe) emitter -> { InputStream inputStream = null; OutputStream outputStream = null; OkHttpClient client = new OkHttpClient(); - Request request = new Request.Builder().url(downloadUrl).build(); + CacheControl cc = new CacheControl.Builder().noStore().build(); + Request request = new Request.Builder().cacheControl(cc).url(downloadUrl).build(); try { Response response = client.newCall(request).execute(); if (response.isSuccessful()) { @@ -204,14 +211,15 @@ private void startDownload(@NonNull String downloadUrl, @NonNull String authorit if (progress != progress2) { progress2 = progress; // emitter.onNext(progress2); - builder.setProgress(100, progress2, false); - builder.setContentText(mActivity.getString(R.string.download_progress, progress2)); + mBuilder.setProgress(100, progress2, false); + mBuilder.setContentText(mActivity.getString(R.string.download_progress, progress2)); if (progress2 == 100) { - builder.setContentInfo(mActivity.getString(R.string.download_done)) + mIsDownLoadSucess = true; + mBuilder.setContentInfo(mActivity.getString(R.string.download_done)) .setContentTitle(mActivity.getString(R.string.download_done)); } - notificationManager.notify(0x3, builder.build()); - progressDialog.setProgress(progress2); + mNotificationManager.notify(NOTIFY_ID, mBuilder.build()); + mProgressDialog.setProgress(progress2); Log.v("startDownload: ", progress2 + "%"); } outputStream.write(data, 0, count); @@ -242,9 +250,14 @@ private void startDownload(@NonNull String downloadUrl, @NonNull String authorit //更新通知栏的下载进度 }, throwable -> {//onError cancelTask(); + ToastUtils.showShort(R.string.update_error); }, () -> {//onComplete - progressDialog.dismiss(); - installApp(mActivity, authority, appName); + cancelNotifyAndDialog(); + if (mIsDownLoadSucess) { + installApp(mActivity, authority, appName); + } else { + ToastUtils.showShort(R.string.update_error); + } })); } @@ -252,42 +265,52 @@ private void initNotification() { //初始化通知栏 String channelId = "channell_id1"; String channelName = "下载通知"; - if (notificationManager == null) { - notificationManager = (NotificationManager) mActivity.getSystemService(Activity.NOTIFICATION_SERVICE); + if (mNotificationManager == null) { + mNotificationManager = (NotificationManager) mActivity.getSystemService(Activity.NOTIFICATION_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - notificationManager.createNotificationChannel(new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT)); + mNotificationManager.createNotificationChannel(new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_LOW)); } } - if (builder == null) { - builder = new NotificationCompat.Builder(mActivity, channelId); + if (mBuilder == null) { + mBuilder = new NotificationCompat.Builder(mActivity, channelId); } //设置通知栏属性 - builder.setSmallIcon(R.drawable.ic_launcher) + mBuilder.setSmallIcon(R.drawable.ic_launcher) .setAutoCancel(true) .setOngoing(false) .setContentInfo(mActivity.getString(R.string.downloading)) .setContentTitle(mActivity.getString(R.string.doing_download)); //初始化下载进度条弹窗 - if (progressDialog == null) { - progressDialog = new ProgressDialog(mActivity); + if (mProgressDialog == null) { + mProgressDialog = new ProgressDialog(mActivity); } - progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);// 设置水平进度条 - progressDialog.setCancelable(false);// 设置是否可以通过点击Back键取消 - progressDialog.setCanceledOnTouchOutside(false);// 设置在点击Dialog外是否取消Dialog进度条 - progressDialog.setTitle(R.string.downloading); - progressDialog.setMax(100); - progressDialog.setProgress(0); - progressDialog.setOnCancelListener(dialogInterface -> cancelTask()); - progressDialog.show(); + mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);// 设置水平进度条 + mProgressDialog.setCancelable(false);// 设置是否可以通过点击Back键取消 + mProgressDialog.setCanceledOnTouchOutside(false);// 设置在点击Dialog外是否取消Dialog进度条 + mProgressDialog.setTitle(R.string.downloading); + mProgressDialog.setMax(100); + mProgressDialog.setProgress(0); + mProgressDialog.setOnCancelListener(dialogInterface -> cancelTask()); + mProgressDialog.show(); } public void cancelTask() { - //取消任务 + cancelDisposable(); + cancelNotifyAndDialog(); + } + + private void cancelDisposable() { if (mCompositeDisposable != null && !mCompositeDisposable.isDisposed()) { mCompositeDisposable.clear(); } - if (progressDialog != null && progressDialog.isShowing()) { - progressDialog.dismiss(); + } + + private void cancelNotifyAndDialog() { + if (mProgressDialog != null && mProgressDialog.isShowing()) { + mProgressDialog.dismiss(); + } + if (null != mNotificationManager) { + mNotificationManager.cancel(NOTIFY_ID); } } diff --git a/module/src/main/java/com/heyongrui/module/data/service/KaiYanService.java b/module/src/main/java/com/heyongrui/module/data/service/KaiYanService.java index 644db7e..40e48be 100644 --- a/module/src/main/java/com/heyongrui/module/data/service/KaiYanService.java +++ b/module/src/main/java/com/heyongrui/module/data/service/KaiYanService.java @@ -47,4 +47,12 @@ public Observable getRelatedRecommend(int id) { .getRelatedRecommend(id) .compose(RxHelper.rxSchedulerHelper()); } + +// public Flowable> contractExpire(int id) { +// return ApiService.createApi(KaiYanApi.class, "", CustomConverterFactory.create()) +// .getRelatedRecommend(id) +// .compose(RxHelper.rxSchedulerHelperFlowable()) +// .compose(RxHelper.rxHandleResultFlowableNullable()); +// } + } diff --git a/network/src/main/java/com/heyongrui/network/configure/FactoryException.java b/network/src/main/java/com/heyongrui/network/configure/FactoryException.java index b789c80..63cd6fc 100644 --- a/network/src/main/java/com/heyongrui/network/configure/FactoryException.java +++ b/network/src/main/java/com/heyongrui/network/configure/FactoryException.java @@ -3,7 +3,9 @@ import com.blankj.utilcode.util.NetworkUtils; import com.google.gson.JsonParseException; import com.google.gson.JsonSyntaxException; +import com.heyongrui.network.convert.CustomIOException; import com.heyongrui.network.core.CoreApiException; +import com.heyongrui.network.core.CoreHeader; import org.json.JSONException; import org.json.JSONObject; @@ -53,8 +55,8 @@ public static CoreApiException analysisExcetpion(Throwable e) { try { String errorBody = ((HttpException) e).response().errorBody().string(); JSONObject jsonObject = new JSONObject(errorBody); - int status = jsonObject.getInt("status"); - String info = jsonObject.getString("info"); + int status = jsonObject.getInt(CoreHeader.KEY_STATUS); + String info = jsonObject.getString(CoreHeader.KEY_MSG); apiException = new CoreApiException(status, info); } catch (IOException ex) { ex.printStackTrace(); @@ -63,6 +65,8 @@ public static CoreApiException analysisExcetpion(Throwable e) { ex.printStackTrace(); apiException = new CoreApiException(e, TIMEOUT_ERROR, "解析错误"); } + } else if (e instanceof CustomIOException) { + apiException = new CoreApiException(e.getCause(), ((CustomIOException) e).getErrorCode(), ((CustomIOException) e).getMsg()); } else if (e instanceof UnknownHostException) { //主机挂了,也就是你服务器关了 apiException = new CoreApiException(e, UNKOWNHOST_ERROR, "服务异常"); diff --git a/network/src/main/java/com/heyongrui/network/configure/ResponseDisposable.java b/network/src/main/java/com/heyongrui/network/configure/ResponseDisposable.java index a76100b..14901a6 100644 --- a/network/src/main/java/com/heyongrui/network/configure/ResponseDisposable.java +++ b/network/src/main/java/com/heyongrui/network/configure/ResponseDisposable.java @@ -14,6 +14,7 @@ /** * Created by lambert on 2018/10/23. + * 适用于Observable */ public abstract class ResponseDisposable extends DisposableObserver { diff --git a/network/src/main/java/com/heyongrui/network/configure/ResponseSubscriber.java b/network/src/main/java/com/heyongrui/network/configure/ResponseSubscriber.java new file mode 100644 index 0000000..cd3b41f --- /dev/null +++ b/network/src/main/java/com/heyongrui/network/configure/ResponseSubscriber.java @@ -0,0 +1,110 @@ +package com.heyongrui.network.configure; + +import android.app.Dialog; +import android.content.Context; +import android.text.TextUtils; + +import com.blankj.utilcode.util.SPUtils; +import com.heyongrui.base.widget.catloadingview.CatLoadingDialog; +import com.heyongrui.network.core.CoreApiException; + +import java.lang.ref.SoftReference; + +import io.reactivex.subscribers.DisposableSubscriber; + + +/** + * Created by lambert on 2018/10/23. + * 适用于Flowable + */ +public abstract class ResponseSubscriber extends DisposableSubscriber { + + private SoftReference mContext; + private Dialog mLoadingDialog; + + //调用此无参构造函数无网络加载框 + public ResponseSubscriber(Context context) { + this(context, false); + } + + public ResponseSubscriber(Context context, boolean isShowLoading) { + this(context, isShowLoading, null); + } + + public ResponseSubscriber(Context context, boolean isShowLoading, String loadingContent) { + if (context != null) { + mContext = new SoftReference(context); + if (isShowLoading) { + loadingContent = TextUtils.isEmpty(loadingContent) ? "" : loadingContent; + showDialog(loadingContent); + } + } + } + + @Override + protected void onStart() { + super.onStart(); + } + + @Override + public void onNext(T t) { + dismissDialog(); + onSuccess(t); + } + + @Override + public void onError(Throwable e) { + unsubscribe(); + dismissDialog(); + //统一处理错误异常 + CoreApiException coreApiException = FactoryException.analysisExcetpion(e); + //用户登录失效,清除本地用户信息 + int code = coreApiException.getCode(); + if (code == 1105) { + SPUtils spUtils = SPUtils.getInstance("http_request"); + spUtils.put("http_request_auth_token", ""); + } + //返回错误信息 + onFailure(coreApiException.getCode(), coreApiException.getDisplayMessage()); + } + + @Override + public void onComplete() { + unsubscribe(); + dismissDialog(); + } + + protected abstract void onSuccess(T t); + + protected abstract void onFailure(int errorCode, String errorMsg); + + private void showDialog(String loadingContent) { + if (mContext == null) return; + Context context = mContext.get(); + if (context == null) return; + if (mLoadingDialog == null) { + mLoadingDialog = new CatLoadingDialog(context); + ((CatLoadingDialog) mLoadingDialog).setGraduallyText(loadingContent); + } + mLoadingDialog.setCancelable(true); + mLoadingDialog.setCanceledOnTouchOutside(false); + mLoadingDialog.setOnCancelListener(dialog -> unsubscribe()); + mLoadingDialog.setOnDismissListener(dialogInterface -> unsubscribe()); + if (!mLoadingDialog.isShowing()) { + mLoadingDialog.show(); + } + } + + private void dismissDialog() { + if (mLoadingDialog != null && mLoadingDialog.isShowing()) { + mLoadingDialog.dismiss(); + } + } + + public void unsubscribe() { + if (isDisposed()) { + dispose(); + } + } + +} diff --git a/network/src/main/java/com/heyongrui/network/configure/RxHelper.java b/network/src/main/java/com/heyongrui/network/configure/RxHelper.java index 6b09b8c..f960920 100644 --- a/network/src/main/java/com/heyongrui/network/configure/RxHelper.java +++ b/network/src/main/java/com/heyongrui/network/configure/RxHelper.java @@ -4,6 +4,7 @@ import com.heyongrui.network.core.CoreApiException; import com.heyongrui.network.core.CoreHeader; import com.heyongrui.network.core.CoreResponse; +import com.heyongrui.network.core.Optional; import org.reactivestreams.Publisher; @@ -84,6 +85,36 @@ public ObservableSource apply(CoreResponse tCoreResponse) throws Exception }; } + /** + * 请求数据统一再处理(常规式,可为空) + */ + public static ObservableTransformer, Optional> rxHandleResultObservableNullable() { + return new ObservableTransformer, Optional>() { + @Override + public ObservableSource> apply(Observable> upstream) { + return upstream.flatMap(new Function, ObservableSource>>() { + @Override + public ObservableSource> apply(CoreResponse tCoreResponse) throws Exception { + CoreHeader header = tCoreResponse.getHeader(); + int responseCode = header == null ? -1 : header.getStatus(); + if (responseCode == 200) { + return Observable.create(e -> { + try { + e.onNext(tCoreResponse.transform()); + e.onComplete(); + } catch (Exception exc) { + e.onError(exc); + } + }); + } else { + return Observable.error(new CoreApiException(responseCode, tCoreResponse.getHeader().getMsg())); + } + } + }); + } + }; + } + /** * 请求数据统一再处理(背压式) */ @@ -116,4 +147,37 @@ public void subscribe(FlowableEmitter emitter) throws Exception { } }; } + + /** + * 请求数据统一再处理(背压式,可为空) + */ + public static FlowableTransformer, Optional> rxHandleResultFlowableNullable() { + return new FlowableTransformer, Optional>() { + @Override + public Publisher> apply(Flowable> upstream) { + return upstream.flatMap(new Function, Publisher>>() { + @Override + public Publisher> apply(CoreResponse tCoreResponse) throws Exception { + CoreHeader header = tCoreResponse.getHeader(); + int responseCode = header == null ? -1 : header.getStatus(); + if (responseCode == 200) { + return Flowable.create(new FlowableOnSubscribe>() { + @Override + public void subscribe(FlowableEmitter> emitter) throws Exception { + try { + emitter.onNext(tCoreResponse.transform()); + emitter.onComplete(); + } catch (Exception e) { + emitter.onError(e); + } + } + }, BackpressureStrategy.BUFFER); + } else { + return Flowable.error(new CoreApiException(responseCode, tCoreResponse.getHeader().getMsg())); + } + } + }); + } + }; + } } diff --git a/network/src/main/java/com/heyongrui/network/convert/CustomConverterFactory.java b/network/src/main/java/com/heyongrui/network/convert/CustomConverterFactory.java new file mode 100644 index 0000000..56343da --- /dev/null +++ b/network/src/main/java/com/heyongrui/network/convert/CustomConverterFactory.java @@ -0,0 +1,59 @@ +package com.heyongrui.network.convert; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.reflect.TypeToken; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +import okhttp3.RequestBody; +import okhttp3.ResponseBody; +import retrofit2.Converter; +import retrofit2.Retrofit; + +/** + * 2020-04-15 09:34 + * heyongrui + */ +public class CustomConverterFactory extends Converter.Factory { + + /** + * Create an instance using a default {@link Gson} instance for conversion. Encoding to JSON and + * decoding from JSON (when no charset is specified by a header) will use UTF-8. + */ + public static CustomConverterFactory create() { + return create(new Gson()); + } + + /** + * Create an instance using {@code gson} for conversion. Encoding to JSON and + * decoding from JSON (when no charset is specified by a header) will use UTF-8. + */ + @SuppressWarnings("ConstantConditions") // Guarding public API nullability. + public static CustomConverterFactory create(Gson gson) { + if (gson == null) { + throw new NullPointerException("gson == null"); + } + return new CustomConverterFactory(gson); + } + + private final Gson gson; + + private CustomConverterFactory(Gson gson) { + this.gson = gson; + } + + @Override + public Converter requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) { + TypeAdapter adapter = gson.getAdapter(TypeToken.get(type)); +// Log.i("CustomConverterFactory", "responseBodyConverter: " + type.toString()); + return new CustomRequestBodyConverter<>(gson, adapter); + } + + @Override + public Converter responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { + TypeAdapter adapter = gson.getAdapter(TypeToken.get(type)); + return new CustomResponseBodyConverter<>(gson, adapter); + } +} diff --git a/network/src/main/java/com/heyongrui/network/convert/CustomIOException.java b/network/src/main/java/com/heyongrui/network/convert/CustomIOException.java new file mode 100644 index 0000000..345aca4 --- /dev/null +++ b/network/src/main/java/com/heyongrui/network/convert/CustomIOException.java @@ -0,0 +1,41 @@ +package com.heyongrui.network.convert; + +import java.io.IOException; + +/** + * 2020-04-15 14:29 + * heyongrui + */ +public class CustomIOException extends IOException { + + private int errorCode; + private String msg; + + public CustomIOException(int errorCode, String msg) { + super(msg); + this.errorCode = errorCode; + this.msg = msg; + } + + public CustomIOException(Throwable cause, int errorCode, String msg) { + super(msg, cause); + this.errorCode = errorCode; + this.msg = msg; + } + + public int getErrorCode() { + return errorCode; + } + + public void setErrorCode(int errorCode) { + this.errorCode = errorCode; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } +} diff --git a/network/src/main/java/com/heyongrui/network/convert/CustomRequestBodyConverter.java b/network/src/main/java/com/heyongrui/network/convert/CustomRequestBodyConverter.java new file mode 100644 index 0000000..2cd2a90 --- /dev/null +++ b/network/src/main/java/com/heyongrui/network/convert/CustomRequestBodyConverter.java @@ -0,0 +1,43 @@ +package com.heyongrui.network.convert; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonWriter; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.nio.charset.Charset; + +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okio.Buffer; +import retrofit2.Converter; + +/** + * 2020-04-15 09:35 + * heyongrui + */ +public class CustomRequestBodyConverter implements Converter { + + private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8"); + private static final Charset UTF_8 = Charset.forName("UTF-8"); + + private final Gson gson; + private final TypeAdapter adapter; + + CustomRequestBodyConverter(Gson gson, TypeAdapter adapter) { + this.gson = gson; + this.adapter = adapter; + } + + @Override + public RequestBody convert(T value) throws IOException { + Buffer buffer = new Buffer(); + Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8); + JsonWriter jsonWriter = gson.newJsonWriter(writer); + adapter.write(jsonWriter, value); + jsonWriter.close(); + return RequestBody.create(MEDIA_TYPE, buffer.readByteString()); + } +} diff --git a/network/src/main/java/com/heyongrui/network/convert/CustomResponseBodyConverter.java b/network/src/main/java/com/heyongrui/network/convert/CustomResponseBodyConverter.java new file mode 100644 index 0000000..1571269 --- /dev/null +++ b/network/src/main/java/com/heyongrui/network/convert/CustomResponseBodyConverter.java @@ -0,0 +1,69 @@ +package com.heyongrui.network.convert; + +import com.google.gson.Gson; +import com.google.gson.JsonIOException; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.heyongrui.network.core.CoreHeader; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.Charset; + +import okhttp3.MediaType; +import okhttp3.ResponseBody; +import retrofit2.Converter; + + +/** + * 2020-04-15 09:36 + * heyongrui + */ +public class CustomResponseBodyConverter implements Converter { + + private final Gson gson; + private final TypeAdapter adapter; + + CustomResponseBodyConverter(Gson gson, TypeAdapter adapter) { + this.gson = gson; + this.adapter = adapter; + } + + @Override + public T convert(ResponseBody value) throws IOException { + try { + String response = value.string(); +// Log.i("CustomGsonResponse", "convert: " + response); + JSONObject mJSONObject = null; + try { + mJSONObject = new JSONObject(response); + } catch (JSONException mE) { + mE.printStackTrace(); + } + int code = mJSONObject.optInt(CoreHeader.KEY_STATUS, -1); + if (code == 200) { + MediaType mediaType = value.contentType(); + Charset charset = mediaType != null ? mediaType.charset(Charset.forName("UTF-8")) : Charset.forName("UTF-8"); + InputStream inputStream = new ByteArrayInputStream(response.getBytes()); + JsonReader jsonReader = gson.newJsonReader(new InputStreamReader(inputStream, charset)); + T result = adapter.read(jsonReader); + if (jsonReader.peek() != JsonToken.END_DOCUMENT) { + throw new JsonIOException("JSON document was not fully consumed."); + } + return result; + } else { + String message = mJSONObject.optString(CoreHeader.KEY_MSG); + value.close(); + throw new CustomIOException(code, message); + } + } finally { + value.close(); + } + } +} diff --git a/network/src/main/java/com/heyongrui/network/core/CoreHeader.java b/network/src/main/java/com/heyongrui/network/core/CoreHeader.java index a2d47bd..077b878 100644 --- a/network/src/main/java/com/heyongrui/network/core/CoreHeader.java +++ b/network/src/main/java/com/heyongrui/network/core/CoreHeader.java @@ -7,6 +7,10 @@ */ public class CoreHeader implements Serializable { + + public static final String KEY_STATUS = "status"; + public static final String KEY_MSG = "msg"; + private int status; private String msg; private String request; diff --git a/network/src/main/java/com/heyongrui/network/core/CoreResponse.java b/network/src/main/java/com/heyongrui/network/core/CoreResponse.java index bfd6a49..b96c63c 100644 --- a/network/src/main/java/com/heyongrui/network/core/CoreResponse.java +++ b/network/src/main/java/com/heyongrui/network/core/CoreResponse.java @@ -16,7 +16,7 @@ public class CoreResponse { */ private T data; - public CoreResponse(){ + public CoreResponse() { super(); } @@ -41,4 +41,8 @@ public T getData() { public void setData(T data) { this.data = data; } + + public Optional transform() { + return new Optional<>(data); + } } diff --git a/network/src/main/java/com/heyongrui/network/core/Optional.java b/network/src/main/java/com/heyongrui/network/core/Optional.java new file mode 100644 index 0000000..78f9454 --- /dev/null +++ b/network/src/main/java/com/heyongrui/network/core/Optional.java @@ -0,0 +1,37 @@ +package com.heyongrui.network.core; + +import androidx.annotation.Nullable; + +import java.util.NoSuchElementException; + +/** + * 2020-03-12 21:07 + * heyongrui + * 包装类,解决后台返回数据为null时rxjava报错的问题 + */ +public class Optional { + + private final M optional; // 接收到的返回结果 + + public Optional(@Nullable M optional) { + this.optional = optional; + } + + // 判断返回结果是否为null + public boolean isEmpty() { + return this.optional == null; + } + + // 获取不能为null的返回结果,如果为null,直接抛异常,经过二次封装之后,这个异常最终可以在走向RxJava的onError() + public M get() { + if (optional == null) { + throw new NoSuchElementException("No value present"); + } + return optional; + } + + // 获取可以为null的返回结果 + public M getIncludeNull() { + return optional; + } +} diff --git a/network/src/main/java/com/heyongrui/network/interceptor/HttpLogger.java b/network/src/main/java/com/heyongrui/network/interceptor/HttpLogger.java index c40ef10..7592d09 100644 --- a/network/src/main/java/com/heyongrui/network/interceptor/HttpLogger.java +++ b/network/src/main/java/com/heyongrui/network/interceptor/HttpLogger.java @@ -12,7 +12,7 @@ public class HttpLogger implements HttpLogInterceptor.Logger { @Override public void log(String message) { -// Log.i("HttpLogger", message); +// longLogPrint("HttpLogger", message, 3); if (message.startsWith("--> POST") || message.startsWith("--> GET")) { mMessage.setLength(0); } @@ -22,7 +22,67 @@ public void log(String message) { } mMessage.append(message.concat("\n")); if (message.startsWith("<-- END HTTP")) { - Log.i("HttpLogger", mMessage.toString()); + longLogPrint("HttpLogger", mMessage.toString(), 3); + } + } + + /** + * 长日志截断处理 + * + * @param tag 标识 + * @param msg 打印数据 + * @param level 打印的日志等级(1=Verbose 2=Debug 3=Info 4=Warn 5=Error) + */ + private void longLogPrint(String tag, String msg, int level) { + if (tag == null || tag.length() == 0 + || msg == null || msg.length() == 0) { + return; + } + + int segmentSize = 3 * 1024; + long length = msg.length(); + // 长度小于等于限制直接打印 + if (length <= segmentSize) { + if (level == 1) { + Log.v(tag, msg); + } else if (level == 2) { + Log.d(tag, msg); + } else if (level == 3) { + Log.i(tag, msg); + } else if (level == 4) { + Log.w(tag, msg); + } else if (level == 5) { + Log.e(tag, msg); + } + } else { + // 循环分段打印日志 + while (msg.length() > segmentSize) { + String logContent = msg.substring(0, segmentSize); + msg = msg.replace(logContent, ""); + if (level == 1) { + Log.v(tag, logContent); + } else if (level == 2) { + Log.d(tag, logContent); + } else if (level == 3) { + Log.i(tag, logContent); + } else if (level == 4) { + Log.w(tag, logContent); + } else if (level == 5) { + Log.e(tag, logContent); + } + } + // 打印剩余日志 + if (level == 1) { + Log.v(tag, msg); + } else if (level == 2) { + Log.d(tag, msg); + } else if (level == 3) { + Log.i(tag, msg); + } else if (level == 4) { + Log.w(tag, msg); + } else if (level == 5) { + Log.e(tag, msg); + } } } @@ -33,7 +93,9 @@ public void log(String message) { * @return 格式化后的json串 */ public static String formatJson(String jsonStr) { - if (null == jsonStr || "".equals(jsonStr)) return ""; + if (null == jsonStr || "".equals(jsonStr)) { + return ""; + } StringBuilder sb = new StringBuilder(); char last = '\0'; char current = '\0'; @@ -71,10 +133,7 @@ public static String formatJson(String jsonStr) { } /** - * 添加space - * - * @param sb - * @param indent + * 添加空格 */ private static void addIndentBlank(StringBuilder sb, int indent) { for (int i = 0; i < indent; i++) { @@ -134,18 +193,20 @@ public static String decodeUnicode(String theString) { } outBuffer.append((char) value); } else { - if (aChar == 't') + if (aChar == 't') { aChar = '\t'; - else if (aChar == 'r') + } else if (aChar == 'r') { aChar = '\r'; - else if (aChar == 'n') + } else if (aChar == 'n') { aChar = '\n'; - else if (aChar == 'f') + } else if (aChar == 'f') { aChar = '\f'; + } outBuffer.append(aChar); } - } else + } else { outBuffer.append(aChar); + } } return outBuffer.toString(); } diff --git a/network/src/main/java/com/heyongrui/network/service/ApiService.java b/network/src/main/java/com/heyongrui/network/service/ApiService.java index f0311d6..f0a4c17 100644 --- a/network/src/main/java/com/heyongrui/network/service/ApiService.java +++ b/network/src/main/java/com/heyongrui/network/service/ApiService.java @@ -2,7 +2,6 @@ import android.text.TextUtils; -import com.google.gson.Gson; import com.heyongrui.network.BuildConfig; import com.heyongrui.network.configure.HttpCache; import com.heyongrui.network.configure.TrustManager; @@ -15,6 +14,7 @@ import java.util.concurrent.TimeUnit; import okhttp3.OkHttpClient; +import retrofit2.Converter; import retrofit2.Retrofit; import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; import retrofit2.converter.gson.GsonConverterFactory; @@ -23,15 +23,16 @@ public class ApiService { public static final String BASE_API_URL_FORMAL = "https://api.findmacau.com/fm/";//用户版API正式环境 public static final String BASE_API_URL_TEST = "https://api.findmacau.com/uat/fm/";//用户版API测试环境 - private static final int TIMEOUT_READ = 20; - private static final int TIMEOUT_CONNECTION = 10; + private static final int CONNECT_TIME = 15; + private static final int WRITE_TIME = 20; + private static final int READ_TIME = 20; private static final HttpLogInterceptor formattingJsonLogInterceptor = new HttpLogInterceptor(new HttpLogger()).setLevel(HttpLogInterceptor.Level.BODY); private static final HeaderInterceptor headerInterceptor = new HeaderInterceptor(); private static final TokenInterceptor tokenInterceptor = new TokenInterceptor(); private static final CacheInterceptor cacheInterceptor = new CacheInterceptor(); private static final OkHttpClient okHttpClient = new OkHttpClient.Builder() - .sslSocketFactory(TrustManager.getSSLSocketFactory(),TrustManager.trustManager)//SSL证书 + .sslSocketFactory(TrustManager.getSSLSocketFactory(), TrustManager.trustManager)//SSL证书 .hostnameVerifier(org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER) .addInterceptor(headerInterceptor) .addInterceptor(tokenInterceptor) @@ -39,9 +40,9 @@ public class ApiService { .cache(HttpCache.getCache()) .addNetworkInterceptor(cacheInterceptor) .addInterceptor(cacheInterceptor) - .connectTimeout(TIMEOUT_CONNECTION, TimeUnit.SECONDS)//TimeOut - .readTimeout(TIMEOUT_READ, TimeUnit.SECONDS) - .writeTimeout(TIMEOUT_READ, TimeUnit.SECONDS) + .connectTimeout(CONNECT_TIME, TimeUnit.SECONDS)//TimeOut + .readTimeout(READ_TIME, TimeUnit.SECONDS) + .writeTimeout(WRITE_TIME, TimeUnit.SECONDS) .retryOnConnectionFailure(true)//失败重连 .build(); @@ -53,7 +54,7 @@ public static T createApi(Class clazz, String baseUrl) { return createApi(clazz, baseUrl, null); } - public static T createApi(Class clazz, String baseUrl, Gson gson) { + public static T createApi(Class clazz, String baseUrl, Converter.Factory factory) { Retrofit.Builder builder = new Retrofit.Builder(); if (TextUtils.isEmpty(baseUrl)) { if (BuildConfig.DEBUG) { @@ -65,7 +66,7 @@ public static T createApi(Class clazz, String baseUrl, Gson gson) { builder.baseUrl(baseUrl); } builder.client(okHttpClient) - .addConverterFactory(gson == null ? GsonConverterFactory.create() : GsonConverterFactory.create(gson)) + .addConverterFactory(factory == null ? GsonConverterFactory.create() : factory) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()); Retrofit retrofit = builder.build(); return retrofit.create(clazz);