Retrofit和RxJava结合使用例子分析

来源:互联网 发布:圣诞玫瑰结局真相知乎 编辑:程序博客网 时间:2024/05/17 04:05

以GitHub上的一个例子进行分析一下

https://github.com/rengwuxian/RxJavaSamples

先讲下总体架构

分为基本转换(MAP)、压合(ZIP)、TOKEN(FLATMAP)、TOKEN_高级(RETRYWHEN)、缓存(BEHAVIORSUBJECT)。

整体是通过viewpager+fragment进行实现,通过Tablayout和viewpager进行关联,进行网络请求展示相关数据。

定义接口API

<span style="font-size:18px;">public interface ZhuangbiApi {    @GET("search")    Observable<List<ZhuangbiImage>> search(@Query("q") String query);}</span>

定义NetWork类返回API接口的实现类。

<span style="font-size:18px;">public class Network {    private static ZhuangbiApi zhuangbiApi;    private static GankApi gankApi;    private static FakeApi fakeApi;    private static OkHttpClient okHttpClient = new OkHttpClient();    private static Converter.Factory gsonConverterFactory = GsonConverterFactory.create();    private static CallAdapter.Factory rxJavaCallAdapterFactory = RxJavaCallAdapterFactory.create();    public static ZhuangbiApi getZhuangbiApi() {        if (zhuangbiApi == null) {            Retrofit retrofit = new Retrofit.Builder()                    .client(okHttpClient)                    .baseUrl("http://zhuangbi.info/")                    .addConverterFactory(gsonConverterFactory)                    .addCallAdapterFactory(rxJavaCallAdapterFactory)                    .build();            zhuangbiApi = retrofit.create(ZhuangbiApi.class);        }        return zhuangbiApi;    }    public static GankApi getGankApi() {        if (gankApi == null) {            Retrofit retrofit = new Retrofit.Builder()                    .client(okHttpClient)                    .baseUrl("http://gank.io/api/")                    .addConverterFactory(gsonConverterFactory)                    .addCallAdapterFactory(rxJavaCallAdapterFactory)                    .build();            gankApi = retrofit.create(GankApi.class);        }        return gankApi;    }    public static FakeApi getFakeApi() {        if (fakeApi == null) {            fakeApi = new FakeApi();        }        return fakeApi;    }}</span>

基本使用

  <span style="font-size:18px;">Observer<List<ZhuangbiImage>> observer = new Observer<List<ZhuangbiImage>>() {        @Override        public void onCompleted() {        }        @Override        public void onError(Throwable e) {            swipeRefreshLayout.setRefreshing(false);            Toast.makeText(getActivity(), R.string.loading_failed, Toast.LENGTH_SHORT).show();        }        @Override        public void onNext(List<ZhuangbiImage> images) {            swipeRefreshLayout.setRefreshing(false);            adapter.setImages(images);        }    };</span>

在onNext方法执行完成后进行数据的展示。

网络请求

<span style="font-size:18px;"> subscription = Network.getZhuangbiApi()                .search(key)                .subscribeOn(Schedulers.io())                .observeOn(AndroidSchedulers.mainThread())                .subscribe(observer);</span>

转换(MAP)

有些服务端的接口设计,会在返回数据外层包裹一些额外信息,这些信息对于调试很有用,但本地显示是用不到的,使用map()可以把外层的格式去掉,只留下需要显示的数据格式。

以下是网络请求代码

 <span style="font-size:18px;">unsubscribe();//当点击按钮进行网络请求时,先取消然后在进行网络请求        subscription = Network.getGankApi()                .getBeauties(10, page)                .map(GankBeautyResultToItemsMapper.getInstance())                .subscribeOn(Schedulers.io())                .observeOn(AndroidSchedulers.mainThread())                .subscribe(observer);</span>


map的功能类似下图所示:


下面是Func1的实现类

