继续趁热!!!Retrofit2使用(基于RxJava2.0)

引言

    hello 大家好,没想到吧!我又回来了,回来了,来了,了(武器大师的话,我加了个特技,哈哈)。
    好了开始我们今天的主题吧,昨天有人问我,该用什么网络框架,怎么用,怎么保证内存不泄露?
    看到这个,我还是很激动的,毕竟是我写博客到现在第一个问我的问题。
    那好,我就拿我经常用的网络框架(Retrofit),慢慢欣赏吧(买好瓜子哈)。

OKHTTP

    总结一句话-->"略",哈哈,别打我,我还是个孩子。
    这个不是今天的重点,而且这个东西出来这么久了,大家估计都用了,所以我就不讲了,大家自己脑补吧(google)。

Retrofit版本介绍

    Retrofit是squareup公司完成的一个很吊的开源的网络框架,为什么这样说呢?,因为大家都说好啊。
    其实这里我也想略的,但是又觉得不好意思,所以这次我扔个连接给你们,接住哈!!!

http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0915/3460.html

Retrofit2使用

    想必大家看了上面的文章,肯定不会再去使用retrofit1.X版本了,所以我直接就讲Retrofit2.X喽,好了,开始啦!!!
    首先创建一个新的工程(名字随便你们喽)打开app/build.gradle(感觉有点多余哈,哈哈),在dependencies节点下面添加
    (当然大家可以不按照我这个来)
    //compile ‘com.squareup.retrofit2:retrofit:2.1.0‘
    //retrofit2 转化器 gson
    compile ‘com.squareup.retrofit2:converter-gson:2.1.0‘
    //引进Rxjava2.0
    compile ‘io.reactivex.rxjava2:rxandroid:2.0.0‘
    //log拦截器
    compile ‘com.squareup.okhttp3:logging-interceptor:3.3.0‘
注意:一定要保证添加的版本相同,要不然会有重复包或者冲突呦,直接按照我这个来得了哈,安全,绿色,多好啊。
这里呢大家可能看到了,我为什么把retrofit2.1.0注释掉了?
因为大部分我们都是使用Gson解析,我就直接引入了converter-gson。
这个就是可以直接把返回的数据通过gson解析成对象(都懂的)。
我们打开此包的pom文件(看下面),他已经引进了retrofit2.0版本,所以就不需要喽,以下同理哈(rxandroid- rxjava)

好了,大家可能也看到我也引进Rxjava2(rxandroid:2.0.0)了,这个留到最后。包引进之后呢,下面就可以撸代码了。

我们创建一个NetClient.java,大家看名字就知道了,好了直接上代码

package com.lingchen.netretrofit.net;

import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;

import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

/**
 * Author    lingchen
 * Email     [email protected]
 * Time      2016/11/4
 * Function  网络客户端
 */

public class NetClient {
    //暂时放在这里
    private static final String URL = "http://bobo.yimwing.com";

    /**
     * 获取Retrofit适配器。
     *
     * @return 网络适配器
     */
    public static Retrofit newRetrofit() {
        return new Retrofit.Builder().baseUrl(URL).client(getClient().build())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }

    /**
     * 获取 OkHttpClient
     *
     * @return OkHttpClient
     */
    public static OkHttpClient.Builder getClient() {
        return new OkHttpClient.Builder()
                .connectTimeout(10, TimeUnit.SECONDS)
                .writeTimeout(10, TimeUnit.SECONDS)
                .readTimeout(300, TimeUnit.SECONDS)
                .addInterceptor(new HttpLoggingInterceptor());//日志

    }
}
    嗯,比较简单的,注释也很清楚哈。现在我们来写一个接口(不要问为什么哈,因为retrofit就是这样规定的,嘻嘻)。
package com.lingchen.netretrofit.net;

/**
 * Author    lingchen
 * Email     [email protected]
 * Time      2016/11/4
 * Function  网络接口 Retrofit().create()
 */

public interface ComInterface {

}
我们再来写一个类,来存放Retrofit实例。
/**
 * Author    lingchen
 * Email     [email protected]
 * Time      2016/11/4
 * Function  网络通用接口
 */

public class ComApi {

    public static ComApi comApi;
    private ComInterface mComInterface;

    public static ComApi getInstance() {
        synchronized (ComApi.class) {
            if (comApi == null) {
                comApi = new ComApi();
            }
        }
        return comApi;
    }

