公司上个项目用的是rxjava1.0,最近看了各种rxjava2.0的介绍,自己也摸索着来,所以打算封装一下,用到项目中,与时俱进嘛!先不多逼逼了,直截了当,上货......
先看一下关于怎么配置Retrofit,这个大家都差不多,就不多解释了。。。base_url直接换成自己公司服务器的地址就可以了
github地址:https://github.com/Veken/RxJava2Retrofit
1 封装
Retrofit配置类
/**
* Retrofit配置
*/
public class RetrofitConnect {
private Retrofit retrofit;
private Service service;
/**
* 网络请求超时时间毫秒
*/
int DEFAULT_TIMEOUT = 20000;
/**
* 你们公司自己的服务器地址
*/
public static String BASE_URL = "";
private RetrofitConnect() {
/* HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);*/
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
try {
String text = URLDecoder.decode(message, "utf-8");
LogUtils.e("OKHttp-----", text);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
LogUtils.e("OKHttp-----", message);
}
}
});
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
File cacheFile = new File(Utils.getContext().getCacheDir(), "cache");
Cache cache = new Cache(cacheFile, 1024 * 1024 * 100); //100Mb
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)
.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)
.addInterceptor(interceptor)
.addNetworkInterceptor(new HttpCacheInterceptor())
.cache(cache)
.build();
Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").serializeNulls().create();
retrofit = new Retrofit.Builder()
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(BASE_URL)
.build();
service = retrofit.create(Service.class);
}
// 创建单例
private static class SingletonHolder {
private static final RetrofitConnect INSTANCE = new RetrofitConnect();
}
public static Service getApiService() {
return SingletonHolder.INSTANCE.service;
}
class HttpCacheInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (!NetworkUtils.isConnected()) { //没网强制从缓存读取
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
LogUtils.d("Okhttp", "no network");
}
Response originalResponse = chain.proceed(request);
if (NetworkUtils.isConnected()) {
//有网的时候读接口上的@Headers里的配置,你可以在这里进行统一的设置
String cacheControl = request.cacheControl().toString();
return originalResponse.newBuilder()
.header("Cache-Control", cacheControl)
.removeHeader("Pragma")
.build();
} else {
return originalResponse.newBuilder()
.header("Cache-Control", "public, only-if-cached, max-stale=2419200")
.removeHeader("Pragma")
.build();
}
}
}
}
接下来第一步封装,把请求的一些网络线程等等封装在一个类SubScriberHandler里面
并且把post请求的参数进行sha加密,参数可以根据公司自己的要求,自己更改
public class SubScriberHandler {
/**
* @param o
* @param <T>
*/
public <T> void toSubscribe(Observable<T> o, DefaultObserver observer) {
o.subscribeOn(Schedulers.io()) //网络耗时操作在io线程处理
.unsubscribeOn(Schedulers.io()) //网络耗时操作在io线程处理
.observeOn(AndroidSchedulers.mainThread()) //更新数据在主线程
.subscribe(observer);
}
/**
* 添加共同参数 SHA1加密
* @param fields
*/
public void handleFields(Map<String, Object> fields) {
fields.put("appKey", "00001");
fields.put("v", "1.0");
String sessionId = SpConfig.getInstance().getString(Constants.SESSIONID_STRING);
if(!TextUtils.isEmpty(sessionId))
fields.put(Constants.SESSIONID_STRING, sessionId);
String sha1 = null;
try {
sha1 = SHA1.SHA1(fields);
} catch (DigestException e) {
e.printStackTrace();
}
fields.put("sign", sha1);
}
}
接下来是关于自定义的实现Observer的类DefalutObserver,同时也添加了一个加载数据时候的progressdialog,可以根据公司的需求更改成自己的progressdialog,我这只是简单的调用普通的。各位客官也可以自己看心情来封装
**
* @author Veken
*/
public abstract class DefaultObserver<T extends BaseRespond> implements Observer<T>,ProgressCancelListener {
private Context context;
private boolean isAddInStop = false;
private ProgressDialogHandler mProgressDialogHandler;
//取消订阅
private Disposable mDisposable;
public DefaultObserver(Context context, boolean isShowLoading) {
this.context = context;
mProgressDialogHandler = new ProgressDialogHandler(context,this,true);
if (isShowLoading) {
showProgressDialog();
}
}
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;
}
}
@Override
public void onSubscribe(Disposable d) {
mDisposable =d;
}
@Override
public void onNext(T response) {
dismissProgressDialog();
if (response.getResCode().equals("200")) {
onSuccess(response);
} else {
onFail(response);
}
}
@Override
public void onError(Throwable e) {
LogUtils.e("Retrofit", e.getMessage());
// dismissProgress();
dismissProgressDialog();
if (e instanceof HttpException) { // HTTP错误
onException(ExceptionReason.BAD_NETWORK);
} else if (e instanceof ConnectException
|| e instanceof UnknownHostException) { // 连接错误
onException(ExceptionReason.CONNECT_ERROR);
} else if (e instanceof InterruptedIOException) { // 连接超时
onException(ExceptionReason.CONNECT_TIMEOUT);
} else if (e instanceof JsonParseException
|| e instanceof JSONException
|| e instanceof ParseException) { // 解析错误
onException(ExceptionReason.PARSE_ERROR);
} else {
onException(ExceptionReason.UNKNOWN_ERROR);
}
}
@Override
public void onComplete() {
dismissProgressDialog();
}
/**
* 请求成功
*
* @param response 服务器返回的数据
*/
abstract public void onSuccess(T response);
/**
* 服务器返回数据,但响应码不为200
*
* @param response 服务器返回的数据
*/
public void onFail(T response) {
String message = response.getResDesc();
if (TextUtils.isEmpty(message)) {
ToastUtils.show(R.string.response_return_error);
} else {
ToastUtils.show(message);
}
}
/**
* 请求异常
*
* @param reason
*/
public void onException(ExceptionReason reason) {
switch (reason) {
case CONNECT_ERROR:
ToastUtils.show(R.string.connect_error, Toast.LENGTH_SHORT);
break;
case CONNECT_TIMEOUT:
ToastUtils.show(R.string.connect_timeout, Toast.LENGTH_SHORT);
break;
case BAD_NETWORK:
ToastUtils.show(R.string.bad_network, Toast.LENGTH_SHORT);
break;
case PARSE_ERROR:
ToastUtils.show(R.string.parse_error, Toast.LENGTH_SHORT);
break;
case UNKNOWN_ERROR:
default:
ToastUtils.show(R.string.unknown_error, Toast.LENGTH_SHORT);
break;
}
}
/**
* 请求网络失败原因
*/
public enum ExceptionReason {
/**
* 解析数据失败
*/
PARSE_ERROR,
/**
* 网络问题
*/
BAD_NETWORK,
/**
* 连接错误
*/
CONNECT_ERROR,
/**
* 连接超时
*/
CONNECT_TIMEOUT,
/**
* 未知错误
*/
UNKNOWN_ERROR,
}
/**
* 取消ProgressDialog的时候,取消对observable的订阅,同时也取消了http请求
*/
@Override
public void onCancelProgress() {
if (!mDisposable.isDisposed()) {
mDisposable.dispose();
}
}
}
现在就是大家耳熟能详的Service类,这就没什么可说的,你要什么请求,就是什么,比如你要登录就写一个login的service,然后Map中装你需要请求的参数,以此类推。
/**
* @author Veken
*/
public interface Service {
/**
* 登录的service
* @param fields
* @return
*/
@FormUrlEncoded
@POST(Constants.URLEND)
Observable<LoginRespond> login(@FieldMap Map<String, Object> fields);
}
现在到了正儿八经的封装了,前面的都是开胃菜,配置都差不多,大同小异,到上真货的时候了
public class UserInfoRequest extends SubScriberHandler {
private UserInfoBean userInfoBean;
private Context context;
public UserInfoRequest(Context context,UserInfoBean userInfoBean) {
this.context = context;
this.userInfoBean = userInfoBean;
}
public void login() {
Map<String, Object> fields = new HashMap<>();
//调用的接口方法,比如login方法
fields.put("method", "user.login");
//加密和传一些常用参数
handleFields(fields);
//需要传递的参数
fields.put("phoneNo", userInfoBean.getPhoneNum());
fields.put("password", userInfoBean.getPwd());
//申请网络
Observable observable = RetrofitConnect.getApiService().login(fields);
toSubscribe(observable, new DefaultObserver(context,true) {
//数据返回在onNext
@Override
public void onSuccess(BaseRespond response) {
}
//数据返回
@Override
public void onNext(@NonNull BaseRespond response) {
//将结果封装在javabean的onSuccess方法里面
userInfoBean.onSuccess(response);
}
@Override
public void onError(Throwable e) {
super.onError(e);
userInfoBean.onError(e);
}
});
}
}
2 调用
下面就是具体调用了,看一下在MainActivity中怎么简单的调用吧
实现UserInfoBean,这样可以在重写的方法中传入我们需要的参数,当然也可以不写一个bean,直接用String传也可以,但是习惯了bean,数据处理也在bean中,简单明了。
初始化你的UserInfoPresent网络请求
private UserInfoRequest userInfoRequest;
userInfoRequest= new UserInfoRequest(this,this);
如果没有实现bean的话,UserInfoPresent()方法中的参数会报错,如果不想调用bean,可以自己简单的修改一下
剩下的就简单了,在自己想要请求网络的地方,调用 userInfoPresent.login();一句代码就可以了
public class MainActivity extends BaseActivity implements UserInfoBean {
private Button btn;
private UserInfoRequest userInfoRequest;
private TextView tv;
@Override
protected int getLayoutId() {
return R.layout.activity_main;
}
@Override
protected void initData(Bundle savedInstanceState) {
}
@Override
protected void initView() {
btn = (Button) findViewById(R.id.btn);
tv = (TextView) findViewById(R.id.tv);
userInfoRequest= new UserInfoRequest(this,this);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
userInfoRequest.login();
}
});
}
//传递的用户
@Override
public String getPhoneNum() {
//根据需要,传递相应的数据
return "登录账号";
}
//传递的密码
@Override
public String getPwd() {
//根据需要,传递相应的数据
return "登录密码";
}
//数据返回
@Override
public void onSuccess(Object object) {
LoginRespond loginRespond = (LoginRespond) object;
Log.d("登录信息:", loginRespond.getResDesc());
tv.setText(loginRespond.getData().getTelphone());
}
@Override
public void onError(Throwable e) {
}
}
想必有老铁就问了,如果一个界面有多处需要请求网络的,那该怎么办呢?
因为我们是通过实现javabean的方法,java是单继承,多实现吗。这不就清楚了
当然你还可以直接使用
Map<String, Object> fields = new HashMap<>();
//调用的接口方法,比如login方法
fields.put("method", "user.login");
//加密和传一些常用参数
handleFields(fields);
//传递需要传递的参数
fields.put("phoneNo", userInfoBean.getPhoneNum());
fields.put("password", userInfoBean.getPwd());
//申请网络
Observable observable = RetrofitConnect.getApiService().login(fields);
toSubscribe(observable, new DefaultObserver(context,true) {
//数据返回在onNext
@Override
public void onSuccess(BaseRespond response) {
}
@Override
public void onNext(@NonNull BaseRespond response) {
userInfoBean.onSuccess(response);
}
@Override
public void onError(Throwable e) {
super.onError(e);
userInfoBean.onError(e);
}
});
初始化UserInfoRequest,就可以了。
还有很多不足的地方,希望各位老爷批评指正,大家共同进步,自己项目实战过的,不是demo,所以可以直接拿来用,有什么问题,还烦请指教、、、
PS:没有用到太多的RxJava的操作符,刚接触,还不是很熟悉,还不会灵活运用,见谅!!!
在这里要感谢那些为我们分享贡献的前辈,感谢他们的辛勤耕耘,才有我们菜鸟的不断进步。
特此感谢:
用水管讲解rxjava和retroft使用的:http://www.jianshu.com/p/464fa025229e
以及这位前辈提供的demo参考:https://github.com/zhpanvip/Retrofit2