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":"{ }"
}

JSON在线视图查看器.png

前面是状态和提示,通常我们只关心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>>()),用泛型封装起来,主要是对泛型的知识还很欠缺,有更好的方法烦请告知

源码下载请点击这里

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容

  • error code(错误代码)=0是操作成功完成。error code(错误代码)=1是功能错误。error c...
    Heikki_阅读 3,372评论 1 9
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,608评论 18 399
  • 点击查看原文 Web SDK 开发手册 SDK 概述 网易云信 SDK 为 Web 应用提供一个完善的 IM 系统...
    layjoy阅读 13,734评论 0 15
  • 前言 RxJava和Retrofit也火了一段时间了,不过最近一直在学习ReactNative和Node相关的姿势...
    流水潺湲阅读 1,223评论 3 6
  • 依我看:互联网社交已经进入实质的第三阶段。第一阶段:陌生人为主,无差别好友关系网络!第二阶段:社交细分领域拓展,主...
    犟嘴小二儿阅读 834评论 3 18