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, Subscribersubscriber) { 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);// Callcall = movieService.getTopMovie(0, 10);// call.enqueue(new Callback () {// @Override// public void onResponse(Call call, Response response) {// tvShow.setText(response.body().getTitle());// }//// @Override// public void onFailure(Call 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 () {// @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 () { @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{ 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{// 常用形式// 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*/ private class HttpResultFunc implements Func1 , T> { @Override public T call(HttpResult tHttpResult) { if (tHttpResult.getCount() == 0) { throw new ApiException(100); } return tHttpResult.getSubjects(); } }
所以getMovie() 方法修改成了
/** * 用于获取豆瓣电影Top250的数据 * @param subscriber 由调用者传过来的观察者对象 * @param start 起始位置 * @param count 获取长度 */ public void getTopMovie(Subscriber
> subscriber, int start, int count){ movieService.getTopMovie(start, count) .map(new HttpResultFunc
>()) .subscribeOn(Schedulers.io()) .unsubscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(subscriber);}
ok,在修改一下activity中的调用方法
HttpMethods.getInstance().getTopMovie(0, 10, new Subscriber
>() { @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 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{ 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 ProgressSubscriberextends Subscriber { 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);// Callcall = movieService.getTopMovie(0, 10);// call.enqueue(new Callback () {// @Override// public void onResponse(Call call, Response response) {// tvShow.setText(response.body().getTitle());// }//// @Override// public void onFailure(Call 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 () {// @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
>() {// @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 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
>( new SubscriberOnNextListenter
>() { @Override public void next(List 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 ProgressSubscriberextends Subscriber 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; } }}
最后看一下效果