<span style="font-size:18px;">public class GankBeautyResultToItemsMapper implements Func1<GankBeautyResult, List<Item>> {    private static GankBeautyResultToItemsMapper INSTANCE = new GankBeautyResultToItemsMapper();    private GankBeautyResultToItemsMapper() {    }    public static GankBeautyResultToItemsMapper getInstance() {        return INSTANCE;    }    @Override    public List<Item> call(GankBeautyResult gankBeautyResult) {        List<GankBeauty> gankBeauties = gankBeautyResult.beauties;        List<Item> items = new ArrayList<>(gankBeauties.size());        SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SS'Z'");        SimpleDateFormat outputFormat = new SimpleDateFormat("yy/MM/dd HH:mm:ss");        for (GankBeauty gankBeauty : gankBeauties) {            Item item = new Item();            try {                Date date = inputFormat.parse(gankBeauty.createdAt);                item.description = outputFormat.format(date);            } catch (ParseException e) {                e.printStackTrace();                item.description = "unknown date";            }            item.imageUrl = gankBeauty.url;            items.add(item);        }        return items;    }}</span>


http://reactivex.io/documentation/operators/map.html 这篇文章对map进行了介绍。


压合(ZIP)

有时候,app中会需要同时访问不同接口,然后将结果糅合后转为统一的格式后输出。这种并行的异步处理比较麻烦,不过用了zip()之后就会简单得多。

下面是网络请求的代码

 <span style="font-size:18px;">unsubscribe();        subscription = Observable.zip(Network.getGankApi().getBeauties(200, 1).map(GankBeautyResultToItemsMapper.getInstance()),                Network.getZhuangbiApi().search("装逼"),                new Func2<List<Item>, List<ZhuangbiImage>, List<Item>>() {                    @Override                    public List<Item> call(List<Item> gankItems, List<ZhuangbiImage> zhuangbiImages) {                        List<Item> items = new ArrayList<Item>();                        for (int i = 0; i < gankItems.size() / 2 && i < zhuangbiImages.size(); i++) {                            items.add(gankItems.get(i * 2));                            items.add(gankItems.get(i * 2 + 1));                            Item zhuangbiItem = new Item();                            ZhuangbiImage zhuangbiImage = zhuangbiImages.get(i);                            zhuangbiItem.description = zhuangbiImage.description;                            zhuangbiItem.imageUrl = zhuangbiImage.image_url;                            items.add(zhuangbiItem);                        }                        return items;                    }                })                .subscribeOn(Schedulers.io())                .observeOn(AndroidSchedulers.mainThread())                .subscribe(observer);</span>
zip就是将多个Observable糅合成一个Observable。然后在进行数据的展示。

如下图例所示





Token(flatMap)

多余安全性、性能等方面的考虑,多数服务器会有一些接口需要传入token才能正确返回结果,而token是需要从另一个接口获取的,这就需要使用两步连续的请求才能获取数据。使用flatMap()可以用较为清晰的代码实现这种连续请求,避免Callback嵌套的结构。


API是这样定义的:

<span style="font-size:18px;">public class FakeApi {    Random random = new Random();    public Observable<FakeToken> getFakeToken(@NonNull String fakeAuth) {        return Observable.just(fakeAuth)                .map(new Func1<String, FakeToken>() {                    @Override                    public FakeToken call(String fakeAuth) {                        // Add some random delay to mock the network delay                        int fakeNetworkTimeCost = random.nextInt(500) + 500;                        try {                            Thread.sleep(fakeNetworkTimeCost);                        } catch (InterruptedException e) {                            e.printStackTrace();                        }                        FakeToken fakeToken = new FakeToken();                        fakeToken.token = createToken();                        return fakeToken;                    }                });    }    private static String createToken() {        return "fake_token_" + System.currentTimeMillis() % 10000;    }    public Observable<FakeThing> getFakeData(FakeToken fakeToken) {        return Observable.just(fakeToken)                .map(new Func1<FakeToken, FakeThing>() {                    @Override                    public FakeThing call(FakeToken fakeToken) {                        // Add some random delay to mock the network delay                        int fakeNetworkTimeCost = random.nextInt(500) + 500;                        try {                            Thread.sleep(fakeNetworkTimeCost);                        } catch (InterruptedException e) {                            e.printStackTrace();                        }                        if (fakeToken.expired) {                            throw new IllegalArgumentException("Token expired!");                        }                        FakeThing fakeData = new FakeThing();                        fakeData.id = (int) (System.currentTimeMillis() % 1000);                        fakeData.name = "FAKE_USER_" + fakeData.id;                        return fakeData;                    }                });    }}</span>


网络请求是这样的:


<strong> </strong> <span style="font-size:18px;">unsubscribe();        final FakeApi fakeApi = Network.getFakeApi();        subscription = fakeApi.getFakeToken("fake_auth_code")                .flatMap(new Func1<FakeToken, Observable<FakeThing>>() {                    @Override                    public Observable<FakeThing> call(FakeToken fakeToken) {                        return fakeApi.getFakeData(fakeToken);                    }                })                .subscribeOn(Schedulers.io())                .observeOn(AndroidSchedulers.mainThread())                .subscribe(new Action1<FakeThing>() {                    @Override                    public void call(FakeThing fakeData) {                        swipeRefreshLayout.setRefreshing(false);                        tokenTv.setText(getString(R.string.got_data, fakeData.id, fakeData.name));                    }                }, new Action1<Throwable>() {                    @Override                    public void call(Throwable throwable) {                        swipeRefreshLayout.setRefreshing(false);                        Toast.makeText(getActivity(), R.string.loading_failed, Toast.LENGTH_SHORT).show();                    }                });</span>

TOKEN_高级(RETRYWHEN)

有的token并非一次性的,而是可以多次使用,直到它超时或被销毁。这样的token处理起来比较麻烦:需要把他保存起来,并且在发现它失效的时候要能够自动重新获取新的token并继续访问之前由于token失效而失败的请求。用传统的Callback请求会写出非常复杂的代码,而RxJava的RetryWhen可以轻松的处理这样的问题。


  <span style="font-size:18px;">final FakeApi fakeApi = Network.getFakeApi();        subscription = Observable.just(null)                .flatMap(new Func1<Object, Observable<FakeThing>>() {                    @Override                    public Observable<FakeThing> call(Object o) {                        return cachedFakeToken.token == null                                ? Observable.<FakeThing>error(new NullPointerException("Token is null!"))                                : fakeApi.getFakeData(cachedFakeToken);                    }                })                .retryWhen(new Func1<Observable<? extends Throwable>, Observable<?>>() {                    @Override                    public Observable<?> call(Observable<? extends Throwable> observable) {                        return observable.flatMap(new Func1<Throwable, Observable<?>>() {                            @Override                            public Observable<?> call(Throwable throwable) {                                if (throwable instanceof IllegalArgumentException || throwable instanceof NullPointerException) {                                    return fakeApi.getFakeToken("fake_auth_code")                                            .doOnNext(new Action1<FakeToken>() {                                                @Override                                                public void call(FakeToken fakeToken) {                                                    tokenUpdated = true;                                                    cachedFakeToken.token = fakeToken.token;                                                    cachedFakeToken.expired = fakeToken.expired;                                                }                                            });                                }                                return Observable.error(throwable);                            }                        });                    }                })                .subscribeOn(Schedulers.io())                .observeOn(AndroidSchedulers.mainThread())                .subscribe(new Action1<FakeThing>() {                    @Override                    public void call(FakeThing fakeData) {                        swipeRefreshLayout.setRefreshing(false);                        String token = cachedFakeToken.token;                        if (tokenUpdated) {                            token += "(" + getString(R.string.updated) + ")";                        }                        tokenTv.setText(getString(R.string.got_token_and_data, token, fakeData.id, fakeData.name));                    }                }, new Action1<Throwable>() {                    @Override                    public void call(Throwable throwable) {                        swipeRefreshLayout.setRefreshing(false);                        Toast.makeText(getActivity(), R.string.loading_failed, Toast.LENGTH_SHORT).show();                    }                });</span>


缓存(BehaviorSubject)

RxJava中有一个很少被人用到的类叫做Subject,它是一种{既是Observable又是Obsver}的东西,因此可以被用做中间件来做数据传递。可以用他的子类BehaviorSubject来进行缓存。

下面是网络请求

  <span style="font-size:18px;">Network.getGankApi()                .getBeauties(100, 1)                .subscribeOn(Schedulers.io())                .map(GankBeautyResultToItemsMapper.getInstance())                .doOnNext(new Action1<List<Item>>() {                    @Override                    public void call(List<Item> items) {                        Database.getInstance().writeItems(items);                    }                })                .subscribe(new Action1<List<Item>>() {                    @Override                    public void call(List<Item> items) {                        cache.onNext(items);//cache是BehaviorSubject的对象                    }                }, new Action1<Throwable>() {                    @Override                    public void call(Throwable throwable) {                        throwable.printStackTrace();                    }                });</span>


1 0
原创粉丝点击