Android基础学习【历史流程重走】 ---- 网络请求(四)

时间:2022-01-05 20:01:25

一、网络请求

移动软件及APP实现主要在于本地功能交互的实现与数据的展示,且数据常为移动软件的核心。数据常源自于

服务器,网络数据交互则扮演十分重要的角色。


二、网络情形考量

网络请求在数据交互中扮演重要角色。因其流程的特殊性,存有多种情形需要考虑。

1,返回值情形

接口崩溃,返回异常情形;以及接口正确抛出异常的返回

接口返回内容为空,或者状态正常,可用数据部分为空;

接口正常返回数据,解析数据出现错误;

2,网络请求执行过程

执行开始前:提示网络请求正在执行,给予用户良好的反馈,屏蔽用户的其他操作【管控恶意点击,重复提交】

执行过程中:网络请求在特定条件下(返回,Home,黑屏,强制关机,电话、短信的插入),需要对网络请求做暂停、取消、重新开始等处理;

执行完成后:解析数据,重新渲染界面;尤其是网络请求错误时,需要数据的回显与数据的重置,并给与用户友好的提示。


针对上面情形,需要考虑的是:

(1)限定网络请求头,可以避免重复提交,也能实现接口的版本控制;

(2)网络请求最好实现队列效果,能够在合适的时间处理其中的某一个网络请求;

(3)实现线程池管理,不能无限开启线程,管控当前应用对系统的消耗;

(4)实现网络请求的数据压缩与加密,增强网络请求的安全性,减少数据量,增强信息传送、携带能力。

网络请求主要交互数据类型:

一般是字符串与图片文件的交互。随应用的扩展,扩展到表单、Map数据,文件以及数据流。


三、开源框架

现已经公开使用且使用效果较为良好的网络请求方式主要有:HttpClient、HttpURLConnection、AsyncTask、xUtlis、Volley、OkHttp、Retrofit、Android_Async_Http。从最初始的网络情求到应用框架实现封装。挑选实现进行管控。

HttpURLConnection:

public class NetWorkHttpConnection {
/**
* 超时时间
*/
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;
}
}
Volley:

**
* 使用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);
}
}
xUtlis:

/**
* @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);
}
}
Gzip实现类,调用GzipNetWork实现网络请求【数据压缩】
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则实现了网络数据的压缩。

点击这里有惊喜$_$

曾记许,旧时处,佩玉鸣箫罢歌舞;剑如初,心如故,三千弱水繁华路   \(^o^)/~