一、网络请求
移动软件及APP实现主要在于本地功能交互的实现与数据的展示,且数据常为移动软件的核心。数据常源自于
服务器,网络数据交互则扮演十分重要的角色。
二、网络情形考量
网络请求在数据交互中扮演重要角色。因其流程的特殊性,存有多种情形需要考虑。
1,返回值情形
接口崩溃,返回异常情形;以及接口正确抛出异常的返回
接口返回内容为空,或者状态正常,可用数据部分为空;
接口正常返回数据,解析数据出现错误;
2,网络请求执行过程
执行开始前:提示网络请求正在执行,给予用户良好的反馈,屏蔽用户的其他操作【管控恶意点击,重复提交】
执行过程中:网络请求在特定条件下(返回,Home,黑屏,强制关机,电话、短信的插入),需要对网络请求做暂停、取消、重新开始等处理;
执行完成后:解析数据,重新渲染界面;尤其是网络请求错误时,需要数据的回显与数据的重置,并给与用户友好的提示。
针对上面情形,需要考虑的是:
(1)限定网络请求头,可以避免重复提交,也能实现接口的版本控制;
(2)网络请求最好实现队列效果,能够在合适的时间处理其中的某一个网络请求;
(3)实现线程池管理,不能无限开启线程,管控当前应用对系统的消耗;
(4)实现网络请求的数据压缩与加密,增强网络请求的安全性,减少数据量,增强信息传送、携带能力。
网络请求主要交互数据类型:
一般是字符串与图片文件的交互。随应用的扩展,扩展到表单、Map数据,文件以及数据流。
三、开源框架
现已经公开使用且使用效果较为良好的网络请求方式主要有:HttpClient、HttpURLConnection、AsyncTask、xUtlis、Volley、OkHttp、Retrofit、Android_Async_Http。从最初始的网络情求到应用框架实现封装。挑选实现进行管控。
HttpURLConnection:
public class NetWorkHttpConnection {Volley:
/**
* 超时时间
*/
private static final int TIMEOUT_IN_MILLIONS = 5000;
/**
* HttpURLConnection GET请求
* 访问路径中直接拼接完整参数
*
* @param urlStr 访问路径
* @return 返回内容
*/
public static String doGet(String urlStr) {
URL url = null;
HttpURLConnection conn = null;
InputStream is = null;
ByteArrayOutputStream baos = null;
try {
url = new URL(urlStr);
conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(TIMEOUT_IN_MILLIONS);
conn.setConnectTimeout(TIMEOUT_IN_MILLIONS);
conn.setRequestMethod("GET");
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("Content-type", "application/json");
if (conn.getResponseCode() == 200) {
is = conn.getInputStream();
baos = new ByteArrayOutputStream();
int len = -1;
byte[] buf = new byte[128];
while ((len = is.read(buf)) != -1) {
baos.write(buf, 0, len);
}
baos.flush();
return baos.toString();
} else {
throw new RuntimeException(" responseCode is not 200 ... ");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (is != null)
is.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (baos != null)
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
if (conn != null) {
conn.disconnect();
}
}
return null;
}
/**
* HttpURLConnection POST请求
*
* @param url 发送请求的 URL
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
* @return 所代表远程资源的响应结果
*/
public static String doPost(String url, String param) {
PrintWriter out = null;
BufferedReader in = null;
String result = "";
try {
URL realUrl = new URL(url);
// 打开和URL之间的连接
HttpURLConnection conn = (HttpURLConnection) realUrl
.openConnection();
// 设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type",
"application/json");
conn.setRequestProperty("charset", "utf-8");
conn.setUseCaches(false);
// 发送POST请求必须设置如下两行
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setReadTimeout(TIMEOUT_IN_MILLIONS);
conn.setConnectTimeout(TIMEOUT_IN_MILLIONS);
if (param != null && !param.trim().equals("")) {
// 获取URLConnection对象对应的输出流
out = new PrintWriter(conn.getOutputStream());
// 发送请求参数
out.print(param);
// flush输出流的缓冲
out.flush();
}
// 定义BufferedReader输入流来读取URL的响应
in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
e.printStackTrace();
}
// 使用finally块来关闭输出流、输入流
finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
return result;
}
}
**xUtlis:
* 使用Volley封装网络请求
* GET/POST都可以,在外部调用时传参一致
*
* @version V1.0
* @author: Vision
* @date: 2016-06-22 13:38
*/
public class NetWorkVolley {
public static RequestQueue mQueue = Volley.newRequestQueue(DemoApplicatipn.getApplication());
/**
* POST方法访问网络
*
* @param method 访问路径
* @param params 传递参数
* @param listener 正确返回值监听器
* @param errorListener 错误时监听器
*/
public static void netWorkByPost(String method, JSONObject params, Response.Listener<JSONObject> listener,
Response.ErrorListener errorListener) {
JsonObjectRequest request = null;
request = new JsonObjectRequest(Request.Method.POST, method, params,
listener, errorListener) {
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("User-Agent", "有代表意义的String串");
headers.put("content-type", "x-www-form-urlencoded");
headers.put("Accept-Encoding", "gzip");
headers.put("api-version", 1 + "");
return headers;
}
};
request.setRetryPolicy(new DefaultRetryPolicy(15000,//设置默认超时时间,15秒
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,//默认最大尝试次数 1
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
mQueue.add(request);
}
/**
* GET方法实现网络请求
*
* @param url 访问路径
* @param parames 传递参数
* @param listener 正确返回值监听器
* @param errorListener 错误时监听器
*/
public static void netWorkByGet(String url, Map<String, Object> parames,
Response.Listener<JSONObject> listener, Response.ErrorListener errorListener) {
/**
* 拼接参数
*/
String paramStr = "";
if (parames != null) {
for (String key : parames.keySet()) {
paramStr = paramStr + key + "=" + parames.get(key).toString() + "&";
}
url = url + "?" + paramStr;
}
/**
* 执行网络请求
*/
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, url, null, listener, errorListener) {
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("User-Agent", "有代表意义的String串");
headers.put("content-type", "x-www-form-urlencoded");
headers.put("Accept-Encoding", "gzip");
headers.put("api-version", 1 + "");
return headers;
}
};
request.setRetryPolicy(new DefaultRetryPolicy(15000,//设置默认超时时间,15秒
0,//默认最大尝试次数
1.0f));
mQueue.add(request);
}
}
/**Gzip实现类,调用GzipNetWork实现网络请求【数据压缩】
* @version V1.0
* @author: Vision
* @date: 2016-06-22 14:33
*/
public class NetWorkXUtils {
/**
* xutil请求网络数据POST
*
* @param url
* @param map
* @param requestCallBack
*/
public static void getJsonPostForXutil(String url, Map<String, Object> map, RequestCallBack<String> requestCallBack) {
HttpUtils httpUtils = new HttpUtils();
httpUtils.configTimeout(10000);
httpUtils.configSoTimeout(10000);
httpUtils.configCurrentHttpCacheExpiry(1000);
RequestParams params = new RequestParams();
for (Map.Entry<String, Object> entry : map.entrySet()) {
params.addBodyParameter(entry.getKey(), entry.getValue().toString());
}
httpUtils.send(HttpRequest.HttpMethod.POST, url, params, requestCallBack);
}
/**
* xutils请求网络数据GET
*
* @param url
* @param map
* @param requestCallBack
*/
public static void getJsonGetForXutil(String url, Map<String, Object> map, RequestCallBack<String> requestCallBack) {
HttpUtils httpUtils = new HttpUtils();
String getUrl = "";
if (map != null) {
for (Map.Entry<String, Object> entry : map.entrySet()) {
getUrl = getUrl + entry.getKey() + "=" + entry.getValue() + "&";
}
if (getUrl.length() > 1) {
getUrl.substring(0, getUrl.length() - 1);
}
}
httpUtils.configCurrentHttpCacheExpiry(1000);//防止连续点击的网络请求
httpUtils.send(HttpRequest.HttpMethod.GET, url + getUrl, requestCallBack);
}
}
public class GZipRequest extends StringRequest { /** * 使用当前构造方法,没有参数体,不能使用POST请求 * 要使用post,需要重写获取参数方法 getParams() * * @param method * @param url * @param listener * @param errorListener */ public GZipRequest(int method, String url, Response.Listener<String> listener, Response.ErrorListener errorListener) { super(method, url, listener, errorListener); } /** * 默认Get请求 * * @param url * @param listener * @param errorListener */ public GZipRequest(String url, Response.Listener<String> listener, Response.ErrorListener errorListener) { super(url, listener, errorListener); } // parse the gzip response using a GZIPInputStream @Override protected Response<String> parseNetworkResponse(NetworkResponse response) { /** * 先判断数据是否为Gzip * 【使用gzip模式访问数据,不压缩的数据格式不接受】 * * Content-Encoding 数据返回格式是否压缩的关键判断字段 */ Map<String, String> headers = response.headers; if (headers != null && headers.containsKey("Content-Encoding") && headers.get("Content-Encoding").contains("gzip")) { String output = ""; // note: better to use StringBuilder try { final GZIPInputStream gStream = new GZIPInputStream(new ByteArrayInputStream(response.data)); final InputStreamReader reader = new InputStreamReader(gStream); final BufferedReader in = new BufferedReader(reader); String read; while ((read = in.readLine()) != null) { output += read; } reader.close(); in.close(); gStream.close(); } catch (IOException e) { return Response.error(new ParseError()); } return Response.success(output, HttpHeaderParser.parseCacheHeaders(response)); } else { String output = null; try { output = new String(response.data, "utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return Response.success(output, HttpHeaderParser.parseCacheHeaders(response)); } } @Override public Map<String, String> getHeaders() throws AuthFailureError { HashMap<String, String> headers = new HashMap<String, String>(); headers.put("User-Agent", "有意义的String"); headers.put("content-type", "application/x-www-form-urlencoded"); headers.put("Accept-Encoding", "gzip"); return headers; }}
public class GzipNetWork { public static RequestQueue mQueue = Volley.newRequestQueue(DemoApplicatipn.getApplication()); /** * 使用Gzip的GET网络请求 * * @param url 网络请求不带参数的链接地址 * @param params 网络请求参数 * @param listener 网络请求正确返回值监听器 * @param errorListener 网络请求错误返回值监听器 */ public static void netGzipWorkByGet(String url, Map<String, String> params, Response.Listener<String> listener, Response.ErrorListener errorListener) { String paramStr = ""; if (params != null) { for (String key : params.keySet()) { paramStr = paramStr + key + "=" + params.get(key) + "&"; } url = url + "?" + paramStr; } GZipRequest gZipRequest = new GZipRequest(url, listener, errorListener); mQueue.add(gZipRequest); } /** * 使用Gzip的POST网络请求 * * @param url 网络请求不带参数的链接地址 * @param params 网络请求参数 * @param listener 网络请求正确返回值监听器 * @param errorListener 网络请求错误返回值监听器 */ public static void netGzipWorkByPost(String url, final Map<String, String> params, Response.Listener<String> listener, Response.ErrorListener errorListener) { GZipRequest gZipRequest = new GZipRequest(Request.Method.POST, url, listener, errorListener) { @Override protected Map<String, String> getParams() throws AuthFailureError { return params; } }; mQueue.add(gZipRequest); }}在实际使用均调用NetWork类中的方法就可以,GET/POST方法都已实现,调用参数形式一致。主要是GET实现在链接后直接拼接参数,能够从外部看到,不安全且传输数据量有限;POST请求有自己的请求体,不在链接中展示,安全性较高,且数据内容长度没有限制。
四、相互关系
HttpURLConnection的实现中,传参较为麻烦,只是基本功能的实现,也没有效率方面的考虑;
xUtlis特长在于四大模块:Dbutlis/ViewUtlis/HttpUtlis/BitmapUtlis。
Volley实现队列访问,是一个相对比较成熟的框架。
Volley+Gzip则实现了网络数据的压缩。