    private ComApi() {
        mComInterface = NetClient.newRetrofit().create(ComInterface.class);
    }
}
好了这里大家可以看到了,此类为单例模式,而且含有retrofit创建出来的mComInterface(这就是retrofit的强大之后了)。
相比以前做过后台,使用过SpringMVC大家肯定会觉得很熟悉,貌似现在还看不出来,哈哈,别着急,现在还不是没有请求吗?)
    准备工作准备了,我们开始来写个请求吧,这是我从公司挑来的,好,我们看下接口
    http://bobo.yimwing.com/api/version/android_new
    当我们拿到这个接口的时候,我们就应该去浏览器看看喽

{“code”:1,”data”:{“versionName”:”1.3.0”,”versionCode”:4,”isQiangzhi”:1,”url”:”http:\/\/bobo-sql.oss-cn-beijing.aliyuncs.com\/app-bobo-release.apk”,”updateContent”:”重要版本更新!”}}

这个就是大家常见的json数据啦,我们使用AS一个插件(GsonFormat)具体安装大家可以google哈。我已经安装了,我直接用啦

很明了吧?可以看到code=1肯定就是成功了,data里面存放就是版本的内容了,我们直接点击ok(要预先创建一个新类哈)

这里我们就要说下了,从这里就可以看到,后台返回的固定格式是int code,Object data(其实应该还有String msg,只是这个接口没返回而已)这里我之所以用Object,是因为不可能所有的返回类型都是我们版本数据,所以我们就要开始提取了

package com.lingchen.netretrofit.net;

/**
 * Author    lingchen
 * Email     838878458@qq.com
 * Time      2016/11/4
 * Function  数据响应模型
 */

public class ResBaseModel<T> {
    private static final int CODE_SUCCESS = 1;

    /**
     * data : {}
     * code : 1
     * msg : 操作成功
     */

    private int code;
    private String msg;
    protected T data;

    public String getMsg() {
        return msg;
    }

    public T getData() {
        return data;
    }

    public boolean isSuccess() {
        return CODE_SUCCESS == code;
    }
}
package com.lingchen.netretrofit.net.model;

/**
 * Author    lingchen
 * Email     [email protected]
 * Time      2016/11/4
 * Function  版本信息
 */

public class VersionModel {

    /**
     * versionName : 1.3.0
     * versionCode : 4
     * isQiangzhi : 1
     * url : http://bobo-sql.oss-cn-beijing.aliyuncs.com/app-bobo-release.apk
     * updateContent : 重要版本更新!
     */

    private String versionName;
    private int versionCode;
    private int isQiangzhi;
    private String url;
    private String updateContent;

    public String getVersionName() {
        return versionName;
    }

    public void setVersionName(String versionName) {
        this.versionName = versionName;
    }

    public int getVersionCode() {
        return versionCode;
    }

    public void setVersionCode(int versionCode) {
        this.versionCode = versionCode;
    }

    public int getIsQiangzhi() {
        return isQiangzhi;
    }

    public void setIsQiangzhi(int isQiangzhi) {
        this.isQiangzhi = isQiangzhi;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUpdateContent() {
        return updateContent;
    }

    public void setUpdateContent(String updateContent) {
        this.updateContent = updateContent;
    }

}

好了,我把我现在的架构发下

好了(大家肯定没看到CheckVErsionActivity,肯定没看到,谁会想到我事先做了测试,哈哈,也没办法,毕竟用了Rxjava2.0,上个月刚出来,太多坑哈)。

下面开始写我们的接口了,首先我们打开我们的ComInterface,在里面写下如下代码

    @GET("/api/version/android_new")
    Call<ResBaseModel<VersionModel>> checkVersion();
使用过SpringMVC的兄弟们,是不是感觉很亲近啊?哈哈,一开始我也是觉得很亲切,毕竟我还搞过半年的后台,好吧跑题了。
接下来我们在ComApi写上一个方法
/**
     * 检测版本
     *
     * @return 回调
     */
    public Call<ResBaseModel<VersionModel>> checkVersion() {
        return mComInterface.checkVersion();
    }
好了,也没啥东西,这里可能对retrofit没有太多的讲解,不懂的兄弟可以自行去google哈(见谅哈)
我们现在写个activity来试试吧
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="start"
        android:text="请求" />

