转载请标明出处:
http://blog.csdn.net/xuehuayous/article/details/54143487
本文出自:【Kevin.zhou的博客】
前言:转眼间2017了,去年给自己定的一周一篇博客被拖成了一月一篇,后来忙着找工作也荒废了。竟然还被CSDN博客之星提名,又点燃了撸博客的激情。
作为一个android codder,网略访问基本上在项目中都会用到,常用的获取JSON数据、下载文件(图片等)、上传数据、上传问文件等等。Android原生的访问网络有HttpClient
和HttpURLConnection
。在Android 6.0时删除了HttpClient
,OkHttp
的呼声也随之高涨。
下面会对这几种网络访问模式写下简单的示例,网上大多示例都是访问别人的服务器或者本地的服务器,不便于大家测试,我用阿里云搭建了个简单的服务端。
吐下槽,之前使用的MyEclipse
开发Java Web
,换电脑之后在网上搜怎么配Mac
上Java Web
开发环境,有很多人提到使用IntelliJ IDEA
,搞环境又搞了一两天,本人后端小菜~
NetworkTask编写
这里我们使用AsyncTask
的方式进行子线程访问网络并回调到主线程,在创建NetworkTask
的时候要传入是GET
还是POST
请求,然后传入参数开始访问网略获取数据。
1. 这里我们约定`GET`请求时参数即为拼接的请求地址,如:
new xxxNetworkTask(NetworkTask.GET).execute("http://123.57.31.11/androidnet/getJoke?id=5");
2. 约定`POST`请求时参数第一个为地址,其他为请求参数对,如:
new xxxNetworkTask(NetworkTask.POST).execute("http://123.57.31.11/androidnet/getJoke", "id=5");
/**
* 访问网络AsyncTask基类,访问网络在子线程进行并返回主线程通知访问的结果
*/
public abstract class NetworkTask extends AsyncTask<String, Integer, String> {
public static final String TAG = "NetworkTask";
public static String GET = "GET";
public static String POST = "POST";
private String mRequestMethod;
public NetworkTask(String method) {
this.mRequestMethod = method;
}
@Override
protected String doInBackground(String... params) {
String data;
if(GET.equals(mRequestMethod)) {
data = doGet(params[0]);
} else if(POST.equals(mRequestMethod)) {
data = doPost(params);
} else {
throw new RuntimeException("Request mode can only be GET or POST!");
}
return data;
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
}
/**
* 以GET的方式访问网络
*
* @param url
* @return 返回的字符串数据
*/
public abstract String doGet(String url);
/**
* 以POST的方式访问网络
*
* @param params
* @return 返回的字符串数据
*/
public abstract String doPost(String[] params);
}
然后我们加上返回数据的监听回调
/**
* 访问网络AsyncTask基类,访问网络在子线程进行并返回主线程通知访问的结果
*/
public abstract class NetworkTask extends AsyncTask<String, Integer, String> {
public static final String TAG = "NetworkTask";
public static String GET = "GET";
public static String POST = "POST";
// 访问方式,只能是GET或POST
private String mRequestMethod;
// 是否访问网络成功
protected boolean isSuccess = true;
// 监听回调
private ResponceLintener mResponceLintener;
public NetworkTask(String method) {
this.mRequestMethod = method;
}
@Override
protected String doInBackground(String... params) {
String data;
String url = params[0];
if(GET.equals(mRequestMethod)) {
data = doGet(url);
} else if(POST.equals(mRequestMethod)) {
Map<String, String> paramMap = new HashMap<String, String>();
// 第一个参数为访问的接口,不为body参数
for (int i = 1; i < params.length; i++) {
String[] value = new String[2];
if (params[i].split("=").length == 1) {
value[0] = params[i].split("=")[0];
value[1] = "";
} else {
value = params[i].split("=");
}
paramMap.put(value[0], value[1]);
}
data = doPost(url, paramMap);
} else {
throw new RuntimeException("Request mode can only be GET or POST!");
}
return data;
}
@Override
protected void onPostExecute(String result) {
if(null != mResponceLintener) {
if(isSuccess) {
mResponceLintener.onSuccess(result);
} else {
mResponceLintener.onError(result);
}
}
}
/**
* 以GET的方式访问网络
*
* @param url
* @return 返回的字符串数据
*/
public abstract String doGet(String url);
/**
* 以POST的方式访问网络
*
* @param url
* @param paramMap
* @return 返回的字符串数据
*/
public abstract String doPost(String url, Map<String, String> paramMap);
public void setResponceLintener(ResponceLintener l) {
this.mResponceLintener = l;
}
public interface ResponceLintener {
/**
* 成功的监听回调
* @param result
*/
void onSuccess(String result);
/**
* 失败的监听回调
* @param error
*/
void onError(String error);
}
}
OK,经过简单的封装之后,我们在使用不同的形式访问网络时只需要对应的去实现doGet
和doPost
就可以了。
HttpClient
尽管
HttpClient
删除了,我们还是简单回顾下它的使用。
HttpClientNetworkTask
创建HttpClientNetworkTask
继承NetworkTask
并实现抽象方法。
/**
* 以HttpClient访问网络AsyncTask,访问网络在子线程进行并返回主线程通知访问的结果
*/
public class HttpClientNetworkTask extends NetworkTask {
public HttpClientNetworkTask(String method) {
super(method);
}
@Override
public String doGet(String url) {
return null;
}
@Override
public String doPost(String url, Map<String, String> paramMap) {
return null;
}
}
下面我们只需要使用HttpClient
去完成响应的网络请求就可以啦~ 是不是很清爽~~
HttpClient使用步骤:
- 创建
HttpClient
对象; - 创建请求的对象,如果为
GET
请求,则创建HttpGet
对象,如果为POST
请求,则创建HttpPost
对象; - 调用
HttpClient
对象的execute
发送请求,执行该方法后,将获得服务器返回的HttpResponse
对象; - 检查相应状态是否正常;
- 获得相应对象当中的数据。
GET
@Override
public String doGet(String url) {
String result = null;
HttpClient httpCient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
try {
HttpResponse httpResponse = httpCient.execute(httpGet);
if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
HttpEntity entity = httpResponse.getEntity();
result = EntityUtils.toString(entity,"utf-8");
} else {
isSuccess = false;
result = "网络响应状态码不为200!";
}
} catch (IOException e) {
isSuccess = false;
result = "网络访问错误:" + e.getMessage();
}
return result;
}
使用如下:
NetworkTask networkTask = new HttpClientNetworkTask(NetworkTask.GET);
networkTask.execute("http://123.57.31.11/androidnet/getJoke?id=5");
networkTask.setResponceLintener(new NetworkTask.ResponceLintener() {
@Override
public void onSuccess(String result) {
tv.setText(result);
}
@Override
public void onError(String error) {
Toast.makeText(getApplicationContext(), error, Toast.LENGTH_SHORT).show();
}
});
据说没图不便于理解,上图:
POST
@Override
public String doPost(String url, Map<String, String> paramMap) {
String result;
HttpClient httpCient = new DefaultHttpClient();
HttpPost httpRequest = new HttpPost(url);
// 使用NameValuePair来保存要传递的Post参数
List<NameValuePair> params = new ArrayList<NameValuePair>();
for (Map.Entry<String, String> entry : paramMap.entrySet()) {
params.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
try {
httpRequest.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
HttpResponse response = httpCient.execute(httpRequest);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
result = EntityUtils.toString(response.getEntity());
} else {
isSuccess = false;
result = "网络响应状态码不为200!";
}
} catch (IOException e) {
isSuccess = false;
result = "网络访问错误:" + e.getMessage();
}
return result;
}
使用如下:
NetworkTask networkTask = new HttpClientNetworkTask(NetworkTask.POST);
networkTask.execute("http://123.57.31.11/androidnet/getJoke", "id=5");
networkTask.setResponceLintener(new NetworkTask.ResponceLintener() {
@Override
public void onSuccess(String result) {
tv.setText(result);
}
@Override
public void onError(String error) {
Toast.makeText(getApplicationContext(), error, Toast.LENGTH_SHORT).show();
}
});
HttpURLConnection
HttpURLConnectionNetworkTask
创建HttpURLConnectionNetworkTask
继承NetworkTask
并实现抽象方法。
/**
* 以HttpURLConnection访问网络AsyncTask,访问网络在子线程进行并返回主线程通知访问的结果
*/
public class HttpURLConnectionNetworkTask extends NetworkTask {
public HttpURLConnectionNetworkTask(String method) {
super(method);
}
@Override
public String doGet(String url) {
return null;
}
@Override
public String doPost(String url, Map<String, String> paramMap) {
return null;
}
}
下面我们用HttpURLConnection
去完成响应的网络。
HttpURLConnection使用步骤:
- 实例化URL对象;
- 实例化HttpUrlConnection对象;
- 设置请求连接属性,传递参数等;
- 获取返回码判断是否链接成功;
- 读取输入流;
- 关闭链接。
GET
@Override
public String doGet(String httpUrl) {
String result;
try {
URL url = new URL(httpUrl);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setReadTimeout(5000);
urlConnection.setConnectTimeout(5000);
urlConnection.setRequestProperty("Charset", "UTF-8");
if (urlConnection.getResponseCode() == 200) {
InputStream is = urlConnection.getInputStream();
result = readFromStream(is);
} else {
isSuccess = false;
result = "网络响应状态码不为200!";
}
} catch(IOException e) {
isSuccess = false;
result = "网络访问错误:" + e.getMessage();
}
return result;
}
这里编写了一个从流获取字符串的工具
/**
* 输入流获取字符串
*
* @param is 输入流
* @return String 返回的字符串
* @throws IOException
*/
public String readFromStream(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
is.close();
String result = baos.toString();
baos.close();
return result;
}
使用方法和HttpClientNetworkTask
一样,只不过是把实现类由HttpClientNetworkTask
换为HttpURLConnection
:
NetworkTask networkTask = new HttpURLConnectionNetworkTask(NetworkTask.GET);
networkTask.execute("http://123.57.31.11/androidnet/getJoke?id=6");
networkTask.setResponceLintener(new NetworkTask.ResponceLintener() {
@Override
public void onSuccess(String result) {
tv.setText(result);
}
@Override
public void onError(String error) {
Toast.makeText(getApplicationContext(), error, Toast.LENGTH_SHORT).show();
}
});
POST
@Override
public String doPost(String httpUrl, Map<String, String> paramMap) {
String result;
try {
URL url = new URL(httpUrl);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setReadTimeout(5000);
urlConnection.setConnectTimeout(5000);
// 配置连接的Content-type
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
urlConnection.setDoOutput(true); // 发送POST请求必须设置允许输出
urlConnection.setDoInput(true); // 发送POST请求必须设置允许输入
String data = "";
boolean firstParam = true;
for (Map.Entry<String, String> entry : paramMap.entrySet()) {
if(firstParam) {
data += entry.getKey() + "=" + URLEncoder.encode(entry.getValue(), "UTF-8");
firstParam = false;
} else {
data += "&" + entry.getKey() + "=" + URLEncoder.encode(entry.getValue(), "UTF-8");
}
}
OutputStream os = urlConnection.getOutputStream();
os.write(data.getBytes());
os.flush();
if (urlConnection.getResponseCode() == 200) {
InputStream is = urlConnection.getInputStream();
result = readFromStream(is);
} else {
isSuccess = false;
result = "网络响应状态码不为200!";
}
} catch (IOException e) {
isSuccess = false;
result = "网络访问错误:" + e.getMessage();
}
return result;
}
使用方法和HttpClientNetworkTask
一样,只不过是把实现类由HttpClientNetworkTask
换为HttpURLConnection
:
NetworkTask networkTask = new HttpURLConnectionNetworkTask(NetworkTask.POST);
networkTask.execute("http://123.57.31.11/androidnet/getJoke", "id=6");
networkTask.setResponceLintener(new NetworkTask.ResponceLintener() {
@Override
public void onSuccess(String result) {
tv.setText(result);
}
@Override
public void onError(String error) {
Toast.makeText(getApplicationContext(), error, Toast.LENGTH_SHORT).show();
}
});
总结
其实开发的时候我们不是很关心具体是怎么联网的,封装好之后使用起来越简单越好。我给你地址和参数你去访问网略然后返回我关心的数据就可以了。
以上是使用HttpClient
和HttpURLConnection
进行了GET
和POST
的返回数据为字符串类型简单封装,有兴趣的朋友也可以进行OkHttp
或其他方式的封装。
当然在使用的时候可能会有更加复杂的场景,比如:上传一个图片、下载一个文件等。
我们最常使用的还是获取JSON类型的数据,然后封装到实体。那么这一块如何封装呢?接下来的一篇博客将抛开NetworkTask
进行获取JSON数据并封装到实体的简单编写。