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>