1. 程式人生 > >Android Retrofit取消請求異常分析

Android Retrofit取消請求異常分析

Retrofit取消請求封裝和分析處理

   此篇文章適合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中進行判斷和處理也是進行的這個判斷處理,這樣就可以放心的使用了,歡迎大家一起探討。