    <TextView
        android:id="@+id/tv_result"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:hint="请求结果" />

</LinearLayout>
/**
 * Author    lingchen
 * Email     [email protected]
 * Time      2016/11/4
 * Function  测试校验版本
 * http://bobo.yimwing.com/api/version/android_new
 */

public class CheckVersionActivity extends AppCompatActivity {
    private TextView resultTv;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_check_version);
        resultTv = (TextView) findViewById(R.id.tv_result);
    }

    //开始请求
    public void start(View view) {
        Call<ResBaseModel<VersionModel>> call = ComApi.getInstance().checkVersion();
        call.enqueue(new Callback<ResBaseModel<VersionModel>>() {//异步请求
            @Override
            public void onResponse(Call<ResBaseModel<VersionModel>> call, final Response<ResBaseModel<VersionModel>> response) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        if (response.isSuccessful() && response.body().isSuccess()) {
                            resultTv.setText(response.body().getData().getUpdateContent());
                        } else {
                            resultTv.setText("失败");
                        }
                    }
                });
            }

            @Override
            public void onFailure(Call<ResBaseModel<VersionModel>> call, final Throwable t) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        resultTv.setText(t.getMessage());
                    }
                });
            }
        });
    }
  }
开始运行吧

成功了。

RxJava2使用

        这里直接就给大家几篇文章,挺不错的,毕竟我也看了,哈哈。

http://gank.io/post/560e15be2dca930e00da1083

http://www.dieyidezui.com/qian-tan-rxjavayu-2-0de-xin-te-xing/

我也写了几个例子,也给大家看看哈

//Rxjava1.x
        Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> e) throws Exception {
                e.onNext("凌晨");
                e.onNext("是");
                e.onNext("大佬");
                e.onError(new Throwable("给大佬低头"));
            }
        }).onErrorResumeNext(Observable.<String>empty()).subscribe();

        //Rxjava2.x
        Flowable.create(new FlowableOnSubscribe<String>() {
            @Override
            public void subscribe(FlowableEmitter<String> e) throws Exception {
                e.onNext("凌晨");
                e.onNext("是");
                e.onNext("大佬");
                e.onError(new Throwable("给大佬低头"));
            }
        }, BackpressureStrategy.BUFFER).doOnNext(new Consumer<String>() {
            @Override
            public void accept(String s) throws Exception {
                Log.e(TAG, "accept() called with: s = [" + s + "]");
            }
        }).doOnError(new Consumer<Throwable>() {
            @Override
            public void accept(Throwable throwable) throws Exception {
                Log.e(TAG, "accept() called with: throwable = [" + throwable.getMessage() + "]");
            }
        }).onErrorResumeNext(Flowable.<String>empty()).subscribe(new Consumer<String>() {
            @Override
            public void accept(String s) throws Exception {
                Log.e(TAG, "accept() called with: s = [" + s + "]");
            }
        });
        Flowable.just(R.mipmap.ic_launcher)
                .map(new Function<Integer, Bitmap>() {

                    @Override
                    public Bitmap apply(Integer integer) throws Exception {
                        return BitmapFactory.decodeResource(getResources(), integer);
                    }
                }).filter(new Predicate<Bitmap>() {
            @Override
            public boolean test(Bitmap bitmap) throws Exception {
                return bitmap != null;
            }
        }).doOnNext(new Consumer<Bitmap>() {
            @Override
            public void accept(Bitmap bitmap) throws Exception {
                Log.e(TAG, "accept() called with: bitmap = [" + bitmap.getWidth() + "]");
            }
        }).onErrorResumeNext(Flowable.<Bitmap>empty())
                .subscribe();
哈哈,就是今天自己去看了下,结合官方wiki,写了2个例子,变化还是蛮大的,好了,大家自己看吧。

Retrofit2+Rxjava2

    我直接上个代码了
