一个完整的Android项目,肯定少不了网络请求的相关类。我整理了一下自己现在项目里二次封装的网络请求类,方便以后查阅,也希望大家指正。
Http请求使用了现在主流的第三方库OKHttp3,数据是json格式的,我直接用了gson。
添加对应的依赖:
compile 'com.squareup.okio:okio:1.11.0'
compile 'com.squareup.okhttp3:okhttp:3.6.0'
compile 'com.google.code.gson:gson:2.7'
整个网络请求功能主要由四个类组成:
· NetworkUtil:负责主要的网络请求及请求结果返回的处理。
· NetworkStatus:枚举类,包含了网络请求的返回结果状态
· Parameter:接口请求参数生成工具类
· NetworkCallBack:抽象类,用于http请求回调
首先说一下NetworkStatus
public enum NetworkStatus {
STATUS_SUCCESS("0", "请求成功", null),
STATUS_FAIL_PARAMS_ERROR("1", "请求参数异常", null),
STATUS_FAIL_SERVICE_ERROR("2", "服务器异常", null),
STATUS_FAIL_IO_EXCEPTION("2", "网络异常", null),
STATUS_FAIL_SESSION_OUT("4", "登录超时", null),
STATUS_FAIL_PARSING_ERROR("5", "数据解析出错", null);
private String code;
private String message;
private String responseStr;
NetworkStatus(String code, String message, String responseStr) {
this.code = code;
this.message = message;
this.responseStr = responseStr;
}
public String getMessage() {
return message;
}
public NetworkStatus setMessage(String message) {
this.message = message;
return this;
}
public String getCode() {
return code;
}
public NetworkStatus setCode(String code) {
this.code = code;
return this;
}
public static NetworkStatus getNetworkStatusByCode(String code){
NetworkStatus resultStatus = NetworkStatus.STATUS_FAIL_PARSING_ERROR;
for(NetworkStatus status: values()){
if(status.code.equals(code)){
resultStatus = status;
break;
}
}
return resultStatus;
}
public String getResponseStr() {
return responseStr;
}
public NetworkStatus setResponseStr(String responseStr) {
this.responseStr = responseStr;
return this;
}
}
这个枚举类包含了6个常量,代表可能出现的6中网络请求结果。类本身有三个参数,其中code和message是服务器返回的状态码和信息,responseStr是json字符串。
除了这三个方法的getter、setter以外,还有一个公共方法getNetworkStatusByCode(String code)。这个方法根据服务器给的code,返回具体的枚举对象。这个方法将用于对http response的处理。
接下来是Parameter类
public class Parameter {
private final static String DATA_FORMAT_JSON = "JSON";
private final static String ENCRYPT_TYPE_DES = "001";
/**
* 数据结构类型(json、xml等等)
*/
public String format;
/**
* 加密类型
* 001:Des加密
*/
public String encryptType;
/**
* 是否加密
* 0:不加密 1:加密
*/
public String isEncrypt;
/**
* 公共参数
*/
private HashMap<String, String> head;
private Object data;
/**
* 默认构造函数,初始化时默认:
* <br>format为FORMAT_JSON
* <br>encryptType为ENCRYPT_TYPE_DES
* <br>isEncrypt为true
* <br>并且在构造方法中设置公共参数
*/
public Parameter(Context context) {
head = new HashMap<>();
data = new HashMap<String, String>();
this.format = DATA_FORMAT_JSON;
this.encryptType = ENCRYPT_TYPE_DES;
this.isEncrypt = String.valueOf(BaseConfig.YES);
head.put("phoneName", DeviceInfo.getInstance().model);
head.put("phoneVersion", DeviceInfo.getInstance().osVersion);
head.put("imei", DeviceInfo.getInstance().imei);
head.put("softVersion", AppUtil.getAppVersionName());
head.put("imsi", DeviceInfo.getInstance().simImsi);
head.put("loginName", ClientApplication.getInstance().getUserInfo() == null ? "" : ClientApplication.getInstance().getUserInfo().getLoginName());
head.put("ownship", DeviceInfo.getInstance().simCarrier);
head.put("sk", DeviceInfo.getInstance().imei);
}
public void setHeadLoginName(String loginName){
head.put("loginName", loginName);
}
/**
* 生成公共参数
*
* @return 按照指定的数据结构类型返回相应的公共参数字符串表述
*/
public String getHeadString() {
if (TextUtils.isEmpty(format)) {
return null;
}
String result = null;
if (format.equals(DATA_FORMAT_JSON)) {
try {
JSONObject main = new JSONObject();
for (Map.Entry<String, String> entry : head.entrySet()) {
main.put(entry.getKey(), entry.getValue());
}
result = main.toString().trim();
} catch (JSONException e) {
e.printStackTrace();
}
}
return result;
}
public void setData(Object data) {
this.data = data;
}
/**
* 生成业务参数
*
* @return 按照指定的数据结构类型返回相应的业务参数字符串表述
*/
public String getDataString() {
if (data == null) {
return null;
}
String result = null;
Gson gson = null;
if (data instanceof Map) {
gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().enableComplexMapKeySerialization().create();
} else {
gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation() // 不导出实体中没有用@Expose注解的属性
// .serializeNulls()// 将取值为null的字段也输出到json字符串中
.setDateFormat("yyyy-MM-dd HH:mm:ss")// 时间转化为特定格式
.setPrettyPrinting() // 对json结果格式化.
.create();
}
result = gson.toJson(data);
return result;
}
@Override
public String toString() {
return "format=" + format + "&isKey=" + isEncrypt + "&keyType=" + encryptType + "&head=" + getHeadString() + "&data=" + getDataString();
}
}
手机端与服务器端的数据交互,一般都是通过“调用后台接口”来完成。
以自己的项目为例,接口请求格式如下:
http://ip:port/action_method.do?format=JSON&encryptType=001&isEncrypt=1&head={公共协议头}&data={业务内容}
action_method.do是后台具体某一接口的接口名。问号后面是请求所携带的参数。
前三个参数已经写死了,format指数据格式,本项目使用json格式。encryptType是报文加密格式(001在项目中是DES加密)。isEncrypt表示是否加密,1代表加密。
后面两个参数“head”和“data”是关键参数。其中head代表项目中公共的参数,从构造方法中可以看到,里面包含了手机硬件的各种信息以及用户的登录名。这样后台想要获取用户名,就可以每次直接从head里面取值了。
data代表不同业务逻辑自己需要的参数。比如说调用查询历史记录的接口,你需要传给后台“日期”参数;调用保存个性签名的接口,你需要上传编写的个性签名字符串作为参数。
data = new HashMap<String, String>();
data是一个HashMap,前一个key是后台指定的接口字段,后一个value就是字段对应的具体值。在构造Parameter对象的时候,我们可以自己十一画一个HashMap,传入key和value,然后通过Parameter的setData方法进行赋值。
NetworkCallBack是一个简单的抽象类,主要用于将OKHttp中的请求回调进行改造:
public abstract class NetworkCallBack {
public abstract void onStart();
/**
* 网络请求或者文件上传下载成功的结果回调
*
* @param result 保存网络请求返回数据
*/
public abstract void onSuccess(String result);
/**
* 网络请求或者文件上传下载失败的回调方法
*
* @param status 接口返回状态信息
*/
public abstract void onFail(NetworkStatus status);
/**
* 上传或者下载文件的时候进度返回
*
* @param current 当前字节数
* @param total 总字节数
* @param progress 进度百分比
*/
public void onProgress(long current, long total, int progress) {
}
public void onLoadPictureSuccess(byte[] bytes) {
}
}
其中onStart(), onSuccess(), onFail()这三个方法是必须要重写的。
最后是最重要的类NetworkUtil,就是在这个类里面对OKHttp进行了简单的二次封装。
public class NetworkUtil {
private final String TAG = getClass().getSimpleName() + "-->>";
/**
* 当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的instance变量(它得是一个对象)来充当锁
* 零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:
* 生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。
*/
private static final byte[] LOCK = new byte[0];
private static NetworkUtil mInstance;
private OkHttpClient mOkHttpClient;
private final static int WHAT_SUCCESS = 1;
private final static int WHAT_FAIL = 2;
private final static int WHAT_PROGRESS = 3;
public static NetworkUtil getInstance() {
if (mInstance == null) {
synchronized (LOCK) {
if (mInstance == null) {
mInstance = new NetworkUtil();
}
}
}
return mInstance;
}
/**
* 构造方法
* 实现 mOkHttpClient
*/
private NetworkUtil() {
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
clientBuilder.cookieJar(new CookieJar() {
private String JSESESSION_VALUE = null;
private ArrayList<Cookie> cookies = new ArrayList<>();
@Override
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
if(TextUtils.isEmpty(JSESESSION_VALUE)){
for(Cookie cookie:cookies){
// Log.d(TAG, url + "\nname = " + cookie.name() + "\nvalue = " + cookie.value());
if(cookie.name().equals("JSESSIONID")){
JSESESSION_VALUE = cookie.value();
this.cookies.clear();
this.cookies.add(cookie);
break;
}
}
}
// Log.d(TAG, "saveFromResponse:" + url.host());
for(Cookie cookie:cookies){
// Log.d(TAG, url + "\nname = " + cookie.name() + "\nvalue = " + cookie.value());
}
}
@Override
public List<Cookie> loadForRequest(HttpUrl url) {
if(cookies != null && cookies.size() > 0){
for(Cookie cookie:cookies){
// Log.d(TAG, url + "\nname = " + cookie.name() + "\nvalue = " + cookie.value());
}
}
return cookies;
}
});
clientBuilder.readTimeout(NetworkConfig.NETWORK_READ_TIMEOUT, TimeUnit.SECONDS);
clientBuilder.connectTimeout(NetworkConfig.NETWORK_CONNECT_TIMEOUT, TimeUnit.SECONDS);
clientBuilder.writeTimeout(NetworkConfig.NETWORK_WRITE_TIMEOUT, TimeUnit.SECONDS);
mOkHttpClient = clientBuilder.build();
}
/**
* 按照接口名称请求数据
* @param method
* @param parameter
* @param callBack
*/
public void post(final String method, Parameter parameter, final NetworkCallBack callBack){
postFullUrl(NetworkConfig.BASE_ACTION_URL + method, parameter, callBack);
}
/**
* post方法提交表单信息
* @param fullUrl 接口地址
* @param parameter 请求参数
* @param callBack 自定义的回调接口
*/
public void postFullUrl(final String fullUrl, Parameter parameter, final NetworkCallBack callBack){
final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case WHAT_SUCCESS:
String result = (String) msg.obj;
callBack.onSuccess(result);
break;
case WHAT_FAIL:
NetworkStatus status = (NetworkStatus) msg.obj;
callBack.onFail(status);
break;
}
}
};
FormBody.Builder builder = new FormBody.Builder();
String format = parameter.format;
String isKey = parameter.isEncrypt;
String keyType = parameter.encryptType;
String head = parameter.getHeadString();
String data = parameter.getDataString();
try { // 将传过来的参数字符串加密
head = Des3.encode(head);
data = Des3.encode(data);
} catch (Exception e1) {
throw new RuntimeException(e1);
}
builder.add("format", format);
builder.add("isKey", isKey);
builder.add("keyType", keyType);
builder.add("head", head);
builder.add("data", data);
//请求体
RequestBody body = builder.build();
//构建请求对象
Request request = new Request.Builder()
.url(fullUrl)
.post(body)
.build();
callBack.onStart();
//发起请求
Call call = mOkHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_IO_EXCEPTION));
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String result = response.body().string();
if(TextUtils.isEmpty(result)){
handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_SERVICE_ERROR));
return;
}
try {
String decodeResult = Des3.decode(result);
Log.d(TAG, "response:" + fullUrl + "\n" + decodeResult);
Gson gson = new GsonBuilder().create();
BaseResponseEntity baseResponse = gson.fromJson(decodeResult, BaseResponseEntity.class);
switch (NetworkStatus.getNetworkStatusByCode(baseResponse.getCode())){
case STATUS_SUCCESS:
handler.sendMessage(handler.obtainMessage(WHAT_SUCCESS, decodeResult));
break;
case STATUS_FAIL_PARAMS_ERROR:
handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_PARAMS_ERROR.setMessage(baseResponse.getMessage()).setResponseStr(decodeResult)));
break;
case STATUS_FAIL_SERVICE_ERROR:
handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_SERVICE_ERROR.setMessage(baseResponse.getMessage()).setResponseStr(decodeResult)));
break;
case STATUS_FAIL_SESSION_OUT:
handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_SESSION_OUT.setMessage(baseResponse.getMessage()).setResponseStr(decodeResult)));
break;
default:
handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_SERVICE_ERROR.setCode(baseResponse.getCode()).setMessage(baseResponse.getMessage()).setResponseStr(decodeResult)));
break;
}
}catch (Exception e){
e.printStackTrace();
handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_PARSING_ERROR));
}
}
});
}
}
这是一个单例类。
在构造方法中进行了cookie的处理,已经http请求超时的设置。(当然都是用的okhttp库的方法)
接下来的post方法和postFullUrl方法其实是一样的,只不过post方法只需要传入接口名,再将请求的url补全了以后传给postFullUrl方法。
· postFullUrl
首先实例化了一个Handler,这个在后面接收response的时候会用到。
然后创建FormBody实例,将接口所需的五个参数加入;
再创建okhttp的请求体RequestBody;
再构建请求对象Request。
此时发送http请求的准备工作已经就绪。
我们在发送请求之前,调用NetworkCallBack的onStart()方法。
(NetworkCallBack的具体实现在业务代码中,方法的重写自然也在具体业务代码里面进行,我会在最后写个小demo。)
接下来正式发起请求。
//发起请求
Call call = mOkHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_IO_EXCEPTION));
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String result = response.body().string();
if(TextUtils.isEmpty(result)){
handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_SERVICE_ERROR));
return;
}
try {
String decodeResult = Des3.decode(result);
Log.d(TAG, "response:" + fullUrl + "\n" + decodeResult);
Gson gson = new GsonBuilder().create();
BaseResponseEntity baseResponse = gson.fromJson(decodeResult, BaseResponseEntity.class);
switch (NetworkStatus.getNetworkStatusByCode(baseResponse.getCode())){
case STATUS_SUCCESS:
handler.sendMessage(handler.obtainMessage(WHAT_SUCCESS, decodeResult));
break;
case STATUS_FAIL_PARAMS_ERROR:
handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_PARAMS_ERROR.setMessage(baseResponse.getMessage()).setResponseStr(decodeResult)));
break;
case STATUS_FAIL_SERVICE_ERROR:
handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_SERVICE_ERROR.setMessage(baseResponse.getMessage()).setResponseStr(decodeResult)));
break;
case STATUS_FAIL_SESSION_OUT:
handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_SESSION_OUT.setMessage(baseResponse.getMessage()).setResponseStr(decodeResult)));
break;
default:
handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_SERVICE_ERROR.setCode(baseResponse.getCode()).setMessage(baseResponse.getMessage()).setResponseStr(decodeResult)));
break;
}
}catch (Exception e){
e.printStackTrace();
handler.sendMessage(handler.obtainMessage(WHAT_FAIL, NetworkStatus.STATUS_FAIL_PARSING_ERROR));
}
}
});
在onFailure和onResponse两个回调中,根据不同的情况,发送不同的message给最前面的handler。
BaseResponseEntity是项目用的实体类,用来接收服务器返回的code和message。
public class BaseResponseEntity implements Serializable {
private String code;
private String message;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
在对code进行区分的时候,用到了之前的类NetworkStatus中的getNetworkStatusByCode()方法。
这样,一个简单的基于okhttp3的网络请求框架就封装好了,我们可以很方便的使用它,举个例子:
//从服务器获取信息的方法
private void getInfo() {
Parameter parameter = new Parameter(this);
Map<String, String> map = new LinkedHashMap<String, String>();
map.put("name", "zz");
map.put("age", "19");
parameter.setData(map);
NetworkUtil.getInstance().post("getInfo.do", parameter, new NetworkCallBack() {
@Override
public void onStart() {
showProgressDialog("正在获取info");
}
@Override
public void onSuccess(String result) {
dismissProgressDialog();
Gson gson = new GsonBuilder().create();
Info info = gson.fromJson(result, Info.class);
//后续操作···
}
@Override
public void onFail(NetworkStatus status) {
dismissProgressDialog();
processNetWorkFailed(status, true, false);
}
});
}
processNetWorkFailed是一个用于处理请求失败的方法。
/**
* 处理请求失败的时候的逻辑
* @param status
* @param showToast
* @param finish
*/
protected void processNetWorkFailed(NetworkStatus status, boolean showToast, boolean finish) {
switch (status) {
case STATUS_FAIL_IO_EXCEPTION:
case STATUS_FAIL_PARAMS_ERROR:
case STATUS_FAIL_SERVICE_ERROR:
case STATUS_FAIL_PARSING_ERROR:
if (showToast) {
uiHandler.showShortToast(status.getMessage());
}
if (finish) {
finish();
}
break;
case STATUS_FAIL_SESSION_OUT:
if (!isFinishing()) {
uiHandler.showShortToast(status.getMessage());
AccountsPreference.setAutoLogin(false);
Intent intent = new Intent(BaseActivity.this, LoginActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
finish();
}
break;
}
}