RxJava配合Retrofit实现网络封装
那么呢,首先呢,我们呢,来记录一下Android中比较火的两种技术,火了大半壁江山的RxJava和垄断了大部分的网络请求Retrofit。这两者的结合其实不需要太多的封装,只要简简单单的搞两下子基本就实现了常用的网络框架了。
废话不多说,代码说明一切:
1、创建一个Android项目;
2、导入下面的依赖;
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4'
compile 'io.reactivex:rxjava:1.1.0'
compile 'io.reactivex:rxandroid:1.1.0'
compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'
compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4'
compile 'com.google.code.gson:gson:2.6.2'
3、新建一个接口 NetService
public interface NetService { }
4、新建一个类 NetUtils
构造函数
private static final long DEFAULT_TIMEOUT = 8; //超时时间设置为8秒
private final String BASE_URL ="http://op.juhe.cn/onebox/"; //固定的网址 必须以‘/’结尾
private static NetUtils INSTANCE;
private final Retrofit retrofit;
public static NetService netService = null;
private NetUtils() {
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();//创建一个OkHttp,如果不指定默认就是OkHttp
httpClientBuilder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
retrofit = new Retrofit.Builder()
.client(httpClientBuilder.build())
.addConverterFactory(GsonConverterFactory.create()) //GSON数据解析器
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl(BASE_URL)
.build();
netService = retrofit.create(NetService.class);
}
我们可以看到这个构造函数是私有的,这里主要是想让这个工具类是一个单例模式:接下来我们实现单例模式:
public static NetService getInstance()
{ if (null == INSTANCE) {
INSTANCE = new NetUtils();
}
return netService;
}
工具已经封装好了,
接下来:看看NetService中的请求方法怎么写:
一般的现在后台返回的数据都是下面这个种格式的:
{
"error_code":"200",
"reason":"请求成功",
"result":"{ }"
}
前面是状态和提示,通常我们只关心result里面的真实数据,所以这里写个通用数据类BaseData
public class BaseData<T> {
public T result;
public int status;
public String reason;
public T getResult() {
return result;
}
public void setResult(T result) {
this.result = result;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
}
5、NetServer中的请求方法
@GET("news/query") //get请求 括号内为请求地址后缀
//@Query("key") @Query("q") key 和q 查询字段
Observable<BaseData<NewsData>> getNewsData(@Query("key") String key, @Query("q")String name);
为什么要用BaseData 主要是每次返回的error_code 和reason和result字段名永远都是不变的,我们不需要再每个接受数据的实体中都写重复的字段,这里以聚合数据中的新闻接口为例,简单写几个接收字段;
public class NewsData {
private String title;
private String content;
private String url;
}
当然set get方法你需要实现,我就不贴代码了;
6、如何请求网络
public void startRequest(View v){
String key=""; //key我就不给你了,少年自己请求账号吧
String q="双十一"; //双十一关键字
getNetService().getNewsData(key,q).
subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<BaseData<List<NewsData>>>() {
@Override
public void onCompleted()
{
}
@Override
public void onError(Throwable e) {
Toast.makeText(NetUtilsActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
@Override
public void onNext(BaseData<List<NewsData>> listBaseData) {
result = listBaseData.getResult();
newsAdapter.notifyDataSetChanged();
Toast.makeText(NetUtilsActivity.this, result.get(0).getTitle(), Toast.LENGTH_SHORT).show();
}
});
}
手指轻轻那么一点,网络请求立马实现
效果图那么是不是就结束了呢,不要着急,连进度框都没有算什么网络请求?所以呢,还需要进一步的优化!
7、建一个BaseSubscriber
public abstract class BaseSubscriber<T> extends Subscriber<T> {
private Context mContext;
private ProgressDialog progressDialog;
public BaseSubscriber(Context context) {
mContext=context;
progressDialog = new ProgressDialog(mContext);
progressDialog.setMessage("正在加载数据,请稍后...");
}
@Override
public void onStart() {
super.onStart();
Log.d("BaseSubscriber", "onStart");
progressDialog.show();
}
@Override public void onCompleted(){
progressDialog.dismiss();
}
@Override
public void onError(Throwable e){
progressDialog.dismiss();
final AlertDialog.Builder builder=new AlertDialog.Builder(mContext);
builder.setMessage(e.getMessage()).setPositiveButton("确定",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
builder.create().dismiss();
}
});
builder.show(); }
@Override public abstract void onNext(T t);
}
onNext为抽象方法,主要是因为大部分时间我们并不需要关心onError()和onCompleted(),这样保证了调用的Fragent和Acitivity的简洁,如果真要处理错误只需要将onError重写。
请求变成这样了
getNetService().getNewsData(key, q).
subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new BaseSubscriber<BaseData<List<NewsData>>>(NetUtilsActivity.this) {
@Override
public void onNext(BaseData<List<NewsData>> newsDatas) {
result=newsDatas.getResult();
newsAdapter.notifyDataSetChanged();
}
});
getNetService()是从BaseActivity来的:
public class BaseActivity extends AppCompatActivity {
public NetService getNetService(){
return NetUtils.getInstance();
}
}
但还是不完美,比如BaseSubscriber<BaseData<List<NewsData>>> 泛型太累赘,比如每次都要写
subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe()这一串进行线程切换,这种固定的我们能不能写成方法直接调用?
那么:
8、新建HttpResultFunc
public class HttpResultFunc<T> implements Func1<BaseData<T>, T> {
@Override
public T call(BaseData<T> baseData) {
if (baseData.geError_Code!=200) {
try {
throw new Exception(baseData.getReason());
} catch (Exception e) {
e.printStackTrace(); }
}
return baseData.getResult(); }
该类的主要功能就是将不关心的数据过滤掉,如果error_code!=200,说明请求数据出错了,此时通常result这样的数据为null,只需要在activity或者Fragment中判断数据是否为null;
修改后 map变形
getNetService().getNewsData(key, q)
.map(new HttpResultFunc<List<NewsData>>())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new BaseSubscriber<List<NewsData>>(NetUtilsActivity.this) {
@Override
public void onNext(List<NewsData> newsDatas) {
if(null!=newsDatas) {
result = newsDatas;
newsAdapter.notifyDataSetChanged();
}
}
});
9、在NetUtils中加入:
public static void toSubscribe(Observable o, Subscriber s) {
o.subscribeOn(Schedulers.io()
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(s);}
这一样我们就将固定的东西封装起来了:
请求变成这样了
NetUtils.toSubscribe(getNetService().getNewsData(key, q)
.map(new HttpResultFunc<List<NewsData>>()),
new BaseSubscriber<List<NewsData>>(NetUtilsActivity.this) {
@Override public void onNext(List<NewsData> o) {
if(null!=0){
result=o;
newsAdapter.notifyDataSetChanged();
}
}
});
但笔者没有找到有个很好的方法把.map(new HttpResultFunc<List<NewsData>>()),用泛型封装起来,主要是对泛型的知识还很欠缺,有更好的方法烦请告知!