rxjava+retrofit实例解析
首先看下retrofit定义的接口:
1.ZhuangbiApi .class
public interface ZhuangbiApi {
@GET("search")
Observable<List<ZhuangbiImage>> search(@Query("q") String query);
}
解释:get请求,baseurl后拼接search,后面跟上 ?q= 的形式(注:Query后自带?)
如:https://www.zhuangbi.info/search?q=110
2.GankApi .class
public interface GankApi {
@GET("data/福利/{number}/{page}")
Observable<GankBeautyResult> getBeauties(@Path("number") int number, @Path("page") int page);
}
解释:get请求,拼接data/福利/{number}/{page},其中,@GET中的{number}会由下面getBeauties参数的number替代,page同理,(注:Path的用法和Query区分)
如:https://www.zhuangbi.info/data/福利/110/1
3.FakeApi .class
// (c)2016 Flipboard Inc, All Rights Reserved.
package com.rengwuxian.rxjavasamples.network.api;
import android.support.annotation.NonNull;
import com.rengwuxian.rxjavasamples.model.FakeThing;
import com.rengwuxian.rxjavasamples.model.FakeToken;
import java.util.Random;
import io.reactivex.Observable;
import io.reactivex.functions.Function;
public class FakeApi {
Random random = new Random();
public Observable<FakeToken> getFakeToken(@NonNull String fakeAuth) {
return Observable.just(fakeAuth)
.map(new Function<String, FakeToken>() {
@Override
public FakeToken apply(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 Function<FakeToken, FakeThing>() {
@Override
public FakeThing apply(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;
}
});
}
}
FakeToken .class
public class FakeToken {
public String token;
public boolean expired;
public FakeToken() {
}
public FakeToken(boolean expired) {
this.expired = expired;
}
}
FakeThing .class
// (c)2016 Flipboard Inc, All Rights Reserved.
package com.rengwuxian.rxjavasamples.model;
public class FakeThing {
public int id;
public String name;
}
解释:getFakeToken方法利用map将string转换成FakeToken,最后返回的是携带了fakeToken.token的FakeToken对象;getFakeData也是类似
4.再看NetWork的封装:
// (c)2016 Flipboard Inc, All Rights Reserved.
package com.rengwuxian.rxjavasamples.network;
import com.rengwuxian.rxjavasamples.network.api.FakeApi;
import com.rengwuxian.rxjavasamples.network.api.GankApi;
import com.rengwuxian.rxjavasamples.network.api.ZhuangbiApi;
import okhttp3.OkHttpClient;
import retrofit2.CallAdapter;
import retrofit2.Converter;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
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 = RxJava2CallAdapterFactory.create();
public static ZhuangbiApi getZhuangbiApi() {
if (zhuangbiApi == null) {
Retrofit retrofit = new Retrofit.Builder()
.client(okHttpClient)
.baseUrl("http://www.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;
}
}
解释:最开始那三个接口的获取
5.ZhuangbiApi 的使用
private void search(String key) {
Log.d("liwenpeng","key:"+key);
disposable = Network.getZhuangbiApi()
.search(key)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<List<ZhuangbiImage>>() {
@Override
public void accept(@NonNull List<ZhuangbiImage> images) throws Exception {
swipeRefreshLayout.setRefreshing(false);
adapter.setImages(images);
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
swipeRefreshLayout.setRefreshing(false);
Toast.makeText(getActivity(), R.string.loading_failed, Toast.LENGTH_SHORT).show();
}
});
}
解释:IO线程进行search,结果到主线程处理
6.GankApi的使用:
private void loadPage(int page) {
swipeRefreshLayout.setRefreshing(true);
unsubscribe();
disposable = Network.getGankApi()
.getBeauties(10, page)
.map(GankBeautyResultToItemsMapper.getInstance())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<List<Item>>() {
@Override
public void accept(@NonNull List<Item> items) throws Exception {
swipeRefreshLayout.setRefreshing(false);
pageTv.setText(getString(R.string.page_with_number, MapFragment.this.page));
adapter.setItems(items);
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
swipeRefreshLayout.setRefreshing(false);
Toast.makeText(getActivity(), R.string.loading_failed, Toast.LENGTH_SHORT).show();
}
});
}
看下map转换的function类:
// (c)2016 Flipboard Inc, All Rights Reserved.
package com.rengwuxian.rxjavasamples.util;
import com.rengwuxian.rxjavasamples.model.GankBeauty;
import com.rengwuxian.rxjavasamples.model.GankBeautyResult;
import com.rengwuxian.rxjavasamples.model.Item;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import io.reactivex.functions.Function;
public class GankBeautyResultToItemsMapper implements Function<GankBeautyResult, List<Item>> {
private static GankBeautyResultToItemsMapper INSTANCE = new GankBeautyResultToItemsMapper();
private GankBeautyResultToItemsMapper() {
}
public static GankBeautyResultToItemsMapper getInstance() {
return INSTANCE;
}
@Override
public List<Item> apply(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;
}
}
解释:此处使用了map将GankBeautyResult转换成List集合,这样在.subscribe的accept里得到的就是List了,再进行处理
7.ZhuangbiApi使用zip操作符
// (c)2016 Flipboard Inc, All Rights Reserved.
package com.rengwuxian.rxjavasamples.module.zip_3;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.rengwuxian.rxjavasamples.BaseFragment;
import com.rengwuxian.rxjavasamples.network.Network;
import com.rengwuxian.rxjavasamples.R;
import com.rengwuxian.rxjavasamples.adapter.ItemListAdapter;
import com.rengwuxian.rxjavasamples.model.Item;
import com.rengwuxian.rxjavasamples.model.ZhuangbiImage;
import com.rengwuxian.rxjavasamples.util.GankBeautyResultToItemsMapper;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.functions.BiFunction;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
public class ZipFragment extends BaseFragment {
@BindView(R.id.gridRv) RecyclerView gridRv;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout swipeRefreshLayout;
ItemListAdapter adapter = new ItemListAdapter();
@OnClick(R.id.zipLoadBt)
void load() {
swipeRefreshLayout.setRefreshing(true);
unsubscribe();
disposable = Observable.zip(Network.getGankApi().getBeauties(200, 1).map(GankBeautyResultToItemsMapper.getInstance()),
Network.getZhuangbiApi().search("装逼"),
new BiFunction<List<Item>, List<ZhuangbiImage>, List<Item>>() {
@Override
public List<Item> apply(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(new Consumer<List<Item>>() {
@Override
public void accept(@NonNull List<Item> items) throws Exception {
swipeRefreshLayout.setRefreshing(false);
adapter.setItems(items);
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
swipeRefreshLayout.setRefreshing(false);
Toast.makeText(getActivity(), R.string.loading_failed, Toast.LENGTH_SHORT).show();
}
});
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_zip, container, false);
ButterKnife.bind(this, view);
gridRv.setLayoutManager(new GridLayoutManager(getActivity(), 2));
gridRv.setAdapter(adapter);
swipeRefreshLayout.setColorSchemeColors(Color.BLUE, Color.GREEN, Color.RED, Color.YELLOW);
swipeRefreshLayout.setEnabled(false);
return view;
}
@Override
protected int getDialogRes() {
return R.layout.dialog_zip;
}
@Override
protected int getTitleRes() {
return R.string.title_zip;
}
}
解释:”Zip通过一个函数将多个Observable发送的事件结合到一起,然后发送这些组合到一起的事件. 它按照严格的顺序应用这个函数。它只发射与发射数据项最少的那个Observable一样多的数据。”,因此我们只需看 new BiFunction里的apply是如何处理的即可。这里是把List gankItems, List zhuangbiImages这两个list的数据用List保存返回。
8.FakeToken的处理flatMap
// (c)2016 Flipboard Inc, All Rights Reserved.
package com.rengwuxian.rxjavasamples.module.token_4;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import com.rengwuxian.rxjavasamples.BaseFragment;
import com.rengwuxian.rxjavasamples.network.Network;
import com.rengwuxian.rxjavasamples.R;
import com.rengwuxian.rxjavasamples.network.api.FakeApi;
import com.rengwuxian.rxjavasamples.model.FakeThing;
import com.rengwuxian.rxjavasamples.model.FakeToken;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
public class TokenFragment extends BaseFragment {
@BindView(R.id.tokenTv) TextView tokenTv;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout swipeRefreshLayout;
@OnClick(R.id.requestBt)
void upload() {
swipeRefreshLayout.setRefreshing(true);
unsubscribe();
final FakeApi fakeApi = Network.getFakeApi();
disposable = fakeApi.getFakeToken("fake_auth_code")
.flatMap(new Function<FakeToken, Observable<FakeThing>>() {
@Override
public Observable<FakeThing> apply(FakeToken fakeToken) {
return fakeApi.getFakeData(fakeToken);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<FakeThing>() {
@Override
public void accept(FakeThing fakeData) {
swipeRefreshLayout.setRefreshing(false);
tokenTv.setText(getString(R.string.got_data, fakeData.id, fakeData.name));
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) {
swipeRefreshLayout.setRefreshing(false);
Toast.makeText(getActivity(), R.string.loading_failed, Toast.LENGTH_SHORT).show();
}
});
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_token, container, false);
ButterKnife.bind(this, view);
swipeRefreshLayout.setColorSchemeColors(Color.BLUE, Color.GREEN, Color.RED, Color.YELLOW);
swipeRefreshLayout.setEnabled(false);
return view;
}
@Override
protected int getDialogRes() {
return R.layout.dialog_token;
}
@Override
protected int getTitleRes() {
return R.string.title_token;
}
}
解释:”FlatMap将一个发送事件的上游Observable变换为多个发送事件的Observables,然后将它们发射的事件合并后放进一个单独的Observable里.”
8.retrywhen
// (c)2016 Flipboard Inc, All Rights Reserved.
package com.rengwuxian.rxjavasamples.module.token_advanced_5;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import com.rengwuxian.rxjavasamples.BaseFragment;
import com.rengwuxian.rxjavasamples.network.Network;
import com.rengwuxian.rxjavasamples.R;
import com.rengwuxian.rxjavasamples.network.api.FakeApi;
import com.rengwuxian.rxjavasamples.model.FakeThing;
import com.rengwuxian.rxjavasamples.model.FakeToken;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
public class TokenAdvancedFragment extends BaseFragment {
@BindView(R.id.tokenTv) TextView tokenTv;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout swipeRefreshLayout;
final FakeToken cachedFakeToken = new FakeToken(true);
boolean tokenUpdated;
@OnClick(R.id.invalidateTokenBt)
void invalidateToken() {
cachedFakeToken.expired = true;
Toast.makeText(getActivity(), R.string.token_destroyed, Toast.LENGTH_SHORT).show();
}
@OnClick(R.id.requestBt)
void upload() {
tokenUpdated = false;
swipeRefreshLayout.setRefreshing(true);
unsubscribe();
final FakeApi fakeApi = Network.getFakeApi();
disposable = Observable.just(1)
.flatMap(new Function<Object, Observable<FakeThing>>() {
@Override
public Observable<FakeThing> apply(Object o) {
return cachedFakeToken.token == null
? Observable.<FakeThing>error(new NullPointerException("Token is null!"))
: fakeApi.getFakeData(cachedFakeToken);
}
})
.retryWhen(new Function<Observable<? extends Throwable>, Observable<?>>() {
@Override
public Observable<?> apply(Observable<? extends Throwable> observable) {
return observable.flatMap(new Function<Throwable, Observable<?>>() {
@Override
public Observable<?> apply(Throwable throwable) {
if (throwable instanceof IllegalArgumentException || throwable instanceof NullPointerException) {
return fakeApi.getFakeToken("fake_auth_code")
.doOnNext(new Consumer<FakeToken>() {
@Override
public void accept(FakeToken fakeToken) {
tokenUpdated = true;
cachedFakeToken.token = fakeToken.token;
cachedFakeToken.expired = fakeToken.expired;
}
});
}
return Observable.error(throwable);
}
});
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<FakeThing>() {
@Override
public void accept(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 Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) {
swipeRefreshLayout.setRefreshing(false);
Toast.makeText(getActivity(), R.string.loading_failed, Toast.LENGTH_SHORT).show();
}
});
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_token_advanced, container, false);
ButterKnife.bind(this, view);
swipeRefreshLayout.setColorSchemeColors(Color.BLUE, Color.GREEN, Color.RED, Color.YELLOW);
swipeRefreshLayout.setEnabled(false);
return view;
}
@Override
protected int getDialogRes() {
return R.layout.dialog_token_advanced;
}
@Override
protected int getTitleRes() {
return R.string.title_token_advanced;
}
}
解释:注意retryWhen的用法