前言
目前的移动流量越来越便宜,手机内存也越来越大。随着Android 手机多年的发展,用户对Android 系统的接受度、认可度越来越高,加之Android 系统的开源性,更多的移动硬件接入Android生态系统,物联网和人工智能的时代来临了,我们进入了一个全新的人工智能时代,万物联网,一切都为智能而生,与此同时,人们对移动终端请求网络的速度和效率要求也越来越苛刻。网络请求的流畅性决定了一款app 产品的用户对产品的依赖度;
目前网路请求处理不当,会造成哪些问题:
- 请求超时,获取数据缓慢。造成系统流畅性降低;如果-UI界面请求的网路加载进度条一直转圈,超时处理;
- 耗费网络流量,增加用户的使用成本;比如,从网上下载过大的视频,照片,尤其是在移动网络中使用;
- 网络的安全性,敏感数据在网络上传递的过程中没有加密或者加密级别太低,秘钥被盗用,数据被黑客拦截,篡改数据包,重新请求;造成用户数据泄露等等;
目前业内常用的网络请求框架:
Volley、async-http、okhttp2、retrofit2;这些框架已经在项目中多次被应用,根据现在近来做的项目,总结一下我自己在工作中用到的网络请求框架,以及平时遇到的问题;
现在的公司使用的是OkHttp3+retrofit2二者结合使用;
首先在build.gradle (Moudle:app) 中引入Okhttp框架包,for example:
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.3.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.8.1'
整个请求分为三部分: 统一的数据结构(code、msg、data)
以登录请求举例:
LoginService.java
支持多种方式访问网络GET、POST、PUT、DELETE等请求协议
加入基础ApiService,减少Api冗余
支持动态配置和自定义底层框架Okhttpclient、Retrofit.
package com.fintech.cuidaren.retrofit.services;
import com.fintech.cuidaren.base.BaseConfig;
import com.fintech.cuidaren.bean.BeanDictData;
import com.fintech.cuidaren.bean.BeanLogin;
import com.fintech.cuidaren.bean.BeanPass;
import com.fintech.cuidaren.bean.BeanSaveLatitude;
import com.fintech.cuidaren.bean.BeanUpdateData;
import com.fintech.cuidaren.bean.UserLogin;
import java.util.List;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Query;
// 提供请求的api
public interface ApiService {
String BASE_SERVICE = BaseConfig.SERVER_URL;
@POST("/business-service/api/login/login")
Call<UserLogin> login(@Body BeanLogin beanLogin);
//修改密码
@POST("business-service/api/login/updatePassword")
Call<String> updatePassword(@Body BeanPass beanPass);
//获取数据字典所有数据
@GET("/business-service/api/dataDictController/getAll")
Call<List<BeanDictData>> getAllDict();
//发布新版本
@GET("/common-service/api/appVersionController/publishAppVersion")
Call<BeanUpdateData> getAppVersion(@Query("os")String os, @Query("version")String version);
}
创建HttpAction 基类;
创建一个OkHttpClient 对象;
package com.fintech.cuidaren.retrofit.services;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public abstract class BaseHttpAction {
OkHttpClient httpClient;
public BaseHttpAction(OkHttpClient httpClient) {
this.httpClient = httpClient;
}
protected <T> T createService(String url, final Class<T> service) {
return new Retrofit.Builder().baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient)
.build()
.create(service);
}
public OkHttpClient getHttpClient() {
return httpClient;
}
}
为每个api 构建一个Action类,并继承BaseHttpAction.java 类;
package com.fintech.cuidaren.retrofit.action;
import com.fintech.cuidaren.bean.BeanDictData;
import com.fintech.cuidaren.bean.BeanLogin;
import com.fintech.cuidaren.bean.BeanPass;
import com.fintech.cuidaren.bean.BeanSaveLatitude;
import com.fintech.cuidaren.bean.BeanUpdateData;
import com.fintech.cuidaren.bean.UserLogin;
import com.fintech.cuidaren.retrofit.services.AbsRemoteAction;
import com.fintech.cuidaren.retrofit.services.AccessTokenInterceptor;
import com.fintech.cuidaren.retrofit.services.LoginService;
import com.fintech.cuidaren.retrofit.services.RetrofitCallback;
import java.util.List;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import retrofit2.Call;
public class LoginAction extends BaseHttpAction {
private final LoginService mloginService;
public LoginAction(OkHttpClient httpClient) {
super(httpClient);
mloginService = (LoginService) createService(LoginService.BASE_SERVICE, LoginService.class);
}
private void updateToken(String token) {
OkHttpClient httpClient = getHttpClient();
for (Interceptor interceptor : httpClient.interceptors()) {
if (interceptor instanceof AccessTokenInterceptor) {
((AccessTokenInterceptor) interceptor).updateToken(token);
break;
}
}
}
//请求成功的回调 onSuccess()
// 请求失败的回调 onFailure()
public void login(BeanLogin beanLogin, final LoginDataListener mLoginDataListener) {
Call<UserLogin> call = mloginService.login(beanLogin);
call.enqueue(new RetrofitCallback<UserLogin>() {
@Override
public void onSuccess(UserLogin model) {
mLoginDataListener.loginData(model);
updateToken(model.getToken());
}
@Override
public void onFailure(int code, String msg) {
}
@Override
public void onThrowable(Throwable t) {
}
});
}
public interface LoginDataListener {
void loginData(UserLogin mUserLogin);
}
public void GetDictAllData(final getDictDataListener getDictDataListener) {
Call<List<BeanDictData>> call = mloginService.getAllDict();
call.enqueue(new RetrofitCallback<List<BeanDictData>>() {
@Override
public void onSuccess(List<BeanDictData> model) {
getDictDataListener.getDictData(model);
}
@Override
public void onFailure(int code, String msg) {
}
@Override
public void onThrowable(Throwable t) {
}
});
}
public interface getDictDataListener {
void getDictData(List<BeanDictData> beanDictData);
}
// 修改密码:
public void modifyPwd (BeanPass beanPass, final UpdatePwdListener mUpdatePwdListener) {
Call<String> call = mloginService.updatePassword(beanPass);
call.enqueue(new RetrofitCallback<String>() {
@Override
public void onSuccess(String model) {
}
@Override
public void onFailure(int code, String msg) {
}
@Override
public void onThrowable(Throwable t) {
mUpdatePwdListener.getUpdatePwdResult();
}
});
}
public interface UpdatePwdListener {
void getUpdatePwdResult();
}
// 退出登录
//发布新版本
public void publishAppVersion(String os,String version, final publishAppVersionListener publishAppVersionListener) {
Call<BeanUpdateData> call = mloginService.getAppVersion(os,version);
call.enqueue(new RetrofitCallback<BeanUpdateData>() {
@Override
public void onSuccess(BeanUpdateData model) {
publishAppVersionListener.getPublishAppVersion(model);
}
@Override
public void onFailure(int code, String msg) {
}
@Override
public void onThrowable(Throwable t) {
}
});
}
public interface publishAppVersionListener {
void getPublishAppVersion(BeanUpdateData beanUpdateData);
}
public void setSaveLatitudeAndLongitude(BeanSaveLatitude beanSaveLatitude, final setSaveLatitudeAndLongitudeListener saveLatitudeAndLongitudeListener) {
Call<String> call = mloginService.saveLatitudeAndLongitude(beanSaveLatitude);
call.enqueue(new RetrofitCallback<String>() {
@Override
public void onSuccess(String model) {
}
@Override
public void onFailure(int code, String msg) {
}
@Override
public void onThrowable(Throwable t) {
saveLatitudeAndLongitudeListener.saveLatitudeAndLongitude();
}
});
}
public interface setSaveLatitudeAndLongitudeListener {
void saveLatitudeAndLongitude();
}
}
新建ManagerAction.java 类,(对所有的Action 进行统一管理)
代码模块如下:
public class ManagerAcion{
private static ManagerAcion instance;
private String accesstoken;
private Context contexts;
private LoginAction loginAction;
private ManagerAcion(Context context){
contexts=context;
accesstoken = PreferencesManager.getInstance(context).get("Access_token");
initRepositories();
}
public synchronized static DataManger init(Context context) {
if (instance != null) {
throw new RuntimeException("DataManager already init!");
}
return instance = new DataManger(context.getApplicationContext());
}
private void initRepositories() {
OkHttpClient httpClient = createHttpClient();
mLoginAction = new LoginAction(httpClient);
}
public LoginAction getLoginAction() {
return mLoginAction;
}
private OkHttpClient createHttpClient() {
final Map<String, String> headers = new HashMap<>();
headers.put("X-UserToken", accesstoken);
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
if (BuildConfig.DEBUG) {
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
} else {
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
}
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient okHttpClient = new OkHttpClient();
OkHttpClient.Builder builder = okHttpClient.newBuilder()
.addInterceptor(loggingInterceptor)
.addInterceptor(new AccessTokenInterceptor(headers))
.hostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS);
Interceptor stethoInterceptor = getStethoInterceptor();
if (stethoInterceptor != null) {
builder.addNetworkInterceptor(stethoInterceptor);
}
return builder.build();
}
private Interceptor getStethoInterceptor() {
try {
Class<?> cls = Class.forName("com.facebook.stetho.okhttp3.StethoInterceptor");
return (Interceptor) cls.newInstance();
} catch (ClassNotFoundException ignored) {
;// ignored
} catch (InstantiationException ignored) {
;// ignored
} catch (IllegalAccessException ignored) {
;// ignored
}
return null;
}
}
到此一个基本的网络请求的封装类就写完了,下面我们看看它是怎么在具体的接口中使用的;
以LoginActivity.java 为例;
LoginActivity.java 继承BaseActivity.java ;在父类中 初始化ManagerAction;
部分代码块如下:
LoginAction loginAction = dataManger.getLoginAction();
if (!NetWorkUtils.isNetworkAvailable(LoginActivity.this)) {
ToastUtil.showToastOnce(LoginActivity.this, getString(R.string.error_network));
}
loginAction.login(beanLogin, new LoginAction.LoginDataListener() {
@Override
public void loginData(UserLogin mUserLogin) {
if (mUserLogin != null) {
pm.put("token", mUserLogin.getToken());
pm.put("icon", mUserLogin.getUser().getPhoto());
pm.put("userName", mTvUserName.getText().toString());
pm.put("password", mTvPass.getText().toString());
pm.put("customename", mUserLogin.getUser().getRealName());
pm.put("UserId",mUserLogin.getUser().getId());
ToastUtil.showToastDefault(LoginActivity.this, LoginActivity.this.getString(R.string.pg_loginSuccess));
IntentUtils.getIntents().Intent(LoginActivity.this, MainActivity.class);
finish();
} else {
// ToastUtil.showToastDefault(LoginActivity.this, "登录失败");
}
}
});
后记
这种模式有个弊端,就是针对每一接口都是去写一个action ,然后在ManagerAction 类中注册管理,如果app API 接口多,则要写很多Action ,从app 设计角度讲,没有做到代码的重用,显得整个项目结构冗余;在下一篇中,我会针对这种情况给出另一种实现方案;不过还是用OkHttp2+retrofit2实现代码逻辑;