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中进行判断和处理也是进行的这个判断处理,这样就可以放心的使用了,欢迎大家一起探讨。