Android Retrofit取消請求異常分析
阿新 • • 發佈:2019-01-05
Retrofit取消請求封裝和分析處理
我們發現rxjava中進行判斷和處理也是進行的這個判斷處理,這樣就可以放心的使用了,歡迎大家一起探討。
此篇文章適合okhttp和retrofit使用者,如果不是請觀看其他文章,如果是的那你就發達了,賺到了。
首先我們在封裝網路請求的時候會遇到activity等檢視已經關閉,然而網路請求並沒有關閉的現象,畢竟網路請求為非同步處理和載入,不處理就會造成異常。那麼接下來我們就需要管理和處理這個現象,那麼怎麼處理呢,請繼續看文章分析。
一:管理和處理call
需要在baseactivity和basefragment中進行封裝call拿到call放入list集合,進行有序放入和有序取消,在onDestroy中進行統一取消請求,這樣就避免了,空指標異常。如下程式碼所示:
import android.content.Context; import android.graphics.Color; import android.os.Bundle; import android.os.IBinder; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.view.MotionEvent; import android.view.View; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import com.jaeger.library.StatusBarUtil; import com.umeng.message.PushAgent; import java.util.ArrayList; import java.util.List; import butterknife.ButterKnife; import butterknife.Unbinder; import edu.com.gaiwen.firstchoice.utils.AppManager; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.Disposable; import retrofit2.Call; /** * Created by xfc on 2017/6/5. * 用於activity基類 */ public abstract class BaseActivityxfc extends AppCompatActivity implements View.OnClickListener { private Unbinder unbind; private CompositeDisposable compositeDisposable; private List<Call> calls; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(getLayoutId()); unbind = ButterKnife.bind(this); StatusBarUtil.setColor(this, Color.parseColor("#0ab6d8")); PushAgent.getInstance(this).onAppStart(); AppManager.getAppManager().addActivity(this); onView(); } public void addCalls(Call call) { if (calls == null) { calls = new ArrayList<>(); } calls.add(call); } private void callCancel() { if (calls != null && calls.size() > 0) { for (Call call : calls) { if (!call.isCanceled()) call.cancel(); } calls.clear(); } } public void addDisposable(Disposable disposable) { if (compositeDisposable == null) { compositeDisposable = new CompositeDisposable(); } compositeDisposable.add(disposable); } protected void cancelDispose() { if (compositeDisposable != null) { compositeDisposable.dispose(); } } @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { View v = getCurrentFocus(); if (isShouldHideKeyboard(v, ev)) { hideKeyboard(v.getWindowToken()); } } return super.dispatchTouchEvent(ev); } /** * 根據EditText所在座標和使用者點選的座標相對比,來判斷是否隱藏鍵盤,因為當用戶點選EditText時則不能隱藏 * * @param v * @param event * @return */ private boolean isShouldHideKeyboard(View v, MotionEvent event) { if (v != null && (v instanceof EditText)) { int[] l = {0, 0}; v.getLocationInWindow(l); int left = l[0], top = l[1], bottom = top + v.getHeight(), right = left + v.getWidth(); if (event.getX() > left && event.getX() < right && event.getY() > top && event.getY() < bottom) { // 點選EditText的事件,忽略它。 return false; } else { v.clearFocus(); return true; } } // 如果焦點不是EditText則忽略,這個發生在檢視剛繪製完,第一個焦點不在EditText上,和使用者用軌跡球選擇其他的焦點 return false; } /** * 獲取InputMethodManager,隱藏軟鍵盤 * * @param token */ private void hideKeyboard(IBinder token) { if (token != null) { InputMethodManager im = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); im.hideSoftInputFromWindow(token, InputMethodManager.HIDE_NOT_ALWAYS); } } /** * 進行銷燬解綁 */ @Override protected void onDestroy() { cancelDispose(); callCancel(); super.onDestroy(); AppManager.getAppManager().finishActivity(this); if (unbind != null) { unbind.unbind(); } } /** * 載入佈局 */ protected abstract int getLayoutId(); /** * 初始化操作 */ protected abstract void onView(); }
basefragment同理,不需要的程式碼看看就算了,List<Call> calls;主要是這個集合的使用。
二:進行非同步判斷處理
在使用上面判斷中發現這樣處理和管理call,在非同步中依然會走onFailure方法,會爆出異常
@Override public void onFailure(Call<ResponseBody> call, Throwable e) { if (!call.isCanceled()) { catchException(e); onFailure(); } }
(使用okhttp方法略有不同,但功能一致),這樣我們就需要判斷call.isCanceled(),我們發現如果是取消請求,則這個方法會返回為true,如果不是則為false。
三:進行原始碼分析
public void cancel() {
canceled = true;
okhttp3.Call call;
synchronized (this) {
call = rawCall;
}
if (call != null) {
call.cancel();
}
}
@Override public boolean isCanceled() {
if (canceled) {
return true;
}
synchronized (this) {
return rawCall != null && rawCall.isCanceled();
}
}
我們發現只有在取消請求後isCanceled才返回為true,其他狀況下則為false,不放心的話我們看下rxjava裡面進行的處理, @Override protected void subscribeActual(Observer<? super Response<T>> observer) {
// Since Call is a one-shot type, clone it for each new observer.
Call<T> call = originalCall.clone();
observer.onSubscribe(new CallDisposable(call));
boolean terminated = false;
try {
Response<T> response = call.execute();
if (!call.isCanceled()) {
observer.onNext(response);
}
if (!call.isCanceled()) {
terminated = true;
observer.onComplete();
}
} catch (Throwable t) {
Exceptions.throwIfFatal(t);
if (terminated) {
RxJavaPlugins.onError(t);
} else if (!call.isCanceled()) {
try {
observer.onError(t);
} catch (Throwable inner) {
Exceptions.throwIfFatal(inner);
RxJavaPlugins.onError(new CompositeException(t, inner));
}
}
}
}
我們發現rxjava中進行判斷和處理也是進行的這個判斷處理,這樣就可以放心的使用了,歡迎大家一起探討。