在移动端开发中和基础架构同样重要的是网络封装,包括对JSON数据的处理。Android中有很多好用的网络请求工具,这里我选择的是OkHttp
一、网络配置文件
新建单例类NetWorkManager,这个类的功能是创建一个全局唯一的OkHttpClient,每次网络请求都使用这个OkHttpClient类来创建一个Call类,每个Call对象只能进行一次网络请求。如果每次网络请求都创建OkHttpClient会造成资源的极大浪费。在这个单例类中可以对OkHttpClient单例对象配置超时时间、请求头、编码格式等基础配置,还可以实现动态修改超时时间,取消某个网络请求等功能。
/**
* 一、网络请求相关的配置,包括超时时间、请求头等
* 二、生成OkHttpClient单例对象,并且以次单例对象生成Call对象进行网络请求
*/
public class NetWorkManager {
private static int mConnectTimeout = 10000; //默认连接超时时间10秒=10000毫秒
private static int mReadTimeout = 10000; //默认读取超时时间10秒=10000毫秒
private static int mWriteTimeout = 10000; //默认写入超时时间10秒=10000毫秒
/**
* 存放当前正在网络请求的call们,以备后续进行取消等操作
*/
private static ArrayList<Call> callList = new ArrayList<Call>();
/**
* 声明OkHttp的客户端类
*/
private static volatile OkHttpClient mOkHttpClient = null;
/**
* 私有化构造方法防止外部创建
*/
private NetWorkManager(){}
/**
* 定义类型变量(不初始化,不使用final关键字,使用volatile保证了多线程访问时mNetWorkManager变量的可见性,
* 避免了mNetWorkManager初始化时其他变量属性还没有赋值完时被另外线程调用)
*/
private static volatile NetWorkManager mNetWorkManager = null;
/**
* 通过OkHttpClient的单例对象创造一个call对象进行本次网络请求
* @param url 地址
* @param map 参数map
* @param method GET/POST
* @return
*/
public static Call getInstance( String url, HashMap<String,Object> map, String method){
/**
* 对象实例化时调用,不使用同步代码块,mNetWorkManager!=null时直接返回对象,提高运行效率
*/
if (mNetWorkManager == null){
synchronized (NetWorkManager.class){
//未初始化时,初始化mNetWorkManager对象
if (mNetWorkManager == null){
mNetWorkManager = new NetWorkManager();
mOkHttpClient = new OkHttpClient
.Builder()
.connectTimeout(mConnectTimeout,TimeUnit.MILLISECONDS)
.readTimeout(mReadTimeout,TimeUnit.MILLISECONDS)
.writeTimeout(mWriteTimeout,TimeUnit.MILLISECONDS)
.build();
}
}
}
/**
* 如果通过setConnectTimeout方法修改了连接超时时间,
* 那么再次进行网络请求时要修改为默认的连接超时时间
*/
if (mOkHttpClient.connectTimeoutMillis() != mConnectTimeout){
try {
Field connectTimeoutField = mOkHttpClient.getClass().getDeclaredField("connectTimeout");
connectTimeoutField.setAccessible(true);
connectTimeoutField.setInt(mOkHttpClient,mConnectTimeout);
}catch (NoSuchFieldException e){
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
/**
* 如果通过setReadTimeout方法修改了连接超时时间,
* 那么再次进行网络请求时要修改为默认的读取超时时间
*/
if (mOkHttpClient.readTimeoutMillis() != mReadTimeout){
try {
Field readTimeoutField = mOkHttpClient.getClass().getDeclaredField("readTimeout");
readTimeoutField.setAccessible(true);
readTimeoutField.setInt(mOkHttpClient,mReadTimeout);
}catch (NoSuchFieldException e){
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
/**
* 如果通过setWriteTimeout方法修改了连接超时时间,
* 那么再次进行网络请求时要修改为默认的写入超时时间
*/
if (mOkHttpClient.writeTimeoutMillis() != mWriteTimeout){ //通过其他方法修改过写入超时时间
try {
Field writeTimeoutField = mOkHttpClient.getClass().getDeclaredField("writeTimeout");
writeTimeoutField.setAccessible(true);
writeTimeoutField.setInt(mOkHttpClient,mWriteTimeout);
}catch (NoSuchFieldException e){
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
//将参数字段转化为json字符串
Gson gson = new Gson();
String json = gson.toJson(map);
//设置请求参数的格式为utf-8
MediaType JSON = MediaType.parse("application/json;charset=UTF-8");
RequestBody body = RequestBody.create(JSON,json);
Request request;
//请求头
Headers headers = new Headers.Builder()
.add("os","ios")
.add("appname","crm")
.add("version","2.9.1")
.add("Content-Type","application/json;charset=UTF-8")
.build();
if (method.equals(PCH.mHttpRequestPost)){ //post请求
request = new Request.Builder()
.url(url)
.post(body)
.headers(headers)
.build();
}else {//get请求
request = new Request.Builder()
.url(url)
.get()
.headers(headers)
.build();
}
Call call = mOkHttpClient.newCall(request);
callList.add(call);
return call;
}
/**
* 如果当前网络请求已经成功或者失败,那么要将当前call从callList中删除
* @param call
*/
public static void removeCall(Call call){
callList.remove(call);
}
/**
* 单独修改本次网络请求的连接超时时间
* @param connectTimeout
*/
public static void setConnectTimeout(int connectTimeout){
if (mOkHttpClient != null && mOkHttpClient.connectTimeoutMillis() != connectTimeout){
try {
Field connectTimeoutField = mOkHttpClient.getClass().getDeclaredField("connectTimeout");
connectTimeoutField.setAccessible(true);
connectTimeoutField.setInt(mOkHttpClient,connectTimeout);
}catch (NoSuchFieldException e){
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
/**
* 单独修改本次网络请求的读取超时时间
* @param readTimeout
*/
public static void setReadTimeout(int readTimeout){
if (mOkHttpClient != null && mOkHttpClient.readTimeoutMillis() != readTimeout){
try {
Field readTimeoutField = mOkHttpClient.getClass().getDeclaredField("readTimeout");
readTimeoutField.setAccessible(true);
readTimeoutField.setInt(mOkHttpClient,readTimeout);
}catch (NoSuchFieldException e){
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
/**
* 单独修改本次网络请求的读取超时时间
* @param writeTimeout
*/
public static void setWriteTimeout( int writeTimeout ) {
if (mOkHttpClient != null && mOkHttpClient.readTimeoutMillis() != writeTimeout){
try {
Field writeTimeoutField = mOkHttpClient.getClass().getDeclaredField("writeTimeout");
writeTimeoutField.setAccessible(true);
writeTimeoutField.setInt(mOkHttpClient,writeTimeout);
}catch (NoSuchFieldException e){
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
二、新建网络请求工具类NetWorkTool
/**
* 网络请求方法
*/
public class NetWorkTool {
/**
* 网络请求
* @param url 地址
* @param paraDic 地址
* @param method GET/POST
* @param myCallBack 回调
*/
public static <T extends BaseDao> void request( final String url, final HashMap<String,Object> paraDic, String method, final MyCallBack<T> myCallBack){
/**
* 通过NetWorkManager.getInstance(url,paraDic,method)获取一个call对象进行异步网络请求
*/
Call call = NetWorkManager.getInstance(url,paraDic,method);
// if (url == ""){ //可以根据具体url设置不同超时时间😂
// NetWorkManager.setConnectTimeout(15000); //可以设置本次网络请求的超时时间
// }
call.enqueue(new Callback() {
/**
* 网络请求失败 没有访问到服务器
* @param call
* @param e
*/
@Override
public void onFailure( Call call, IOException e ) {
/**
* 打印出本次网络请求的错误信息,开发调试使用,生产环境可以注释掉
*/
Log.d(" "," ");Log.d(" "," ");Log.d(" "," ");
Log.d("网络请求失败url=",url);
Log.d("requestHeader=", String.valueOf(call.request().headers()));
Log.d("requestPara=", String.valueOf(paraDic));
JUtil.i("response==", String.valueOf(e));
Log.d("","--------------------------------------------------------------------------------------------------------");
Log.d(" "," ");Log.d(" "," ");Log.d(" "," ");
/**
* 在NetWorkManager的单例对象中保留当前网络请求的所有call对象,
* 当本次网络请求结束(成功、失败)以后删除此call对象
*/
NetWorkManager.removeCall(call);
myCallBack.onError(PCH.mHttpConnectError,e);
}
/**
* 网络请求访问到服务器了,分析服务器数据得到想要的结果
* 如果数据很简单不需要解析为model可以直接传入BaseDao,然后获取
* myCallBack.onSuccess(t,jsonStr,response)中的jsonStr自己做解析
*
* @param call
* @param response
* @throws IOException
*/
@Override
public void onResponse( Call call, Response response ) throws IOException {
/**
* 在NetWorkManager的单例对象中保留当前网络请求的所有call对象,
* 当本次网络请求结束(成功、失败)以后删除此call对象
*/
NetWorkManager.removeCall(call);
//response.body().string()只能执行一次
// 否则会报AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher错误
String jsonStr = response.body().string();
/**
* 打印出本次网络请求的信息
*/
//格式化输出获取的数据,开发调试使用,生产环境可以注释掉
Gson gson = new GsonBuilder().setPrettyPrinting().create();
HashMap<String,Object> jsonModel = gson.fromJson(jsonStr,new TypeToken<HashMap<String,Object>>(){}.getType());
String outputJsonStr = gson.toJson(jsonModel,new TypeToken<HashMap<String,Object>>(){}.getType());
Log.d(" "," ");Log.e(" "," ");Log.i(" "," ");
Log.d("网络请求成功url=",url);
Log.d("requestHeader=", String.valueOf(call.request().headers()));
Log.d("requestPara=", String.valueOf(paraDic));
JUtil.i("response==",outputJsonStr);
Log.d("","--------------------------------------------------------------------------------------------------------");
Log.d(" "," ");Log.e(" "," ");Log.i(" "," ");
//设计思想: 由上而下,层层细化: ,传入要解析对象,返回解析实体对象。
T t = null;
// 解析对象过程
//获得泛型集合
//实体类型
Class<? extends MyCallBack> callBackClass = myCallBack.getClass();
String name = callBackClass.getSimpleName();
Type[] interfaces = callBackClass.getGenericInterfaces(); //获取接口类型
if (!(interfaces[0] instanceof ParameterizedType)){ //MyCall中的范型T不能为空,如果不需要解析为model可以传BaseDao
myCallBack.onError(PCH.mHttpParseError,null);
return;
}
ParameterizedType parameterizedType = (ParameterizedType) (interfaces[0]);
Type type = parameterizedType.getActualTypeArguments()[0];
Class<T> entityClass = (Class<T>) (type);
t = NetUtil.parse(jsonStr,entityClass);
if (t != null){
if (t.getStatusCode() == 200){ //请求成功了 状态码200表示成功
myCallBack.onSuccess(t,jsonStr,response);
}else if (t.getStatusCode() == 401){ //用户未登录 602表示用户不是销售
}else if (t.getStatusCode() == 600){ //升级
}else { //请求失败
myCallBack.onError(PCH.mHttpConnectError,null);
}
}else {
myCallBack.onError(PCH.mHttpConnectError,null);
}
}
});
}
}
三、新建Dao的基类BaseDao
第二章节中的网络请求需要传入一个dao对象,然后返回一个数据解析成功之后的dao文件,这个dao不能为空,这个BaseDao包含了所有接口都会返回的最基本的信息
public class BaseDao {
private String status;
private int statusCode;
private String message;
public String getStatus() {
return status;
}
public void setStatus( String status ) {
this.status = status;
}
public int getStatusCode() {
return statusCode;
}
public void setStatusCode( int statusCode ) {
this.statusCode = statusCode;
}
public String getMessage() {
return message;
}
public void setMessage( String message ) {
this.message = message;
}
}
四、使用方法
/**
*这个类可以做首页初始化完成之后需要处理的事情,
* 首页显示的东西在TabbarActivity已经初始化完成,这里可以什么都不做 😂
*/
HashMap<String,Object> para = new HashMap<>();
para.put("username","username");
para.put("password","password");
NetWorkTool.request(PCH.requestLoginAction, para, PCH.mHttpRequestPost, new MyCallBack<BaseDao>() {
@Override
public void onSuccess( BaseDao baseDao, String responseString, Response response ) {
// Gson gson = new Gson();
// HashMap<String,String> obj = gson.fromJson(responseString,(new HashMap<String,Object>()).getClass());
// String string = obj.get("data").toString();
Log.d("网络请求成功",responseString);
// SharedPreferences sharedPreferences = getSharedPreferences(PCH.userLoginCookieKey,Context.MODE_PRIVATE);
// SharedPreferences.Editor editor = sharedPreferences.edit();
// editor.putString(PCH.userLoginCookieKey, "f26bf1fc7f9640fe8c8fa7926aebb414");
// editor.apply();
}
@Override
public void onError( String errString, IOException e ) {
Log.d("网络请求失败",errString);
}
});
五、项目实战
相比较iOS我比较喜欢Android的一个用法就是内部类的使用,尤其在Dao中,不需要新建很多的Dao类,在一个类中根据json数据的结构新建内部类就可以了,见以下代码
public class MessageListDao extends BaseDao {
private DataBean data = new DataBean();
public DataBean getData() {
return data;
}
/**
* data中的数据
*/
public static class DataBean{
private int pageSize = 0;
private int pageNo = 0;
private int count = 0; //共多少条数据
private int pageCount = 0; //共多少页数据
private ArrayList<ListData> list = new ArrayList<>();
public int getPageSize() {
return pageSize;
}
public void setPageSize( int pageSize ) {
this.pageSize = pageSize;
}
public int getPageNo() {
return pageNo;
}
public void setPageNo( int pageNo ) {
this.pageNo = pageNo;
}
public int getCount() {
return count;
}
public void setCount( int count ) {
this.count = count;
}
public int getPageCount() {
return pageCount;
}
public void setPageCount( int pageCount ) {
this.pageCount = pageCount;
}
public ArrayList<ListData> getList() {
return list;
}
/**
* 上拉加载更多数据插入
* @param list
*/
public void loadMoreInsertList( ArrayList<ListData> list ) {
this.list.addAll(list);
}
/**
* list中的数据
*/
public static class ListData{
private String notifyId = ""; /** 消息体id 用于获取消息详情*/
private String title = ""; /** 标题*/
private String content = ""; /** 内容*/
private String readFlag = ""; /** 用户阅读状态 0未读 1已读*/
private String sendTime = ""; /** 消息发送时间*/
private String showTime = ""; /** 消息发送时间 */
public String getTitle() {
return title;
}
public String getContent() {
return content;
}
public String getReadFlag() {
return readFlag;
}
public String getSendTime() {
return sendTime;
}
public String getShowTime() {
return showTime;
}
public String getNotifyId() {
return notifyId;
}
}
}
}
我对网络请求的理解就是会用,对底层理解不深入,所以就不讲解了😄
网友评论