//开始请求
    public void start(View view) {
        Flowable.create(new FlowableOnSubscribe<ResBaseModel<VersionModel>>() {
            @Override
            public void subscribe(final FlowableEmitter<ResBaseModel<VersionModel>> e) throws Exception {
                Call<ResBaseModel<VersionModel>> call = ComApi.getInstance().checkVersionCall();
                call.enqueue(new Callback<ResBaseModel<VersionModel>>() {//异步请求
                    @Override
                    public void onResponse(Call<ResBaseModel<VersionModel>> call, final Response<ResBaseModel<VersionModel>> response) {
                        if (!e.isCancelled()) {
                            e.onNext(response.body());
                        }
                        call.cancel();
                        e.onComplete();
                    }

                    @Override
                    public void onFailure(Call<ResBaseModel<VersionModel>> call, final Throwable t) {
                        if (!e.isCancelled()) {
                            e.onError(t);
                        }
                        call.cancel();
                        e.onComplete();
                    }
                });
            }
        }, BackpressureStrategy.BUFFER)
                .doOnNext(new Consumer<ResBaseModel<VersionModel>>() {
                    @Override
                    public void accept(ResBaseModel<VersionModel> versionModelResBaseModel) throws Exception {

                        if (versionModelResBaseModel.isSuccess()) {
                            resultTv.setText(versionModelResBaseModel.getData().getUpdateContent());
                        } else {
                            resultTv.setText("请求失败");
                        }
                    }
                }).subscribeOn(new IoScheduler())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnError(new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        resultTv.setText(throwable.getMessage());
                    }
                })
                .onErrorResumeNext(Flowable.<ResBaseModel<VersionModel>>empty())
                .subscribe();
    }
好了,想必大家肯定看的很丑,其实也没办法,为啥呢?因为Rxjava2还没出来retrofit-adapter,大家敬请期待吧。我自己封装了下,给大家看下。
package com.lingchen.netretrofit.net;

import android.support.annotation.NonNull;

import org.reactivestreams.Publisher;

import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import io.reactivex.FlowableEmitter;
import io.reactivex.FlowableOnSubscribe;
import io.reactivex.FlowableTransformer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.internal.schedulers.IoScheduler;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

/**
 * Author    lingchen
 * Email     [email protected]
 * Time      2016/11/4
 * Function  通用接口基类
 * 封装线程调度
 */

public class BaseComApi {

    /**
     * 自己封装的方法,来减少代码亮
     *
     * @param call retrofit的call
     * @param <T>  泛型
     * @return Flowable
     */
    public static <T> Flowable<T> create(@NonNull final Call<T> call) {
        return Flowable.create(new FlowableOnSubscribe<T>() {
            @Override
            public void subscribe(final FlowableEmitter<T> e) throws Exception {
                call.enqueue(new Callback<T>() {
                    @Override
                    public void onResponse(Call<T> call, Response<T> response) {
                        e.onNext(response.body());
                        e.onComplete();
                        call.cancel();
                    }

                    @Override
                    public void onFailure(Call<T> call, Throwable t) {
                        e.onError(t);
                        e.onComplete();
                        call.cancel();
                    }
                });
            }
        }, BackpressureStrategy.BUFFER);
    }

    /**
     * 后台线程执行同步,主线程执行异步操作
     * 并且拦截所有错误,不让app崩溃
     *
     * @param <T> 数据类型
     * @return Transformer
     */
    public static <T> FlowableTransformer<T, T> background() {
        return new FlowableTransformer<T, T>() {
            @Override
            public Publisher<T> apply(Flowable<T> upstream) {
                return upstream.subscribeOn(new IoScheduler())
                        .observeOn(AndroidSchedulers.mainThread())
                        .onErrorResumeNext(Flowable.<T>empty());
            }
        };
    }

}

那我们再来看看我们的代码调用

 /**
     * 检测版本
     */
    public Flowable<ResBaseModel<VersionModel>> checkVersion() {
        return create(mComInterface.checkVersion())
                .compose(BaseComApi.<ResBaseModel<VersionModel>>background());
    }
//开始请求
    public void start(View view) {
        ComApi.getInstance().checkVersion()
                .doOnNext(new Consumer<ResBaseModel<VersionModel>>() {
                    @Override
                    public void accept(ResBaseModel<VersionModel> versionModelResBaseModel) throws Exception {
                        if (versionModelResBaseModel.isSuccess()) {
                            resultTv.setText(versionModelResBaseModel.getData().getUpdateContent());
                        } else {
                            resultTv.setText("请求失败");
                        }
                    }
                })
                .doOnError(new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        resultTv.setText(throwable.getMessage());
                    }
                })
                .subscribe();
    }
是不是代码少了好多?嘻嘻,如果使用lambda表达式,更简介(好希望squareup公司赶紧出retrofit2支持Rxjava2的adapter)

