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;
}
}
}
最后看一下效果
