在android客户端开发中,使用http协议向服务器交换数据时最平常的,SDK中自带的HttpURLConnection虽然能基本满足需求,但是日常使用中基本不会使用,andriod开发的思路就是坚决不造轮子,追求使用最好的轮子。所以square公司实现了一个HTTP客户端的类库——Okhttp 。
首先我们的导入相关依赖:
implementation ‘com.squareup.okhttp3:okhttp:3.3.1’
核心类
基本上就用四个类:OkHttpClient、Request、Call 和 Response。下面我们分析一下这几个类
OkHttpClient
直接new OkHttpClient()出对象,最好将其作为全局的实例进行保存,从而在App的各处都只使用这一个实例对象,这样所有的HTTP请求都可以共用Response缓存、共用线程池以及共用连接池。
public class MainApp extends Application {
@Override
public void onCreate() {
super.onCreate();
}
//通过内部类实现单例模式
static class OkHttpHolder {
private static final OkHttpClient CLIENT = new OkHttpClient();
}
public static OkHttpClient getClient() {
return OkHttpHolder.CLIENT;
}
}
需要使用的时候,直接用MainApp.getClient()
调用,而且可以保证全局唯一。
接下来需要给这个client配置一些网络必要的参数如访问的代理、协议、DNS、连接超时时长。这里使用了典型的建造者模式,方便用户根据需求搭建client配置。
OkHttpClient client = new OkHttpClient.Builder()
.dns(dns)
.protocols(protocols)
.proxy(proxy)
.readTimeout(30, TimeUnit.SECONDS)
.build();
当然在实际中我们使用默认参数就好了。
Request
Request类封装了请求报文信息:请求的Url地址、请求的方法(如GET、POST等)、各种请求头(如Content-Type、Cookie)以及可选的请求体。同样他也是使用建造者模式进行链式创建的。
Request request = new Request.Builder()
.url("http://www.baidu.com")
.build();
Call
Call代表了一个实际的HTTP请求,它是连接Request和Response的桥梁,通过Request对象的newCall()方法可以得到一个Call对象。Call对象既支持同步获取数据,也可以异步获取数据。对此他有两个方法:
- execute():同步获取,它是会阻塞线程,因此调用它一定要新开一个子线程,否则会报
android.os.NetworkOnMainThreadException
,其实程序试图在其主线程上执行耗时长的任务时(如网络操作)都可能会报错。该方法返回一个Response对象。 - enqueue():异步获取,它不会阻塞线程,它是通过事件回调机制来实现对任务完成与否的后续操作。
Response
Response类封装了响应报文信息:状态吗(200、404等)、响应头(Content-Type、Server等)以及可选的响应体。可以通过Call对象的execute()方法获得Response对象,异步回调执行Callback对象的onResponse方法时也可以获取Response对象。
下面在一个activity中进行一个http请求:
public class MainActivity extends AppCompatActivity {
private OkHttpClient mClient=null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mClient = MainApp.getClient();
//新开一个线程,使用excute()方法
new Thread(new Runnable() {
@Override
public void run() {
Request request = new Request.Builder()
.url("http://www.baodui.com")
.build();
Response response = null;
try {
response = mClient.newCall(request).execute();
} catch (IOException e) {
e.printStackTrace();
}
if (!response.isSuccessful())
return;
try {
System.out.println(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
//通过异步方法enqueue()
private void run(){
Request request = new Request.Builder()
.url("http://www.baidu.com")
.build();
mClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (!response.isSuccessful())
return;
System.out.println(response.body().string());
}
});
}
}
注意
这里有几点需要注意:
- 在AndroidManifest.xml中需要在
<application>
中添加android:name=".MainApp"
- 在AndroidManifest.xml中打开网络权限
<uses-permission android:name="android.permission.INTERNET"/>
- android9之后限制使用http协议直接进行网络请求,我的解决方法是在res下新建一个xml文件夹,然后添加一个叫做
netconfig.xml
文件,内容如下:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
最后在AndroidManifest.xml中的<application>
中添加android:networkSecurityConfig="@xml/netconfig"
即可