retrofit-helper
Retrofit是很多android開發(fā)者都在使用的Http請求庫醋界!他負責(zé)網(wǎng)絡(luò)請求接口的封裝,底層實現(xiàn)是OkHttp,它的一個特點是包含了特別多注解竟宋,通過動態(tài)代理的方式使得開發(fā)者在使用訪問網(wǎng)絡(luò)的時候更加方便簡單高效。
-
1. Retrofit-helper擴展了那些功能
描述 相關(guān)類和方法 回調(diào)函數(shù)中直接處理請求結(jié)果形纺,無需再次判斷是否成功 Callback.onSuccess(Call<T> call, T response)
請求開始和結(jié)束監(jiān)聽 Callback.onStart(Call<T> call)
和Callback.onCompleted(Call<T> call, @Nullable Throwable t)
全局維護多個Retrofit實例 RetrofitFactory.DEFAULT
和RetrofitFactory.OTHERS
統(tǒng)一解析異常信息 Callback.parseThrowable(Call<T> call, Throwable t)
綁定Activity或者Fragment生命周期 LifeCall<T> bindToLifecycle(LifecycleProvider provider, Lifecycle.Event event)
攔截器監(jiān)聽下載和上傳進度 ProgressInterceptor
丘侠、ProgressListener
單獨指定某個請求的日志級別 HttpLoggingInterceptor
-
2. 封裝邏輯解析
-
2.1
RetrofitFactory
全局管理retrofit
實例全局管理retrofit 實例,通用的做法有單利模式或者靜態(tài)類成員屬性的方式逐样,并且在Application中初始化蜗字,在這里只是管理全局retrofit對象,故采用靜態(tài)的成員屬性即可滿足需求
DEFAULT 靜態(tài)變量管理默認常用的的retrofit對象脂新,OTHERS 管理其他多個不同配置的retrofit
/** * 創(chuàng)建時間:2018/4/3 * 編寫人: chengxin * 功能描述:管理全局的Retrofit實例 */ public final class RetrofitFactory { /** * 緩存不同配置的retrofit集合挪捕,如不同的url ,converter等 */ public static final Map<String, Retrofit> OTHERS = new ConcurrentHashMap<>(2); /** * 全局的Retrofit對象 */ public static volatile Retrofit DEFAULT; /** * A {@code null} value is permitted */ @Nullable public static volatile OnEventListener LISTENER; private RetrofitFactory() { } public static <T> T create(Class<T> service) { //確保多線程的情況下retrofit不為空或者被修改了 Retrofit retrofit = DEFAULT; Utils.checkState(retrofit != null, "DEFAULT == null"); return retrofit.create(service); } /** * @param name 獲取 OTHERS 中指定名字的retrofit */ 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 自定義Call,支持綁定生命周期。
Call
接口實現(xiàn)enqueue(Callback<T> callback)
方法enqueue(Callback<T> callback)
争便,支持綁定Activity或者Fragment生命周期bindToLifecycle(LifecycleProvider provider, Lifecycle.Event event)
/** * 創(chuàng)建時間:2018/4/8 * 編寫人: chengxin * 功能描述:支持生命周期綁定的Call{@link retrofit2.Call} */ public interface Call<T> extends Callable<T>, Cloneable { String TAG = Call.class.getSimpleName(); boolean isExecuted(); void cancel(); boolean isCanceled(); Call<T> clone(); Request request(); /** * 綁定生命周期 * * @param provider LifecycleProvider * @param event {@link Lifecycle.Event} * @return */ LifeCall<T> bindToLifecycle(LifecycleProvider provider, Lifecycle.Event event); /** * default event is {@link Lifecycle.Event#ON_DESTROY} * * @param provider LifecycleProvider * @return LifeCall * @see Call#bindToLifecycle(LifecycleProvider, Lifecycle.Event) */ LifeCall<T> bindUntilDestroy(LifecycleProvider provider); }
-
2.3
Callback
統(tǒng)一處理回調(diào)和異常retrofit 默認的callback只有支持成功和失敗的回調(diào)级零,而一般我們的場景需要在請求開始和結(jié)束的地方做一些UI的展示處理,如顯示加載動畫等滞乙,故添加了
onStart
和onCompleted
監(jiān)聽奏纪。parseThrowable
方法可以處理各種請求過程中拋出的throwable,轉(zhuǎn)換成統(tǒng)一的格式方便我們處理展示等/** * if {@link LifeCall#isDisposed()} return true, all methods will not call * * @param <T> Successful response body type. */ @UiThread public interface Callback<T> { /** * @param call The {@code Call} that was started */ void onStart(Call<T> call); /** * @param call The {@code Call} that has thrown exception * @param t 統(tǒng)一解析throwable對象轉(zhuǎn)換為HttpError對象,如果throwable為{@link HttpError} * <li>則為{@link retrofit2.Converter#convert(Object)}內(nèi)拋出的異常</li> * 如果為{@link retrofit2.HttpException} * <li>則為{@link Response#body()}為null的時候拋出的</li> */ @NonNull HttpError parseThrowable(Call<T> call, Throwable t); /** * 過濾一次數(shù)據(jù),如剔除List中的null等,默認可以返回t */ @NonNull T transform(Call<T> call, T t); void onError(Call<T> call, HttpError error); void onSuccess(Call<T> call, T t); /** * @param t 請求失敗的錯誤信息 */ void onCompleted(Call<T> call, @Nullable Throwable t); }
-
-
2.4
HttpError
統(tǒng)一包裝異常錯誤酷宵,由CallbackparseThrowable
方法統(tǒng)一返回? HttpError類中有兩個成員屬性msg 被body亥贸,msg是保存錯誤的描述信息等,body可以保存異常的具體信息或者原始的json等浇垦,
onError(Call<T> call, HttpError error)
回調(diào)方法可以根據(jù)body的具體信息做二次處理炕置。/** * 通用的錯誤信息,一般請求是失敗只需要彈出一些錯誤信息即可,like{@link retrofit2.HttpException} * Created by chengxin on 2017/6/22. */ public final class HttpError extends RuntimeException { private static final long serialVersionUID = -134024482758434333L; /** * 展示在前端的錯誤描述信息 */ public String msg; /** * <p> * 請求失敗保存失敗信息,for example: * <li>BusiModel: {code:xxx,msg:xxx} 業(yè)務(wù)錯誤信息</li> * <li>original json: 原始的json</li> * <li>{@link retrofit2.Response}:錯誤響應(yīng)體->Response<?></li> * <li>Throwable: 拋出的異常信息</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; } /** * 保證和msg一致 */ @Override public String getMessage() { return msg; } @Override public String toString() { return "HttpError {msg=" + msg + ", body=" + body + '}'; } }
-
2.5 CallAdapterFactory返回
Call
請求適配器處理請求接口方法返回為Call的請求適配器工廠類
public final class CallAdapterFactory extends CallAdapter.Factory { private static final String RETURN_TYPE = Call.class.getSimpleName(); public static final CallAdapter.Factory INSTANCE = new CallAdapterFactory(); private CallAdapterFactory() { } /** * 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) != Call.class) { return null; } if (!(returnType instanceof ParameterizedType)) { throw new IllegalArgumentException( String.format("%s return type must be parameterized as %s<Foo> or %s<? extends Foo>", RETURN_TYPE, RETURN_TYPE, RETURN_TYPE)); } final Type responseType = getParameterUpperBound(0, (ParameterizedType) returnType); return new CallAdapter<Object, Call<?>>() { @Override public Type responseType() { return responseType; } @Override public Call<Object> adapt(retrofit2.Call<Object> call) { return new RealCall<>(call); } }; } }
-
2.6
RealLifeCall
繼承LifeCall
實現(xiàn)Activity或Fragment生命周期綁定男韧,自動管理生命周期負責(zé)生命周期的監(jiān)聽朴摊,在
onChanged(@NonNull Lifecycle.Event event)
方法中如果匹配到指定的event,標記為disposed 并取消請求此虑,這時Callback中所有的回調(diào)函數(shù)將不會在執(zhí)行甚纲,保證回調(diào)處的安全性。final class RealLifeCall<T> implements LifeCall<T> { private final Call<T> delegate; private final Lifecycle.Event event; private final LifecycleProvider provider; /** * LifeCall是否被釋放了 * like rxAndroid MainThreadDisposable or rxJava ObservableUnsubscribeOn, IoScheduler */ private final AtomicBoolean once = new AtomicBoolean(); RealLifeCall(Call<T> delegate, Lifecycle.Event event, LifecycleProvider provider) { this.delegate = delegate; this.event = event; this.provider = provider; provider.observe(this); } @Override public void enqueue(final Callback<T> callback) { Utils.checkNotNull(callback, "callback==null"); delegate.enqueue(new Callback<T>() { @Override public void onStart(Call<T> call) { if (!isDisposed()) { callback.onStart(call); } } @NonNull @Override public HttpError parseThrowable(Call<T> call, Throwable t) { if (!isDisposed()) { return callback.parseThrowable(call, t); } return new HttpError("Already disposed.", t); } @NonNull @Override public T transform(Call<T> call, T t) { if (!isDisposed()) { return callback.transform(call, t); } return t; } @Override public void onSuccess(Call<T> call, T t) { if (!isDisposed()) { callback.onSuccess(call, t); } } @Override public void onError(Call<T> call, HttpError error) { if (!isDisposed()) { callback.onError(call, error); } } @Override public void onCompleted(Call<T> call, @Nullable Throwable t) { if (!isDisposed()) { callback.onCompleted(call, t); provider.removeObserver(RealLifeCall.this); } } }); } @NonNull @Override public T execute() throws Throwable { try { if (isDisposed()) { throw new DisposedException("Already disposed."); } T body = delegate.execute(); if (isDisposed()) { throw new DisposedException("Already disposed."); } return body; } catch (Throwable t) { if (isDisposed() && !(t instanceof DisposedException)) { throw new DisposedException("Already disposed.", t); } throw t; } finally { if (!isDisposed()) { provider.removeObserver(this); } } } @Override public void onChanged(@NonNull Lifecycle.Event event) { if (this.event == event || event == Lifecycle.Event.ON_DESTROY //Activity和Fragment的生命周期是不會傳入 {@code Lifecycle.Event.ON_ANY}, //可以手動調(diào)用此方法傳入 {@code Lifecycle.Event.ON_ANY},用于區(qū)分是否為手動調(diào) 用 || event == Lifecycle.Event.ON_ANY) { if (once.compareAndSet(false, true)/*保證原子性*/) { delegate.cancel(); RetrofitFactory.getOnEventListener().onDisposed(delegate, event); provider.removeObserver(this); } } } @Override public boolean isDisposed() { return once.get(); } }
-
2.7
AndroidLifecycle
觀察者模式統(tǒng)一分發(fā)生命周期事件繼承LifecycleObserver 接口監(jiān)聽當(dāng)前的Activity或者Fragment的生命周期朦前,分發(fā)生命周期事件到Observer的
onChanged
方法介杆。這個類是線程安全的,保證多線程環(huán)境的正確性/** * 實現(xiàn)LifecycleObserver監(jiān)聽Activity和Fragment的生命周期 * It is thread safe. * * @see android.database.Observable */ public final class AndroidLifecycle implements LifecycleProvider, LifecycleObserver { private final Object mLock = new Object(); @GuardedBy("mLock") private final ArrayList<Observer> mObservers = new ArrayList<>(); /** * 緩存當(dāng)前的Event事件 */ @GuardedBy("mLock") @Nullable private Lifecycle.Event mEvent; @MainThread public static LifecycleProvider createLifecycleProvider(LifecycleOwner owner) { return new AndroidLifecycle(owner); } private AndroidLifecycle(LifecycleOwner owner) { owner.getLifecycle().addObserver(this); } @OnLifecycleEvent(Lifecycle.Event.ON_ANY) void onEvent(LifecycleOwner owner, Lifecycle.Event event) { synchronized (mLock) { //保證線程的可見性 mEvent = event; // since onChanged() is implemented by the app, it could do anything, including // removing itself from {@link mObservers} - and that could cause problems if // an iterator is used on the ArrayList {@link mObservers}. // to avoid such problems, just march thru the list in the reverse order. for (int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onChanged(event); } } if (event == Lifecycle.Event.ON_DESTROY) { owner.getLifecycle().removeObserver(this); } } @Override public void observe(Observer observer) { if (observer == null) { throw new IllegalArgumentException("The observer is null."); } synchronized (mLock) { if (mObservers.contains(observer)) { return; } mObservers.add(observer); RetrofitFactory.getOnEventListener().onObserverCountChanged(this, mObservers.size() - 1, mObservers.size()); // since onChanged() is implemented by the app, it could do anything, including // removing itself from {@link mObservers} - and that could cause problems if // an iterator is used on the ArrayList {@link mObservers}. // to avoid such problems, just call onChanged() after {@code mObservers.add(observer)} if (mEvent != null) { observer.onChanged(mEvent); } } } @Override public void removeObserver(Observer observer) { if (observer == null) { throw new IllegalArgumentException("The observer is null."); } synchronized (mLock) { int index = mObservers.indexOf(observer); if (index == -1) { return; } mObservers.remove(index); RetrofitFactory.getOnEventListener().onObserverCountChanged(this, mObservers.size() + 1, mObservers.size()); } } @Override public String toString() { return "AndroidLifecycle@" + Integer.toHexString(hashCode()); } }
-
2.8
ProgressInterceptor
攔截器監(jiān)聽下載和上傳進度繼承
okhttp3.Interceptor
韭寸,構(gòu)造方法中傳入ProgressListener
監(jiān)聽進度/** * 創(chuàng)建時間:2018/8/2 * 編寫人: chengxin * 功能描述:上傳或下載進度監(jiān)聽攔截器 */ 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(); //判斷是否有上傳需求 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
可以單獨指定某個請求的日志級別構(gòu)造OkhttpClient時添加此攔截器春哨,在請求的服務(wù)方法中添加注解
@Headers("LogLevel:NONE") 或 @Headers("LogLevel:BASIC") 或 @Headers("LogLevel:HEADERS") 或@Headers("LogLevel:BODY")
@FormUrlEncoded @Headers("LogLevel:HEADERS") @POST("user/login") Call<LoginInfo> getLogin(@Field("username") String username, @Field("password") String password);
-
3.實戰(zhàn)
-
3.1 初始化全局Retrofit對象
Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://wanandroid.com/") .callFactory(new OkHttpClient.Builder() .addNetworkInterceptor(httpLoggingInterceptor) .build()) //必須添加此adapter 用于構(gòu)建Call .addCallAdapterFactory(CallAdapterFactory.INSTANCE) //添加自定義json解析器 .addConverterFactory(GsonConverterFactory.create()) .build(); RetrofitFactory.DEFAULT = retrofit; //可以添加多個,如: RetrofitFactory.OTHERS.put("other",otherRetrofit);
-
3.2 添加請求服務(wù)接口
下面為登錄的 post請求
@FormUrlEncoded @POST("user/login") Call<LoginInfo> getLogin(@Field("username") String username, @Field("password") String password);
-
3.3 發(fā)起請求
是不是很簡單恩伺,媽媽再也不用擔(dān)心Activity銷毀后資源回收導(dǎo)致的NullPointException等問題了
public class MainActivity extends AppCompatActivity { LifecycleProvider provider = AndroidLifecycle.createLifecycleProvider(this); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final Context context = this; RetrofitFactory.create(ApiService.class) .getLogin("loginName", "password") //.bindUntilDestroy(provider) Activity銷毀時取消請求 .bindToLifecycle(provider, Lifecycle.Event.ON_STOP) .enqueue(new DefaultCallback<LoginInfo>() { @Override public void onStart(Call<LoginInfo> call) { showLoading(); } @Override public void onError(Call<LoginInfo> call, HttpError error) { Toast.makeText(context, error.msg, Toast.LENGTH_SHORT).show(); } @Override public void onSuccess(Call<LoginInfo> call, LoginInfo loginInfo) { Toast.makeText(context, "登錄成功赴背!", Toast.LENGTH_SHORT).show(); //do... } @Override public void onCompleted(Call<LoginInfo> call, @Nullable Throwable t){ hideLoading(); } }); } }
-
-
4.注意事項
4.1 構(gòu)建retrofit是需要CallAdapterFactory實例,否則無法處理返回為Call的服務(wù)接口
-
4.2
Callback
的回調(diào)函數(shù)均在主線程執(zhí)行,如果Call綁定了生命周期觸發(fā)了cancel()
方法UI回調(diào)方法均不會執(zhí)行凰荚,如果要監(jiān)聽那些請求被取消了燃观,可以設(shè)置
RetrofitFactory.LISTENER
屬性,其為一個全局的監(jiān)聽器OnEventListener
便瑟。
-
5.下載
dependencies { implementation 'com.xcheng:retrofit-helper:1.5.5' }
? github地址: retrofit-helper
License
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.