网络生命周期管理

    这个是老生长叹的问题了,用过rxjava1.x都知道调用subscribe之后会返回Subscription,然后利用

CompositeSubscription进行统一管理

    //防止RX 导致的内存泄漏
    protected CompositeSubscription subscription = new CompositeSubscription();
    //添加订阅 由 subscription 管理
    protected void add(Subscription sub) {
        if (this.subscription != null && sub != null) {
            this.subscription.add(sub);
        }
    }
    //移除订阅,并且取消订阅
    protected void remove(Subscription sub) {
        if (this.subscription != null && sub != null) {
            this.subscription.remove(sub);
        }
    }
    //移除所有订阅,并且取消订阅
    protected void remove() {
        if (this.subscription != null && !this.subscription.isUnsubscribed()) {
            this.subscription.unsubscribe();
        }
    }
    那到了Rxjava2.0又怎么管理呢?我们还是看下我们调用subscribe之后返回的是什么吧
    @BackpressureSupport(BackpressureKind.UNBOUNDED_IN)
    @SchedulerSupport(SchedulerSupport.NONE)
    public final Disposable subscribe() {
        return subscribe(Functions.emptyConsumer(), Functions.ERROR_CONSUMER,
                Functions.EMPTY_ACTION, FlowableInternalHelper.RequestMax.INSTANCE);
    }
   /**
     * Represents a disposable resource.
     */
    public interface Disposable {
    /**
     * Dispose the resource, the operation should be idempotent.
     * 处理资源
     */
    void dispose();

    /**
     * Returns true if this resource has been disposed.
     * @return true if this resource has been disposed
     * 是否处理资源
     */
    boolean isDisposed();
}

/**
 * Common interface to add and remove disposables from a container.
 * @since 2.0
 */
public interface DisposableContainer {

    /**
     * Adds a disposable to this container or disposes it if the
     * container has been disposed.
     * @param d the disposable to add, not null
     * @return true if successful, false if this container has been disposed
     */
    boolean add(Disposable d);

    /**
     * Removes and disposes the given disposable if it is part of this
     * container.
     * @param d the disposable to remove and dispose, not null
     * @return true if the operation was successful
     */
    boolean remove(Disposable d);

    /**
     * Removes (but does not dispose) the given disposable if it is part of this
     * container.
     * @param d the disposable to remove, not null
     * @return true if the operation was successful
     */
    boolean delete(Disposable d);
}
看到这里,是不是都晓得了?对了DisposableContainer 是一个接口,那我们看看他们的子类
/**
 * A disposable container that can hold onto multiple other disposables.
 */
public final class ListCompositeDisposable implements Disposable, DisposableContainer {

    List<Disposable> resources;

    volatile boolean disposed;

    public ListCompositeDisposable() {
    }
   ***
 }
/**
 * A disposable container that can hold onto multiple other disposables and
 * offers O(1) add and removal complexity.
 */
public final class CompositeDisposable implements Disposable, DisposableContainer {

    OpenHashSet<Disposable> resources;

    volatile boolean disposed;

    /**
     * Creates an empty CompositeDisposable.
     */
    public CompositeDisposable() {
    }
    ***
}
我贴出了关键的代码,2个子类使用了不同的数据结构存储,利弊,大家自己选择吧。
好了我们写个BaseActivity,这里我使用ListCompositeDisposable
/**
 * Author    lingchen
 * Email     [email protected]
 * Time      2016/11/4
 * Function  所有界面基类
 */

public class BaseActivity extends AppCompatActivity {
    private ListCompositeDisposable listCompositeDisposable = new ListCompositeDisposable();

    protected void addDisposable(Disposable disposable) {
        if (disposable != null && !disposable.isDisposed()) {
            listCompositeDisposable.add(disposable);
        }
    }

    protected void reDisposable(Disposable disposable) {
        if (disposable != null) {
            listCompositeDisposable.remove(disposable);
        }
    }

    protected void clear() {
        if (!listCompositeDisposable.isDisposed()) {
            listCompositeDisposable.clear();
        }
    }

    @Override
    protected void onDestroy() {
        clear();
        super.onDestroy();
    }
}
    我们修改下刚才的请求
