RxJava配合Retrofit实现网络封装

cnbond 8年前
   <p>那么呢,首先呢,我们呢,来记录一下Android中比较火的两种技术,火了大半壁江山的RxJava和垄断了大部分的网络请求Retrofit。这两者的结合其实不需要太多的封装,只要简简单单的搞两下子基本就实现了常用的网络框架了。</p>    <p>废话不多说,代码说明一切:</p>    <p><strong>1、创建一个Android项目;</strong></p>    <p><strong>2、导入下面的依赖;</strong></p>    <pre>  <code class="language-java">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'</code></pre>    <p><strong>3、新建一个接口 NetService</strong></p>    <pre>  <code class="language-java">public interface NetService  {    }</code></pre>    <p><strong>4、新建一个类 NetUtils</strong></p>    <p>构造函数</p>    <pre>  <code class="language-java">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);   }</code></pre>    <p>我们可以看到这个构造函数是私有的,这里主要是想让这个工具类是一个单例模式:接下来我们实现单例模式:</p>    <pre>  <code class="language-java">public static NetService getInstance()   {    if (null == INSTANCE) {          INSTANCE = new NetUtils();      }            return netService;  }</code></pre>    <p>工具已经封装好了,</p>    <p>接下来:看看NetService中的请求方法怎么写:</p>    <p>一般的现在后台返回的数据都是下面这个种格式的:</p>    <p>{</p>    <p>"error_code":"200",</p>    <p>"reason":"请求成功",</p>    <p>"result":"{ }"</p>    <p>}</p>    <p><img src="https://simg.open-open.com/show/3e6d0981b3b79140b76d4dd8aa0a45f7.png"></p>    <p>JSON在线视图查看器.png</p>    <p>前面是状态和提示,通常我们只关心result里面的真实数据,所以这里写个通用数据类BaseData</p>    <pre>  <code class="language-java">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;      }  }</code></pre>    <p><strong>5、NetServer中的请求方法</strong></p>    <pre>  <code class="language-java">@GET("news/query") //get请求 括号内为请求地址后缀  //@Query("key") @Query("q")  key 和q 查询字段  Observable<BaseData<NewsData>> getNewsData(@Query("key") String key, @Query("q")String name);</code></pre>    <p>为什么要用BaseData 主要是每次返回的error_code 和reason和result字段名永远都是不变的,我们不需要再每个接受数据的实体中都写重复的字段,这里以聚合数据中的新闻接口为例,简单写几个接收字段;</p>    <pre>  <code class="language-java">public class NewsData {      private String title;      private String content;     private String url;  }</code></pre>    <p>当然set get方法你需要实现,我就不贴代码了;</p>    <p><strong>6、如何请求网络</strong></p>    <pre>  <code class="language-java">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();            }     });  }</code></pre>    <p>手指轻轻那么一点,网络请求立马实现</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/4f761dce2360bb5fb85e2c710af6e811.png"></p>    <p>那么是不是就结束了呢,不要着急,连进度框都没有算什么网络请求?所以呢,还需要进一步的优化!</p>    <p><strong>7、建一个BaseSubscriber</strong></p>    <pre>  <code class="language-java">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);  }</code></pre>    <p>onNext为抽象方法,主要是因为大部分时间我们并不需要关心onError()和onCompleted(),这样保证了调用的Fragent和Acitivity的简洁,如果真要处理错误只需要将onError重写。</p>    <p>请求变成这样了</p>    <pre>  <code class="language-java">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();              }          });</code></pre>    <p>getNetService()是从BaseActivity来的:</p>    <pre>  <code class="language-java">public class BaseActivity extends AppCompatActivity {     public NetService getNetService(){          return NetUtils.getInstance();      }  }</code></pre>    <p>但还是不完美,比如BaseSubscriber<BaseData<List<NewsData>>> 泛型太累赘,比如每次都要写</p>    <p>subscribeOn(Schedulers.io())</p>    <p>.observeOn(AndroidSchedulers.mainThread())</p>    <p>.subscribe()这一串进行线程切换,这种固定的我们能不能写成方法直接调用?</p>    <p>那么:</p>    <p><strong>8、新建HttpResultFunc</strong></p>    <pre>  <code class="language-java">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();    }</code></pre>    <p>该类的主要功能就是将不关心的数据过滤掉,如果error_code!=200,说明请求数据出错了,此时通常result这样的数据为null,只需要在activity或者Fragment中判断数据是否为null;</p>    <p>修改后 map变形</p>    <pre>  <code class="language-java">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();                          }                    }          });</code></pre>    <p><strong>9、在NetUtils中加入:</strong></p>    <pre>  <code class="language-java">public static  void toSubscribe(Observable o, Subscriber s) {               o.subscribeOn(Schedulers.io()              .unsubscribeOn(Schedulers.io())              .observeOn(AndroidSchedulers.mainThread())              .subscribe(s);}</code></pre>    <p>这一样我们就将固定的东西封装起来了:</p>    <p>请求变成这样了</p>    <pre>  <code class="language-java">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();           }         }    });</code></pre>    <p>但笔者没有找到有个很好的方法把.map(new HttpResultFunc<List<NewsData>>()),用泛型封装起来,主要是对泛型的知识还很欠缺。</p>    <p>来自:http://www.jianshu.com/p/d96be4123308</p>    <p> </p>