Android--Retrofit+RxJava的简单封装(三)
1,继续接着上一篇的讲讲,话说如果像上一篇这样的话,那么我们每一次请求一个结构都要创建一堆的Retrofit对象,而且代码都是相同的,我们可以试试封装一下
先创建一个HttpMethods类,将Retrofit对象创建封装起来
HttpMethods.java
package com.qianmo.retrofitdemo.http; import com.qianmo.retrofitdemo.entry.MovieEntity; import java.util.concurrent.TimeUnit; import okhttp3.OkHttpClient; import retrofit2.Retrofit; import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; import retrofit2.converter.gson.GsonConverterFactory; import rx.Subscriber; import rx.android.schedulers.AndroidSchedulers; import rx.schedulers.Schedulers; /** * Created by wangjitao on 2016/11/3 0003. * 对Retrofit的的简单封装 */ public class HttpMethods { public static final String BASE_URL = "https://api.douban.com/v2/movie/"; private static final int DEFAULT_TIMEOUT = 5; private Retrofit retrofit; private MovieService movieService; //先构造私有的构造方法 private HttpMethods() { //手动创建一个OkHttpClient并设置超时时间 OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(); httpClientBuilder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS); retrofit = new Retrofit.Builder() .client(httpClientBuilder.build()) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .baseUrl(BASE_URL) .build(); movieService = retrofit.create(MovieService.class); } //创建单例 public static class SingleonHolder { private static final HttpMethods instance = new HttpMethods(); } //获取单例 public static HttpMethods getInstance() { return SingleonHolder.instance; } /** * * @param start 起始位置 * @param count 获取长度 * @param subscriber 传递过来的观察者对象 */ public void getTopMovie(int start, int count, Subscriber<MovieEntity> subscriber) { movieService.getTopMovie(start, count) .subscribeOn(Schedulers.io()) .unsubscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(subscriber); } }
在直接在Activity中进行调用
package com.qianmo.retrofitdemo; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import com.qianmo.retrofitdemo.entry.MovieEntity; import com.qianmo.retrofitdemo.http.HttpMethods; import com.qianmo.retrofitdemo.http.MovieService; import butterknife.Bind; import butterknife.ButterKnife; import butterknife.OnClick; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; import retrofit2.converter.gson.GsonConverterFactory; import rx.Subscriber; import rx.android.schedulers.AndroidSchedulers; import rx.schedulers.Schedulers; public class MainActivity extends AppCompatActivity { @Bind(R.id.tv_show) TextView tvShow; @Bind(R.id.btn_request) Button btnRequest; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); } @OnClick({R.id.btn_request}) public void onClick(View view) { switch (view.getId()) { case R.id.btn_request: getMovie(); break; } } //请求网络数据 private void getMovie() { // //https://api.douban.com/v2/movie/top250?start=0&count=10 目标地址 // String baseUrl = "https://api.douban.com/v2/movie/"; // // //创建Retrofit对象 // Retrofit retrofit = new Retrofit.Builder() // .baseUrl(baseUrl) // .addConverterFactory(GsonConverterFactory.create()) // .build(); // // MovieService movieService = retrofit.create(MovieService.class); // Call<MovieEntity> call = movieService.getTopMovie(0, 10); // call.enqueue(new Callback<MovieEntity>() { // @Override // public void onResponse(Call<MovieEntity> call, Response<MovieEntity> response) { // tvShow.setText(response.body().getTitle()); // } // // @Override // public void onFailure(Call<MovieEntity> call, Throwable t) { // tvShow.setText(t.getMessage()); // } // }); //第二种 // String baseUrl = "https://api.douban.com/v2/movie/"; // // Retrofit retrofit = new Retrofit.Builder() // .baseUrl(baseUrl) // .addConverterFactory(GsonConverterFactory.create()) // .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // .build(); // MovieService movieService = retrofit.create(MovieService.class); // // movieService.getTopMovie(0, 10) // .subscribeOn(Schedulers.io()) // .observeOn(AndroidSchedulers.mainThread()) // .subscribe(new Subscriber<MovieEntity>() { // @Override // public void onCompleted() { // Toast.makeText(MainActivity.this, "Get Top Movie Completed", Toast.LENGTH_SHORT).show(); // } // // @Override // public void onError(Throwable e) { // tvShow.setText(e.getMessage()); // } // // @Override // public void onNext(MovieEntity movieEntity) { // tvShow.setText(movieEntity.getTitle()); // } // }); HttpMethods.getInstance().getTopMovie(0, 10, new Subscriber<MovieEntity>() { @Override public void onCompleted() { Toast.makeText(MainActivity.this, "Get Top Movie Completed", Toast.LENGTH_SHORT).show(); } @Override public void onError(Throwable e) { tvShow.setText(e.getMessage()); } @Override public void onNext(MovieEntity movieEntity) { tvShow.setText(movieEntity.getTitle()); } }); } }
但是现在存在一个问题,当存在相同格式的数据时候 我们应该怎么封装,一般来说我们后台返给我们的数据是这样的:
{ "resultCode": 0, "resultMessage": "成功", "data": {} }
主要的数据是data,然后通过code来判断是否请求成功 由于data里面的数据是多变的 可以是一个对象也可以是一个数组 这时候我们需要创建一个HttpResult
package com.qianmo.retrofitdemo.http; /** * Created by Administrator on 2016/11/3 0003. */ public class HttpResult<T> { private int resultCode ; private String resultMessage ; private T data ; }
用过我们这次借口的同学都知道我们这次接口其实不是这样的,先来看看我们这次的数据结构吧
{ "count": 10, "start": 0, "total": 250, "subjects": [], "title": "豆瓣电影Top250" }
我们打算通过count的数量来判断请求是否成功,所以吧HttpResult类修改成下面
package com.qianmo.retrofitdemo.http; /** * Created by Administrator on 2016/11/3 0003. */ public class HttpResult<T> { // 常用形式 // private int resultCode ; // private String resultMessage ; // // private T data ; //本次接口的数据结构 private int count; private int start; private int total; private String title; //用来模仿Data private T subjects; public int getCount() { return count; } public void setCount(int count) { this.count = count; } public int getStart() { return start; } public void setStart(int start) { this.start = start; } public int getTotal() { return total; } public void setTotal(int total) { this.total = total; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public T getSubjects() { return subjects; } public void setSubjects(T subjects) { this.subjects = subjects; } @Override public String toString() { StringBuffer sb = new StringBuffer(); sb.append("title=" + title + " count=" + count + " start=" + start); if (null != subjects) { sb.append(" subjects:" + subjects.toString()); } return sb.toString(); } }
在正常服务器返回的数据下code=200或者code=0的时候是表示成功的,但是由于我们这次的接口没有这一块的内容,所以打算使用count字段来判断,当coun = 0的时候表示没有数据,请求失败,所以这里我们需要在HttpMethod类中添加一个判断的方法
/** * 用来处理请求的code * @param <T> */ private class HttpResultFunc<T> implements Func1<HttpResult<T>, T> { @Override public T call(HttpResult<T> tHttpResult) { if (tHttpResult.getCount() == 0) { throw new ApiException(100); } return tHttpResult.getSubjects(); } }
所以getMovie() 方法修改成了
/** * 用于获取豆瓣电影Top250的数据 * @param subscriber 由调用者传过来的观察者对象 * @param start 起始位置 * @param count 获取长度 */ public void getTopMovie(Subscriber<List<Subject>> subscriber, int start, int count){ movieService.getTopMovie(start, count) .map(new HttpResultFunc<List<Subject>>()) .subscribeOn(Schedulers.io()) .unsubscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(subscriber); }
ok,在修改一下activity中的调用方法
HttpMethods.getInstance().getTopMovie(0, 10, new Subscriber<List<Subject>>() { @Override public void onCompleted() { Toast.makeText(MainActivity.this, "Get Top Movie Completed", Toast.LENGTH_SHORT).show(); } @Override public void onError(Throwable e) { tvShow.setText(e.getMessage()); } @Override public void onNext(List<Subject> subjects) { StringBuffer sb = new StringBuffer(); for (Subject subject : subjects) { sb.append(subject.toString()); } tvShow.setText(sb); } });
让我们看一下效果:(截取的动态图又太大了!!上传不了)
但是这也不是我们想要的最终结果,在请求数据的时候显示加载框,在请求完成的时候或者请求出错的时候影藏掉加载框,这样的话才算一个正常的网络请求框架。由于使用了RxJava,所以要创建一个类,继承与Subscriber,由于Subscriver对象中有四个方法,onStart、onNext、onError、onCompleted方法,我们需要在onStart中显示进度框,在onCompleted和onError中隐藏掉加载框,由于activity只关心最后的数据,所以我们需要写一个接口回调,将onNext方法中的数据传递给activity,让activity去处理逻辑。
SubscriberOnNextListenter.java
package com.qianmo.retrofitdemo.http; /** * Created by Administrator on 2016/11/3 0003. */ public interface SubscriberOnNextListenter<T> { void next(T t); }
ProgressSubscriber.java
package com.qianmo.retrofitdemo.http; import android.content.Context; import android.widget.Toast; import rx.Subscriber; /** * Created by wangjitao on 2016/11/3 0003. */ public class ProgressSubscriber<T> extends Subscriber<T> { private SubscriberOnNextListenter mSubscriberOnNextListenter; private Context context; public ProgressSubscriber(SubscriberOnNextListenter mSubscriberOnNextListenter, Context context) { this.mSubscriberOnNextListenter = mSubscriberOnNextListenter; this.context = context; } @Override public void onStart() { } @Override public void onCompleted() { Toast.makeText(context, "Get Top Movie Completed", Toast.LENGTH_SHORT).show(); } @Override public void onError(Throwable e) { Toast.makeText(context, "error:" + e.getMessage(), Toast.LENGTH_SHORT).show(); } @Override public void onNext(T t) { mSubscriberOnNextListenter.next(t); } }
再来修改一下我们activity调用的代码
package com.qianmo.retrofitdemo; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import com.qianmo.retrofitdemo.entry.Subject; import com.qianmo.retrofitdemo.http.HttpMethods; import com.qianmo.retrofitdemo.http.ProgressSubscriber; import com.qianmo.retrofitdemo.http.SubscriberOnNextListenter; import java.util.List; import butterknife.Bind; import butterknife.ButterKnife; import butterknife.OnClick; import rx.Subscriber; public class MainActivity extends AppCompatActivity { @Bind(R.id.tv_show) TextView tvShow; @Bind(R.id.btn_request) Button btnRequest; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); } @OnClick({R.id.btn_request}) public void onClick(View view) { switch (view.getId()) { case R.id.btn_request: getMovie(); break; } } //请求网络数据 private void getMovie() { // //https://api.douban.com/v2/movie/top250?start=0&count=10 目标地址 // String baseUrl = "https://api.douban.com/v2/movie/"; // // //创建Retrofit对象 // Retrofit retrofit = new Retrofit.Builder() // .baseUrl(baseUrl) // .addConverterFactory(GsonConverterFactory.create()) // .build(); // // MovieService movieService = retrofit.create(MovieService.class); // Call<MovieEntity> call = movieService.getTopMovie(0, 10); // call.enqueue(new Callback<MovieEntity>() { // @Override // public void onResponse(Call<MovieEntity> call, Response<MovieEntity> response) { // tvShow.setText(response.body().getTitle()); // } // // @Override // public void onFailure(Call<MovieEntity> call, Throwable t) { // tvShow.setText(t.getMessage()); // } // }); //第二种 // String baseUrl = "https://api.douban.com/v2/movie/"; // // Retrofit retrofit = new Retrofit.Builder() // .baseUrl(baseUrl) // .addConverterFactory(GsonConverterFactory.create()) // .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // .build(); // MovieService movieService = retrofit.create(MovieService.class); // // movieService.getTopMovie(0, 10) // .subscribeOn(Schedulers.io()) // .observeOn(AndroidSchedulers.mainThread()) // .subscribe(new Subscriber<MovieEntity>() { // @Override // public void onCompleted() { // Toast.makeText(MainActivity.this, "Get Top Movie Completed", Toast.LENGTH_SHORT).show(); // } // // @Override // public void onError(Throwable e) { // tvShow.setText(e.getMessage()); // } // // @Override // public void onNext(MovieEntity movieEntity) { // tvShow.setText(movieEntity.getTitle()); // } // }); //第三种 // HttpMethods.getInstance().getTopMovie(0, 10, new Subscriber<List<Subject>>() { // @Override // public void onCompleted() { // Toast.makeText(MainActivity.this, "Get Top Movie Completed", Toast.LENGTH_SHORT).show(); // } // // @Override // public void onError(Throwable e) { // tvShow.setText(e.getMessage()); // } // // @Override // public void onNext(List<Subject> subjects) { // StringBuffer sb = new StringBuffer(); // for (Subject subject : subjects) { // sb.append(subject.toString()); // } // tvShow.setText(sb); // } // }); SubscriberOnNextListenter subscriberOnNextListenter = new SubscriberOnNextListenter() { @Override public void next(Object o) { } }; //第四种 HttpMethods.getInstance() .getTopMovie(0, 10, new ProgressSubscriber<List<Subject>>( new SubscriberOnNextListenter<List<Subject>>() { @Override public void next(List<Subject> subjects) { StringBuffer sb = new StringBuffer(); for (Subject subject : subjects) { sb.append(subject.toString()); } tvShow.setText(sb); } }, MainActivity.this)); } }
再加上取消加载的接口和封装的progress就基本上完成了
package com.qianmo.retrofitdemo.http; public interface ProgressCancelListener { void onCancelProgress(); }
package com.qianmo.retrofitdemo.http; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.os.Handler; import android.os.Message; /** * Created by liukun on 16/3/10. */ public class ProgressDialogHandler extends Handler { public static final int SHOW_PROGRESS_DIALOG = 1; public static final int DISMISS_PROGRESS_DIALOG = 2; private ProgressDialog pd; private Context context; private boolean cancelable; private ProgressCancelListener mProgressCancelListener; public ProgressDialogHandler(Context context, ProgressCancelListener mProgressCancelListener, boolean cancelable) { super(); this.context = context; this.mProgressCancelListener = mProgressCancelListener; this.cancelable = cancelable; } private void initProgressDialog(){ if (pd == null) { pd = new ProgressDialog(context); pd.setCancelable(cancelable); if (cancelable) { pd.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialogInterface) { mProgressCancelListener.onCancelProgress(); } }); } if (!pd.isShowing()) { pd.show(); } } } private void dismissProgressDialog(){ if (pd != null) { pd.dismiss(); pd = null; } } @Override public void handleMessage(Message msg) { switch (msg.what) { case SHOW_PROGRESS_DIALOG: initProgressDialog(); break; case DISMISS_PROGRESS_DIALOG: dismissProgressDialog(); break; } } }
progressSubscriber.java
package com.qianmo.retrofitdemo.http; import android.content.Context; import android.widget.Toast; import java.net.ConnectException; import java.net.SocketTimeoutException; import rx.Subscriber; /** * Created by wangjitao on 2016/11/3 0003. */ public class ProgressSubscriber<T> extends Subscriber<T> implements ProgressCancelListener { private SubscriberOnNextListenter mSubscriberOnNextListenter; private ProgressDialogHandler mProgressDialogHandler; private Context context; public ProgressSubscriber(SubscriberOnNextListenter mSubscriberOnNextListenter, Context context) { this.mSubscriberOnNextListenter = mSubscriberOnNextListenter; this.context = context; mProgressDialogHandler = new ProgressDialogHandler(context, this, true); } /** * 在开始订阅的时候显示加载框 */ @Override public void onStart() { if (mProgressDialogHandler != null) { mProgressDialogHandler.obtainMessage(ProgressDialogHandler.SHOW_PROGRESS_DIALOG).sendToTarget(); } } /** * 在完成的时候进行影藏 */ @Override public void onCompleted() { Toast.makeText(context, "Get Top Movie Completed", Toast.LENGTH_SHORT).show(); dismissProgressDialog(); } /** * 在出错的时候也进行影藏 * * @param e */ @Override public void onError(Throwable e) { if (e instanceof SocketTimeoutException) { Toast.makeText(context, "网络中断,请检查您的网络状态", Toast.LENGTH_SHORT).show(); } else if (e instanceof ConnectException) { Toast.makeText(context, "网络中断,请检查您的网络状态", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(context, "error:" + e.getMessage(), Toast.LENGTH_SHORT).show(); } dismissProgressDialog(); } @Override public void onNext(T t) { mSubscriberOnNextListenter.next(t); } @Override public void onCancelProgress() { if (!this.isUnsubscribed()) { this.unsubscribe(); } } private void showProgressDialog() { if (mProgressDialogHandler != null) { mProgressDialogHandler.obtainMessage(ProgressDialogHandler.SHOW_PROGRESS_DIALOG).sendToTarget(); } } private void dismissProgressDialog() { if (mProgressDialogHandler != null) { mProgressDialogHandler.obtainMessage(ProgressDialogHandler.DISMISS_PROGRESS_DIALOG).sendToTarget(); mProgressDialogHandler = null; } } }
最后看一下效果