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的几种用法