公司上個項目用的是rxjava1.0,最近看了各種rxjava2.0的介紹块请,自己也摸索著來宵统,所以打算封裝一下优妙,用到項目中子巾,與時俱進嘛胚宦!先不多逼逼了脊凰,直截了當,上貨......
先看一下關(guān)于怎么配置Retrofit箭券,這個大家都差不多净捅,就不多解釋了。辩块。蛔六。base_url直接換成自己公司服務(wù)器的地址就可以了
github地址:https://github.com/Veken/RxJava2Retrofit
1 封裝
Retrofit配置類
/**
* Retrofit配置
*/
public class RetrofitConnect {
private Retrofit retrofit;
private Service service;
/**
* 網(wǎng)絡(luò)請求超時時間毫秒
*/
int DEFAULT_TIMEOUT = 20000;
/**
* 你們公司自己的服務(wù)器地址
*/
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);
}
// 創(chuàng)建單例
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()) { //沒網(wǎng)強制從緩存讀取
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
LogUtils.d("Okhttp", "no network");
}
Response originalResponse = chain.proceed(request);
if (NetworkUtils.isConnected()) {
//有網(wǎng)的時候讀接口上的@Headers里的配置,你可以在這里進行統(tǒng)一的設(shè)置
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();
}
}
}
}
接下來第一步封裝废亭,把請求的一些網(wǎng)絡(luò)線程等等封裝在一個類SubScriberHandler里面
并且把post請求的參數(shù)進行sha加密国章,參數(shù)可以根據(jù)公司自己的要求,自己更改
public class SubScriberHandler {
/**
* @param o
* @param <T>
*/
public <T> void toSubscribe(Observable<T> o, DefaultObserver observer) {
o.subscribeOn(Schedulers.io()) //網(wǎng)絡(luò)耗時操作在io線程處理
.unsubscribeOn(Schedulers.io()) //網(wǎng)絡(luò)耗時操作在io線程處理
.observeOn(AndroidSchedulers.mainThread()) //更新數(shù)據(jù)在主線程
.subscribe(observer);
}
/**
* 添加共同參數(shù) 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);
}
}
接下來是關(guān)于自定義的實現(xiàn)Observer的類DefalutObserver豆村,同時也添加了一個加載數(shù)據(jù)時候的progressdialog液兽,可以根據(jù)公司的需求更改成自己的progressdialog,我這只是簡單的調(diào)用普通的。各位客官也可以自己看心情來封裝
**
* @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 服務(wù)器返回的數(shù)據(jù)
*/
abstract public void onSuccess(T response);
/**
* 服務(wù)器返回數(shù)據(jù)四啰,但響應碼不為200
*
* @param response 服務(wù)器返回的數(shù)據(jù)
*/
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;
}
}
/**
* 請求網(wǎng)絡(luò)失敗原因
*/
public enum ExceptionReason {
/**
* 解析數(shù)據(jù)失敗
*/
PARSE_ERROR,
/**
* 網(wǎng)絡(luò)問題
*/
BAD_NETWORK,
/**
* 連接錯誤
*/
CONNECT_ERROR,
/**
* 連接超時
*/
CONNECT_TIMEOUT,
/**
* 未知錯誤
*/
UNKNOWN_ERROR,
}
/**
* 取消ProgressDialog的時候宁玫,取消對observable的訂閱,同時也取消了http請求
*/
@Override
public void onCancelProgress() {
if (!mDisposable.isDisposed()) {
mDisposable.dispose();
}
}
}
現(xiàn)在就是大家耳熟能詳?shù)腟ervice類柑晒,這就沒什么可說的欧瘪,你要什么請求,就是什么匙赞,比如你要登錄就寫一個login的service佛掖,然后Map中裝你需要請求的參數(shù),以此類推涌庭。
/**
* @author Veken
*/
public interface Service {
/**
* 登錄的service
* @param fields
* @return
*/
@FormUrlEncoded
@POST(Constants.URLEND)
Observable<LoginRespond> login(@FieldMap Map<String, Object> fields);
}
現(xiàn)在到了正兒八經(jīng)的封裝了芥被,前面的都是開胃菜,配置都差不多坐榆,大同小異撕彤,到上真貨的時候了
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<>();
//調(diào)用的接口方法,比如login方法
fields.put("method", "user.login");
//加密和傳一些常用參數(shù)
handleFields(fields);
//需要傳遞的參數(shù)
fields.put("phoneNo", userInfoBean.getPhoneNum());
fields.put("password", userInfoBean.getPwd());
//申請網(wǎng)絡(luò)
Observable observable = RetrofitConnect.getApiService().login(fields);
toSubscribe(observable, new DefaultObserver(context,true) {
//數(shù)據(jù)返回在onNext
@Override
public void onSuccess(BaseRespond response) {
}
//數(shù)據(jù)返回
@Override
public void onNext(@NonNull BaseRespond response) {
//將結(jié)果封裝在javabean的onSuccess方法里面
userInfoBean.onSuccess(response);
}
@Override
public void onError(Throwable e) {
super.onError(e);
userInfoBean.onError(e);
}
});
}
}
2 調(diào)用
下面就是具體調(diào)用了猛拴,看一下在MainActivity中怎么簡單的調(diào)用吧
實現(xiàn)UserInfoBean,這樣可以在重寫的方法中傳入我們需要的參數(shù)蚀狰,當然也可以不寫一個bean愉昆,直接用String傳也可以,但是習慣了bean麻蹋,數(shù)據(jù)處理也在bean中跛溉,簡單明了。
初始化你的UserInfoPresent網(wǎng)絡(luò)請求
private UserInfoRequest userInfoRequest;
userInfoRequest= new UserInfoRequest(this,this);
如果沒有實現(xiàn)bean的話扮授,UserInfoPresent()方法中的參數(shù)會報錯芳室,如果不想調(diào)用bean,可以自己簡單的修改一下
剩下的就簡單了刹勃,在自己想要請求網(wǎng)絡(luò)的地方堪侯,調(diào)用 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() {
//根據(jù)需要,傳遞相應的數(shù)據(jù)
return "登錄賬號";
}
//傳遞的密碼
@Override
public String getPwd() {
//根據(jù)需要荔仁,傳遞相應的數(shù)據(jù)
return "登錄密碼";
}
//數(shù)據(jù)返回
@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) {
}
}
想必有老鐵就問了伍宦,如果一個界面有多處需要請求網(wǎng)絡(luò)的,那該怎么辦呢乏梁?
因為我們是通過實現(xiàn)javabean的方法次洼,java是單繼承,多實現(xiàn)嗎遇骑。這不就清楚了
當然你還可以直接使用
Map<String, Object> fields = new HashMap<>();
//調(diào)用的接口方法卖毁,比如login方法
fields.put("method", "user.login");
//加密和傳一些常用參數(shù)
handleFields(fields);
//傳遞需要傳遞的參數(shù)
fields.put("phoneNo", userInfoBean.getPhoneNum());
fields.put("password", userInfoBean.getPwd());
//申請網(wǎng)絡(luò)
Observable observable = RetrofitConnect.getApiService().login(fields);
toSubscribe(observable, new DefaultObserver(context,true) {
//數(shù)據(jù)返回在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,就可以了落萎。
還有很多不足的地方亥啦,希望各位老爺批評指正炭剪,大家共同進步,自己項目實戰(zhàn)過的禁悠,不是demo念祭,所以可以直接拿來用,有什么問題碍侦,還煩請指教粱坤、、瓷产、
PS:沒有用到太多的RxJava的操作符站玄,剛接觸,還不是很熟悉濒旦,還不會靈活運用株旷,見諒!6恕晾剖!
在這里要感謝那些為我們分享貢獻的前輩,感謝他們的辛勤耕耘梯嗽,才有我們菜鳥的不斷進步齿尽。
特此感謝:
用水管講解rxjava和retroft使用的:http://www.reibang.com/p/464fa025229e
以及這位前輩提供的demo參考:https://github.com/zhpanvip/Retrofit2