//开始请求
    public void start(View view) {
        //交给父类管理生命周期
        addDisposable(ComApi.getInstance().checkVersion()
                .doOnNext(new Consumer<ResBaseModel<VersionModel>>() {
                    @Override
                    public void accept(ResBaseModel<VersionModel> versionModelResBaseModel) throws Exception {
                        if (versionModelResBaseModel.isSuccess()) {
                            resultTv.setText(versionModelResBaseModel.getData().getUpdateContent());
                        } else {
                            resultTv.setText("请求失败");
                        }
                    }
                })
                .doOnError(new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        resultTv.setText(throwable.getMessage());
                    }
                })
                .subscribe());
    }
我擦,差点忘记重点,在封装call转成Flowable的时候没有加上判断,上代码,你们能看懂的
                         Log.e(TAG, "onResponse: ");
                        if (!e.isCancelled()) {
                            Log.e(TAG, "onResponse: no cancel");
                            e.onNext(response.body());
                            e.onComplete();
                        }
                        call.cancel();
onFailure我就不放了 一样的,那好,我们现在写个测试一下,我们在开启请求的时候写一个延迟调用父类的clear,代码如下
 //开始请求
    public void start(View view) {
        //交给父类管理生命周期
        addDisposable(ComApi.getInstance().checkVersion()
                .doOnNext(new Consumer<ResBaseModel<VersionModel>>() {
                    @Override
                    public void accept(ResBaseModel<VersionModel> versionModelResBaseModel) throws Exception {
                        if (versionModelResBaseModel.isSuccess()) {
                            resultTv.setText(versionModelResBaseModel.getData().getUpdateContent());
                        } else {
                            resultTv.setText("请求失败");
                        }
                    }
                })
                .doOnError(new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        resultTv.setText(throwable.getMessage());
                    }
                })
                .subscribe());
        //延迟0.1s取消
        resultTv.postDelayed(new Runnable() {
            @Override
            public void run() {
                clear();
            }
        }, 100);
    }
好,我们运行下(这里就不妨图片了,因为大家都在吐槽图片看不清,而且不能放大)
11-04 18:22:45.304 21372-21372/com.lingchen.netretrofit E/BaseComApi: onResponse: 

大家看到了只有onResponse回调,那么意思走到 if (!e.isCancelled()),我们取消了,所以没有打印onResponse: no cancel,

我们现在把延迟去掉或者延迟时间长一点,在运行下

11-04 18:26:35.631 24805-24805/com.lingchen.netretrofit E/BaseComApi: onResponse:
11-04 18:26:35.631 24805-24805/com.lingchen.netretrofit E/BaseComApi: onResponse: no cancel

哈哈,大家看懂了吧

到这里大家可能会问,那我重复点击发送,是什么样子呢?我们运行下

11-04 18:26:55.354 24805-24805/com.lingchen.netretrofit E/BaseComApi: onResponse:
11-04 18:26:55.355 24805-24805/com.lingchen.netretrofit E/BaseComApi: onResponse: no cancel
11-04 18:26:55.472 24805-24805/com.lingchen.netretrofit E/BaseComApi: onResponse:
11-04 18:26:55.472 24805-24805/com.lingchen.netretrofit E/BaseComApi: onResponse: no cancel

可以发现,确实回来了2次,那我们现在想做的是,如果多次点击,那就取消前面发送的,这个东西呢我们只能自己去封装,去实现DisposableContainer 这个接口,大体的意思可以为发送请求加一个Tag标识符,每当我们add(Disposable )时候,我们就看看我们存储的Disposable有没有标识符是Tag的,如果有,那就直接取消吧,嗯,大体就是这个样子。

结束语

    好了,搞腾了一天了,终于搞定了。感谢大家对我前2个博客的支持,谢谢大家。
    Rxjava2.0刚出来,以后我会出个针对Rxjava2.0的文章。
    下篇文章可能会出retrofit相关的(上传/下载图片或者文件等)!!!
    拜拜喽(今天周五了哈)!!!!下面给出源码

https://github.com/CFlingchen/CSDN_NetRetrofit

感谢&完善

    感谢"yuke"对我这次封装的提的意见(以前公司的同事),我们来看看原来封装的代码:
