HTTP是现代应用常用的一种交换数据和媒体的网络方式,高效地使用HTTP能让资源加载更快,节省带宽。OkHttpClient是一个高效的Http客户端,一般可以用它来请求第三方接口。它的请求/响应 API 使用构造器模式builders来设计,支持阻塞式的同步请求和带回调的异步请求。
官方地址 https://github.com/square/okhttp
1.1 同步GET请求
操作步骤:
(1) 通过new OkHttpClient()构造OkHttpClient对象;
(2) 构造Request对象;
(3) 通过前两步构造的OkHttpClient对象和Request对象调用newCall()方法构造Call对象;
(4) 通过Call对象的execute()方法来提交同步请求。
注意,同步GET请求的方式会阻塞调用线程,所以在Android中应放在子线程中执行,否则有可能引起ANR异常,Android3.0 以后已经不允许在主线程访问网络。
代码示例如下:
String url = "http://wwww.baidu.com";
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder().url(url)
.get()//默认就是GET请求,可以不写
.build();
final Call call = okHttpClient.newCall(request);
new Thread(new Runnable() {
@Override
public void run() {
try {
Response response = call.execute();
Log.d(TAG, "run: " + response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
1.2 异步GET请求
异步GET请求的请求步骤与同步GET请求只有最后一步不同。异步发起的请求会被加入到 Dispatcher 中的 runningAsyncCalls双端队列中通过线程池来执行,不会阻塞调用线程。
操作步骤:
(1) 通过new OkHttpClient()构造OkHttpClient对象;
(2) 构造Request对象;
(3) 通过前两步构造的OkHttpClient对象和Request对象调用newCall()方法构造Call对象;
(4) 通过Call对象的enqueue(new Callback())方法来提交异步请求。
代码示例如下:
String url = "http://wwww.baidu.com";
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
.url(url)
.get()//默认就是GET请求,可以不写
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d(TAG, "onFailure: ");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d(TAG, "onResponse: " + response.body().string());
}
});
2.1 POST方式提交Sting
这种方式与前面的区别就是在构造Request对象时,需要多构造一个RequestBody对象,用它来携带我们要提交的数据。在构造 RequestBody 需要指定MediaType,用于描述请求/响应 body 的内容类型,关于 MediaType 的更多信息可以查看 RFC 2045,RequstBody的几种构造方式:
ResponseBody creat(MediaType contentType,File file)
ResponseBody creat(MediaType contentType,byte[] content)
ResponseBody creat(MediaType contentType,String content)
ResponseBody creat(MediaType contentType,ByteString content)
ResponseBody creat(MediaType contentType,byte[] content, int offset,int byteCounts)
代码示例:
MediaType mediaType = MediaType.parse("text/x-markdown; charset=utf-8");
String requestBody = "I am Jdqm.";
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(RequestBody.create(mediaType, requestBody))
.build();
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d(TAG, "onFailure: " + e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d(TAG, response.protocol() + " " +response.code() + " " + response.message());
Headers headers = response.headers();
for (int i = 0; i < headers.size(); i++) {
Log.d(TAG, headers.name(i) + ":" + headers.value(i));
}
Log.d(TAG, "onResponse: " + response.body().string());
}
});
除此之外,POST方式还可以提交多种不同格式,如流、文件、表单等。详见:
OkhttpClient的使用详解
下面写一个用于网络请求的工具类HttpUtils:
首先添加maven依赖:
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.4.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
网络请求工具类:
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Objects;
public class HttpUtils {
private static Logger LOGGER = LoggerFactory.getLogger(HttpUtils.class);
private static class HttpClientSingletonFactory {
private static final OkHttpClient okHttpClientInstance = new OkHttpClient();
public static OkHttpClient getOkHttpClientInstance() {
return okHttpClientInstance;
}
}
public static String post(String url, String json, MediaType mediaType) {
try {
RequestBody body = RequestBody.create(json, mediaType);
Request request = (new Request.Builder()).url(url).post(body).build();
return Objects.requireNonNull(HttpClientSingletonFactory.getOkHttpClientInstance().newCall(request).execute().body()).string();
} catch (Exception e) {
LoggerUtils.error(e, LOGGER, "HttpUtils post error, url={0}, params={1}, mediaType={2}", url, json, mediaType.toString());
return null;
}
}
public static String get(String url) {
Request request = (new Request.Builder()).url(url).build();
try {
return Objects.requireNonNull(HttpClientSingletonFactory.getOkHttpClientInstance().newCall(request).execute().body()).string();
} catch (Exception e) {
LoggerUtils.error(e, LOGGER, "HttpUtils get error, url={0}, params={1}, mediaType={2}", url);
return null;
}
}
}
接下来假设要调用高德搜索服务API接口查询一个经纬度坐标点附近设定的范围内的POI信息,高德搜索服务API接口介绍与使用文档详见搜索POI。
假设现在使用周边搜索,参考文档可以知道,周边搜索API服务地址URL为https://restapi.amap.com/v3/place/around?parameters ,其中parameters即为组合的请求参数,构造完URL地址后,调用get方法即可。
示例:
//高德地图的接口地址
private static final String AMAP_URL = "https://restapi.amap.com/";
Map<String, String> params = new HashMap<>();
params.put("keywords", request.getName());
params.put("location", request.getLongitude() + "," + request.getLatitude());
//医疗保健服务分类 参考POI分类编码下载 https://lbs.amap.com/api/webservice/download
params.put("types", "090000");
params.put("radius", "5000");
params.put("page", "1");
params.put("offset", "20");
params.put("extensions", "all");
StringBuilder urlParams = new StringBuilder("key=ff8fe7625c889a214da630dd0e0b8c5a");
params.forEach((key, value) -> urlParams.append("&").append(key).append("=").append(value));
return HttpUtils.get(AMAP_URL + "v3/place/around" + urlParams);
注意此时返回的是String类型的数据,但是实际上看文档我们可以知道,实际里面是很多的JSON数据,因此还需要调用com.alibaba.fastjson包下JSON相关的方法将其转化为JSON数据。详细可以参考FastJson对于JSON格式字符串、JSON对象及JavaBean之间的相互转换、JSON.parseObject的几种用法。