retrofit-helper
Retrofit is the Http request library that many android developers are using!He is responsible for the encapsulation of the network request interface. The underlying implementation is OkHttp. One of its features is that it contains extra annotations to simplify your code volume. CallAdapter.Factory and Converter.Factory can flexibly extend your request.We still need to encapsulate a layer to make it easier for us to use. The purpose of retrofit-helper is to simplify your request again.
1. Retrofit-helper extends those capabilities
2. Encapsulation Logic Resolution
2.1 RetrofitFactory globally manages retrofit instances
DEFAULT static variable manages retrofit objects commonly used by default, OTHERS manages retrofit for many other different configurations
/** * Creation time: 2018/4/3 * Writer: Chegxin * Functional description: Manage global instances of R ofit */ public final class RetrofitFactory { /** * Cache retrofit collections of different configurations, such as different urls, converters, and so on */ public static final Map<String, Retrofit> OTHERS = new ConcurrentHashMap<>(2); /** * Global etrofit objects */ public static volatile Retrofit DEFAULT; private RetrofitFactory() { } public static <T> T create(Class<T> service) { //Make sure retrofit is not empty or modified with multiple threads Retrofit retrofit = DEFAULT; Utils.checkState(retrofit != null, "DEFAULT == null"); return retrofit.create(service); } /** * @param name Gets retrofit of the specified name in OTHERS */ public static <T> T create(String name, Class<T> service) { Utils.checkNotNull(name, "name == null"); Retrofit retrofit = OTHERS.get(name); Utils.checkState(retrofit != null, String.format("retrofit named with '%s' was not found , have you put it in OTHERS ?", name)); return retrofit.create(service); } }
2.2 Call2 interface inherits retrofit.Call overloaded enqueue (Callback<T> callback) method
Enqueue (@Nullable Object tag, Callback2<T> callback2) method incoming request tags this request, tag tags are needed to cancel the request
/** * Creation time: 2018/4/8 * Writer: Chegxin * Function description: Add overloaded method {@link Call2#enqueue(Object, Callback2)} method */ public interface Call2<T> extends retrofit2.Call<T> { /** * @param tag Requested tag, used to cancel the request use * @param callback2 Callback requested */ void enqueue(@Nullable Object tag, Callback2<T> callback2); @Override Call2<T> clone(); }
2.3 Callback2 Unified Processing Callbacks
Request Start, Success Processing, Failure Processing, Successful Callback, Failure Callback and Request End are handled uniformly here. Each method can be overridden according to different business. For example, parseResponse method can be overridden to make different prompt descriptions according to improper HTTP codes or
Override parseThrowable method to handle various Throwable
@UiThread public abstract class Callback2<T> { public abstract void onStart(Call2<T> call2); @NonNull public Result<T> parseResponse(Call2<T> call2, Response<T> response) { T body = response.body(); if (response.isSuccessful()) { if (body != null) { return Result.success(body); } else { return Result.error(new HttpError("No data at this time", response)); } } final String msg; switch (response.code()) { case 400: msg = "Parameter error"; break; case 401: msg = "Identity not authorized"; break; case 403: msg = "No access"; break; case 404: msg = "Address not found"; break; default: msg = "Service Exception"; } return Result.error(new HttpError(msg, response)); } /** * Unified Resolution Throwable Object to HttpError Object.If it is HttpError, * Is an exception thrown in {@link retrofit2.Converter#convert(Object)} * * @param call2 call * @param t Throwable * @return HttpError result */ @NonNull public HttpError parseThrowable(Call2<T> call2, Throwable t) { if (t instanceof HttpError) { //Direct throw exception reception for convert function return (HttpError) t; } else if (t instanceof UnknownHostException) { return new HttpError("Network exception", t); } else if (t instanceof ConnectException) { return new HttpError("Network exception", t); } else if (t instanceof SocketException) { return new HttpError("Service Exception", t); } else if (t instanceof SocketTimeoutException) { return new HttpError("Response timeout", t); } else { return new HttpError("request was aborted", t); } } public abstract void onError(Call2<T> call2, HttpError error); public abstract void onSuccess(Call2<T> call2, T response); /** * @param t Error message for request failure * @param canceled Has the request been cancelled */ public abstract void onCompleted(Call2<T> call2, @Nullable Throwable t, boolean canceled); }
2.4 HttpError Unified Handling Exception Errors
Two member attributes of the HttpError class, msg, are body. MSG is the description of the error. body can store the specific information of the exception or the original json, etc. onError (Call2<T> Call2, HttpError error) callback method can do secondary processing based on the specific information of the body.
/** * Common error message. A common request is a failure. It only needs to pop up some error message, like{@link retrofit2.HttpException} * Created by chengxin on 2017/6/22. */ public final class HttpError extends RuntimeException { private static final long serialVersionUID = -134024482758434333L; /** * Error description information displayed on the front end */ public String msg; /** * <p> * Failed request to save failure information, for example: * <li>BusiModel: {code:xxx,msg:xxx} Business error information </li> * <li>original json: Original json< /li> * <li>{@link retrofit2.Response}:Error Response Body - > Response<?> < /li> * <li>Throwable: Exception information thrown </li> * </p> */ @Nullable public final transient Object body; public HttpError(String msg) { this(msg, null); } public HttpError(String msg, @Nullable Object body) { super(msg); if (body instanceof Throwable) { initCause((Throwable) body); } //FastPrintWriter#print(String str) this.msg = msg != null ? msg : "null"; this.body = body; } /** * Be consistent with msg */ @Override public String getMessage() { return msg; } @Override public String toString() { return "HttpError {msg=" + msg + ", body=" + body + '}'; } }
2.5 ExecutorCallAdapterFactory returns Call2 request adapter
Request adapter factory class that handles request interface methods returning to Call2
public final class ExecutorCallAdapterFactory extends CallAdapter.Factory { public static final CallAdapter.Factory INSTANCE = new ExecutorCallAdapterFactory(); private ExecutorCallAdapterFactory() { } /** * Extract the raw class type from {@code type}. For example, the type representing * {@code List<? extends Runnable>} returns {@code List.class}. */ public static Class<?> getRawType(Type type) { return CallAdapter.Factory.getRawType(type); } @Override public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { if (getRawType(returnType) != Call2.class) { return null; } if (!(returnType instanceof ParameterizedType)) { throw new IllegalArgumentException( "Call return type must be parameterized as Call2<Foo> or Call2<? extends Foo>"); } final Type responseType = getParameterUpperBound(0, (ParameterizedType) returnType); final Executor callbackExecutor = retrofit.callbackExecutor(); if (callbackExecutor == null) throw new AssertionError(); return new CallAdapter<Object, Call<?>>() { @Override public Type responseType() { return responseType; } @Override public Call<Object> adapt(Call<Object> call) { return new ExecutorCallbackCall2<>(callbackExecutor, call); } }; } }
2.6 ExecutorCallbackCall2 inherits Call2 proxy OkHttpCall to handle UI callbacks
Decorator mode proxy all methods of OkHttpCall, and thread dispatch handles callback methods of Callback2 executed on the main thread
final class ExecutorCallbackCall2<T> implements Call2<T> { private final Executor callbackExecutor; private final Call<T> delegate; /** * The executor used for {@link Callback} methods on a {@link Call}. This may be {@code null}, * in which case callbacks should be made synchronously on the background thread. */ ExecutorCallbackCall2(Executor callbackExecutor, Call<T> delegate) { this.callbackExecutor = callbackExecutor; this.delegate = delegate; } @Override public void enqueue(final Callback<T> callback) { throw new UnsupportedOperationException("please call enqueue(Object tag, Callback2<T> callback2)"); } @Override public void enqueue(@Nullable Object tag, final Callback2<T> callback2) { Utils.checkNotNull(callback2, "callback2==null"); CallManager.getInstance().add(this, tag != null ? tag : "NO_TAG"); callbackExecutor.execute(new Runnable() { @Override public void run() { if (!isCanceled()) { callback2.onStart(ExecutorCallbackCall2.this); } } }); delegate.enqueue(new Callback<T>() { @Override public void onResponse(Call<T> call, final Response<T> response) { callbackExecutor.execute(new Runnable() { @Override public void run() { callResult(callback2, response, null); } }); } @Override public void onFailure(Call<T> call, final Throwable t) { callbackExecutor.execute(new Runnable() { @Override public void run() { callResult(callback2, null, t); } }); } }); } @UiThread private void callResult(Callback2<T> callback2, @Nullable Response<T> response, @Nullable Throwable failureThrowable) { try { if (!isCanceled()) { //1. Get parsing results Result<T> result; if (response != null) { result = callback2.parseResponse(this, response); Utils.checkNotNull(result, "result==null"); } else { Utils.checkNotNull(failureThrowable, "failureThrowable==null"); HttpError error = callback2.parseThrowable(this, failureThrowable); result = Result.error(error); } //2. Callback success and failure if (result.isSuccess()) { callback2.onSuccess(this, result.body()); } else { callback2.onError(this, result.error()); } } callback2.onCompleted(this, failureThrowable, isCanceled()); } finally { CallManager.getInstance().remove(this); } } @Override public boolean isExecuted() { return delegate.isExecuted(); } @Override public Response<T> execute() throws IOException { return delegate.execute(); } @Override public void cancel() { delegate.cancel(); } @Override public boolean isCanceled() { return delegate.isCanceled(); } @SuppressWarnings("CloneDoesntCallSuperClone") // Performing deep clone. @Override public Call2<T> clone() { return new ExecutorCallbackCall2<>(callbackExecutor, delegate.clone()); } @Override public Request request() { return delegate.request(); } }
2.7 CallManager Unified Management Request, Cancel Request
Save all requests globally, add and delete requests, and cancel certain tag matching requests.CallManager.getInstance().cancel (yourTag) can be called in the destroy method of an Activity or Fragment
/** * Creation time: 2018/5/31 * Writer: Chegxin * Function description: Manage Call request management globally, just like {@link okhttp3.Dispatcher} */ public final class CallManager implements ActionManager<Call<?>> { @GuardedBy("this") private final List<CallTag> callTags = new ArrayList<>(4); private volatile static CallManager instance; private CallManager() { } public static CallManager getInstance() { if (instance == null) { synchronized (CallManager.class) { if (instance == null) { instance = new CallManager(); } } } return instance; } @Override public synchronized void add(Call<?> call, Object tag) { Utils.checkState(!contains(call), "Call<?> " + call + " is already added."); callTags.add(new CallTag(call, tag)); } /** * Remove when call ends * * @param call Retrofit Call */ @Override public synchronized void remove(Call<?> call) { if (callTags.isEmpty()) return; for (int index = 0; index < callTags.size(); index++) { if (call == callTags.get(index).call) { //like okhttp3.Headers#removeAll(String name) //The remove(int index) method is superior to remove(Object o) and does not need to be traversed again callTags.remove(index); break; } } } /** * Cancel and remove the call for the corresponding tag to ensure that the call is no longer referenced after it is cancelled. * Double insurance with {@link #remove(Call)} method * * @param tag call Corresponding tag */ @Override public synchronized void cancel(final @Nullable Object tag) { if (callTags.isEmpty()) return; if (tag != null) { for (int index = 0; index < callTags.size(); index++) { CallTag callTag = callTags.get(index); if (callTag.tag.equals(tag)) { callTag.call.cancel(); callTags.remove(index); index--; } } } else { for (CallTag callTag : callTags) { callTag.call.cancel(); } callTags.clear(); } } @Override public synchronized boolean contains(Call<?> call) { for (CallTag callTag : callTags) { if (call == callTag.call) { return true; } } return false; } /** * Save call and tag */ final static class CallTag { private final Call<?> call; private final Object tag; CallTag(Call<?> call, Object tag) { Utils.checkNotNull(call == null, "call==null"); Utils.checkNotNull(tag == null, "tag==null"); this.call = call; this.tag = tag; } } }
2.8 ProgressInterceptor interceptor listens for download and upload progress
Inherit okhttp3.Interceptor, pass ProgressListener into the construction method to monitor progress
/** * Creation time: 2018/8/2 * Writer: Chegxin * Capability description: Upload or download progress monitor interceptor */ public class ProgressInterceptor implements Interceptor { private final ProgressListener mProgressListener; public ProgressInterceptor(ProgressListener progressListener) { Utils.checkNotNull(progressListener, "progressListener==null"); this.mProgressListener = progressListener; } @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); RequestBody requestBody = request.body(); //Determine if there is an upload requirement if (requestBody != null && requestBody.contentLength() > 0) { Request.Builder builder = request.newBuilder(); RequestBody newRequestBody = new ProgressRequestBody(requestBody, mProgressListener, request); request = builder.method(request.method(), newRequestBody).build(); } Response response = chain.proceed(request); ResponseBody responseBody = response.body(); if (responseBody != null && responseBody.contentLength() > 0) { Response.Builder builder = response.newBuilder(); ResponseBody newResponseBody = new ProgressResponseBody(responseBody, mProgressListener, request); response = builder.body(newResponseBody).build(); } return response; } }
2.9 HttpLoggingInterceptor allows you to specify the log level for a request individually
Add this interceptor when constructing the OkhttpClient, add comments to the requested service method
@Headers("LogLevel:NONE") or @Headers("LogLevel:BASIC") or @Headers("LogLevel:HEADERS") or @Headers("LogLevel:BODY")
@FormUrlEncoded @Headers("LogLevel:HEADERS") @POST("user/login") Call2<LoginInfo> getLogin(@Field("username") String username, @Field("password") String password);
3. Actual Warfare
3.1 Initialize the global Retrofit object
Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://wanandroid.com/") .callFactory(new OkHttpClient.Builder() .addNetworkInterceptor(httpLoggingInterceptor) .build()) //This adapter must be added to build processing callbacks .addCallAdapterFactory(ExecutorCallAdapterFactory.INSTANCE) //Add a custom json parser .addConverterFactory(GsonConverterFactory.create()) .build(); RetrofitFactory.DEFAULT = retrofit; //You can add multiple, such as: RetrofitFactory.OTHERS.put("other",otherRetrofit);
3.2 Add Request Service Interface
The following are post requests for login
@FormUrlEncoded @POST("user/login") Call2<LoginInfo> getLogin(@Field("username") String username, @Field("password") String password);
3.3 Add ILoadingView to start and end animations
Activity or Fragment can inherit the ILoadingView interface to start and end animations
public interface ILoadingView { /** * Show Loading */ void showLoading(); /** * Hide Loading */ void hideLoading(); }
3.4 Add AnimCallback to handle animations
Override parseThrowable here to handle some unhandled exceptions in Callback2
public abstract class AnimCallback<T> extends Callback2<T> { private ILoadingView mLoadingView; public AnimCallback(@Nullable ILoadingView loadingView) { this.mLoadingView = loadingView; } @Override public void onStart(Call2<T> call2) { if (mLoadingView != null) mLoadingView.showLoading(); } @Override public void onCompleted(Call2<T> call2, @Nullable Throwable t, boolean canceled) { if (canceled) return; if (mLoadingView != null) mLoadingView.hideLoading(); } @NonNull @Override public HttpError parseThrowable(Call2<T> call2, Throwable t) { HttpError filterError; if (t instanceof JsonSyntaxException) { filterError = new HttpError("Resolve Exception", t); } else { filterError = super.parseThrowable(call2, t); } return filterError; } }
3.5 Initiate Request
RetrofitFactory.create(ApiService.class) .getLogin("xxxxx", "123456") .enqueue(hashCode(), new AnimCallback<LoginInfo>(this) { @Override public void onError(Call2<LoginInfo> call2, HttpError error) { //Processing Failure } @Override public void onSuccess(Call2<LoginInfo> call2, LoginInfo response) { //Processing success such as saving login information } }); //Cancel an open request in onDestor @Override protected void onDestroy() { super.onDestroy(); //hashCode() guarantees uniqueness and cancels all requests made by the current page as long as // enqueue(tag, callback2) passes in the corresponding hashCode(). CallManager.getInstance().cancel(hashCode()); }
4. Notes
4.1 Building retrofit requires an ExecutorCallAdapterFactory instance, otherwise the service interface returned as Call2 cannot be handled
The callback functions of 4.2 Callback 2 are all executed on the main thread. If the Call2.cancel() method is called, no callback method will execute except onCompleted() method.
5. Download
implementation "com.xcheng:retrofit-helper:1.0.0"\
retrofit-helper GitHub address:
https://github.com/xchengDroid/retrofit-helper
Copyright 2019 xchengDroid Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Last
For Android programmers, I've sorted out some information for you, including advanced UI, performance optimization, architect courses, NDK, ReactNative+Weex Wechat applets, Flutter and other advanced Android practice techniques. I hope to help you, save you time searching for information on the web, and also share dynamic information with you.Edge friends study together!
-
Outline of Android Frontier Technologies
-
Complete Systematic Advanced Architecture Video
Data collection: Tian Zan+Jia Qun for free Android IOC Architecture Design
Additive group Android IOC Architecture Design Receive and obtain prior Android Advanced Architecture data, source code, notes, videos.Advanced UI, performance optimization, architect courses, advanced Android practices for all aspects of ReactNative+Weex, and technical bulls in the group discuss and communicate to solve problems.