/**
     * 自己封装的方法,来减少代码亮
     *
     * @param call retrofit的call
     * @param <T>  泛型
     * @return Flowable
     */
    public static <T> Flowable<T> create(@NonNull final Call<T> call) {
        return Flowable.create(new FlowableOnSubscribe<T>() {
            @Override
            public void subscribe(final FlowableEmitter<T> e) throws Exception {
                call.enqueue(new Callback<T>() {
                    @Override
                    public void onResponse(Call<T> call, Response<T> response) {
                        Log.e(TAG, "onResponse: ");
                        if (!e.isCancelled()) {
                            Log.e(TAG, "onResponse: no cancel");
                            e.onNext(response.body());
                            e.onComplete();
                        }
                    }

                    @Override
                    public void onFailure(Call<T> call, Throwable t) {
                        Log.e(TAG, "onFailure() called with: , t = [" + t + "]");
                        if (!e.isCancelled()) {
                            Log.e(TAG, "onResponse: no cancel");
                            e.onError(t);
                            e.onComplete();
                        }
                    }
                });
            }
        }, BackpressureStrategy.BUFFER);
    }
第一个问题:我们这里使用的是 call.enqueue,这是异步的调用方法,很明显就没有跟Rxjava线程管理搭上边,所以我们修正下(只是把异步执行转成同步,把线程分配交给Rxjava)。
第二个问题:这个也不是问题吧,是个改善。大家可以看看上面的代码,我们不管你取消不取消当前的网络请求,我们都会去请求网络,只是在回调的时候我们再去判断是否取消了订阅。我们下面假设下面2种情况
    1.网络请求还没发送,我们就取消订阅了。(其实这种情况是不可能发生的,除非你在发送请求的线程做个sleep。这种情况我们就不考虑了,因为是我们自己封装的,也不会在发送请求之前还延迟,所以就不考虑这种情况了)。
    2.网络请求发送了,还没有回调,我们现在取消订阅。我们手动调用call.cancel();就会触发onFailure,异常是sokcet closed(异步,同步是 thread interrupted),那就跟我们以前的做法一样,都是在网络回调做处理,但是,如果我在取消订阅的时候,立马执行call.cancel(),带来的好处就是,会立马触发onFailure,不用等待网络请求回调了(毕竟都不知道会什么时候回调),所以这就体现我手动调用call.cancel()的好处。那好,我们现在上最终的代码:
/**
     * 自己封装的方法,来减少代码亮
     *
     * @param call retrofit的call
     * @param <T>  泛型
     * @return Flowable
     */
    public static <T> Flowable<T> create(@NonNull final Call<T> call) {
        return Flowable.create(new FlowableOnSubscribe<T>() {
            @Override
            public void subscribe(final FlowableEmitter<T> e) throws Exception {
                //设置取消监听
                e.setCancellable(new Cancellable() {
                    @Override
                    public void cancel() throws Exception {
                        Log.e(TAG, "cancel: ");
                        if (!call.isCanceled()) {
                            call.cancel();
                        }
                    }
                });
                //同步执行请求,把线程管理交给Rx
                try {
                    Response<T> response = call.execute();
                    Log.e(TAG, "onResponse: ");
                    if (!e.isCancelled()) {
                        e.onNext(response.body());
                        e.onComplete();
                    }
                } catch (Exception exception) {
                    Log.e(TAG, "exception with: exception = [" + exception.getMessage() + "]");
                    if (!e.isCancelled()) {
                        Log.e(TAG, "onResponse: no cancel");
                        e.onError(exception);
                        e.onComplete();
                    }
                }
            }
        }, BackpressureStrategy.BUFFER);
    }
    好了,这里再次感谢"yuke",也希望大家多多提提意见,一起成长!!!代码已经同步到github。
    拜拜!!!
时间: 2024-10-10 05:54:15

继续趁热!!!Retrofit2使用(基于RxJava2.0)的相关文章

redis集群热扩展(基于4.0.9)

1:环境说明,首先说一下要做的事情,我们要迁移redis集群槽位,现有redis集群环境如下 172,16.128.240:7003 172,16.128.240:7004 172,16.128.241:7003 172,16.128.241:7004 172,16.128.242:7003 172,16.128.242:7004 我们看一下集群的基本信息: 172,16.128.240:7003> cluster nodes 8ea64a0049e0b193296aeba868391e7b00

基于vue2.0的一个豆瓣电影App

1.搭建项目框架 使用vue-cli 没安装的需要先安装 npm intall -g vue-cli 使用vue-cli生成项目框架 vue init webpack-simple vue-movie 然后一路回车 接着 进入项目目录 cd vue-movie 然后安装项目依赖包 cnpm install 没安装cnpm的先执行这个命令 npm install -g cnpm --registry=https://registry.npm.taobao.org 接着 npm run dev 看到

给初学者的RxJava2.0教程(二)

前言 上一节教程讲解了最基本的RxJava2的使用, 在本节中, 我们将学习RxJava强大的线程控制. 正题 还是以之前的例子, 两根水管: RxJava 正常情况下, 上游和下游是工作在同一个线程中的, 也就是说上游在哪个线程发事件, 下游就在哪个线程接收事件. 怎么去理解呢, 以Android为例, 一个Activity的所有动作默认都是在主线程中运行的, 比如我们在onCreate中打出当前线程的名字: @Override protected void onCreate(Bundle s

RxJava2.0的使用详解

RxJava2.0的使用详解 1,初识RxJava RxJava就是一种用Java语言实现的响应式编程,来创建基于事件的异步程序 RxJava是一个基于事件订阅的异步执行的一个类库,目前比较火的一些技术框架! 参考资料: Github上RxJava的项目地址: https://github.com/ReactiveX/RxJava 技术文档Api: http://reactivex.io/RxJava/javadoc/ RxAndroid,用于 Android 开发: https://githu

使用RxPermissions(基于RxJava2)

使用RxPermissions(基于RxJava2) 0. 背景 Android 6.0 (API level 23)中.将权限分成了两类.一类是Install权限(称之为安装时权限).还有一类是Runtime权限(称之为执行时权限). Install权限是什么? Install权限:安装时权限,顾名思义,是在安装app时.就赋予该app的权限,即安装后马上获取到的权限.normal和signature级别的权限都是安装时权限.赋予app normal和signature权限时,不会给用户提示界

红米3(ido)基于6.0.1适配第三方中文jirecovery/TWRP 3.0.2/刷遍所有ROM/官方不卡屏

TWRP3.0.2更新简介: TWRP是TeamWin团队https://github.com/TeamWin/Team-Win-Recovery-Project的开源项目,也是Omnirom系统默认的android_bootable_recovery. 红米3-TWRP-21060823更新日志: 1.基于Aink1199的20160805-cm13最新6.01内核制作. 2.基于omnirom-android-6.0适配. 3.加入禁止恢复官方recovery选项(刷完机重启时会提示). 4

基于Cocos2d-x-1.0.1的飞机大战游戏开发实例(下)

在飞机大战游戏开发中遇到的问题和解决方法: 1.在添加菜单时,我要添加一个有背景的菜单,需要在菜单pMenu中添加一个图片精灵,结果编译过了但是运行出错,如下图: 查了很多资料,调试了很长时间,整个人都要崩溃了. 最后发现引擎中CCMenu::itemForTouch函数中有遍历子节点的行为,但是循环中没有判断子节点类型是否为CCMenuItem.如图:码,这样一来,加入到pMenu中的图片精灵被当作菜单项取了出来使用,导致报错.老版本的果然又不完善的地方,整个人都不好了...果断修改引擎里的源

WmS详解(二)之如何理解Window和窗口的关系?基于Android7.0源码

上篇博客(WmS详解(一)之token到底是什么?基于Android7.0源码)中我们简要介绍了token的作用,这里涉及到的概念非常多,其中出现频率最高的要数Window和窗口这一对搭档了,那么我们今天就来看看到底我们该如何理解Android系统中的Window和窗口. 窗口这个概念,从不同的角度来看它的含义不一样,如果我们从WmS(WindowManagerService)的角度来看窗口,那么这个窗口并不是一个Window类,而是一个View.用户发来的消息被WmS接收之后并不能直接发给各个

iOS_SN_基于AFNetworking3.0网络封装

转发文章,原地址:http://www.henishuo.com/base-on-afnetworking3-0-wrapper/?utm_source=tuicool&utm_medium=referral 前言 对于开发人员来说,学习网络层知识是必备的,任何一款App的开发,都需要到网络请求接口.很多朋友都还在使用原生的NSURLConnection一行一行地写,代码到处是,这样维护起来更困难了. 对于使用AFNetworking的朋友来说,很多朋友都是直接调用AFNetworking的AP