这么多网络框架为什么使用OkHttp?因为不仅在接口封装上做的简单易用,在底层实现上也上自成一派,比起原声的HttpURLConnection,可以说上有过之而无不及,现在已经成了广大Android开发者首选的网络通信库。
OkHttp Github 主页:https://github.com/square/okhttp
使用OkHttp学习常见的网络请求
- GET请求
- 普通POST form请求
Content- Type: application/x-www-form-urlencoded - 支持文件上传的POST form请求
Content-Type: multipart/form-data; boundary= - POST JSON字符串
Content-Type: text/html; charset=UTF-8
Android网络请求注意事项
- 使用HTTP协议的URL 从Android P开始,默认不再允许直接访问HTTP请求;
- 通过设置Network Security Configuration支持;
网络请求测试接口
http://www.imooc.com/api/okhttp/getmethod
http://www.imooc.com/api/okhttp/postmethod
http://www.imooc.com/api/okhttp/postjson
一、GET请求
1.使用前的配置
首先在app/build.gradle中添加依赖库SDK最低版本为21。(进入项目主页可以找到最新版本)
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation("com.squareup.okhttp3:okhttp:4.9.0")
}
添加网络安全配置,在res目录下新建xml文件写入代码。以 Android 6.0(API 级别 23)及更低版本为目标平台的应用的默认配置如下所示。
具体可以参考:Android开发者文档网络安全配置
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
</network-security-config>
在AndroidManifest.xml文件中声明权限
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:networkSecurityConfig="@xml/network_security_config">
</application>
如果不使用network_security_config,则会出现
2.同步请求
- 创建请求: Request.Builder() -> Request对象
- 通过Request得到Call对象:client.newCall(request) -> Call对象
- 执行Call :同步call.execute(),异步call.enqueue()
- 得到Response对象
新建一个OkHttpUtils类,使用单例模式
public class OkHttpUtils {
private OkHttpClient okHttpClient;
//设置单例模式
private OkHttpUtils(){}
private static OkHttpUtils sInstance = new OkHttpUtils();
public static OkHttpUtils getInstance() {
return sInstance;
}
}
OkHttpUtils类中创建一个方法
//url接口 http://www.imooc.com/api/okhttp/getmethod?username=David
//需要自己开一个线程,或者使用handler处理请求到的数据
public String doGetBySync(String url) {
try {
//1.拿到OkHttpClient对象
OkHttpClient client = new OkHttpClient();
//2.构造Request对象
Request request = new Request.Builder()
.url(url)
.build();
//3.将Request封装为Call
Call call = client.newCall(request);
//4.调用同步请求方法
Response response = call.execute();
//返回应答
return response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
MainActivity中声明全局变量
//传入将主线程中的Looper,也就是说handleMessage会运行在主线程中
private Handler handler = new Handler();
使用按键调用该方法 doGetBySync();
new Thread() {
@Override
public void run() {
String content = OkHttpUtils.getInstance()
.doGetBySync("http://www.imooc.com/api/okhttp/getmethod?username=David&password=123");
//子线程不能处理UI,使用Handler的Post
handler.post(new Runnable() {
@Override
public void run() {
//将收到的内容现实在TextView上
mTvContent.setText(content);
}
});
}
}.start();
3.异步请求
首先创建一个接口 INetCallBack
public interface INetCallBack {
void onFailed(Throwable ex);
void onSuccess(String response);
}
OkHttpUtils类中声明全局变量
//传入主线程Looper
private final Handler mHandler = new Handler(Looper.getMainLooper());
OkHttpUtils类中创建一个方法
public void doGetByAsync(String url, INetCallBack callBack) {
//1.拿到OkHttpClient对象
OkHttpClient client = new OkHttpClient();
//2.构造Request对象
Request request = new Request.Builder()
.url(url)
.build();
//3.将Request封装为Call
Call call = client.newCall(request);
//4.调用异步请求方法,重写回调方法
call.enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
//处理UI线程
mHandler.post(new Runnable() {
@Override
public void run() {
//调用回调方法
callBack.onFailed(e);
}
});
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
String str = null;
try {
str = response.body().string();
} catch (IOException e) {
mHandler.post(new Runnable() {
@Override
public void run() {
callBack.onFailed(e);
}
});
return;
}
final String rspStr = str;
mHandler.post(new Runnable() {
@Override
public void run() {
callBack.onSuccess(rspStr);
}
});
}
});
}
使用按键调用该方法 doGetByAsync();
OkHttpUtils.getInstance().doGetByAsync(
"http://www.imooc.com/api/okhttp/getmethod?username=David&password=123",
new INetCallBack() {
@Override
public void onFailed(Throwable ex) {
//写回调方法
Toast.makeText(MainActivity.this, "获取数据失败", Toast.LENGTH_SHORT).show();
}
@Override
public void onSuccess(String response) {
mTvContent.setText(response);
}
}
);
最终运行结果
二、POST请求
1.使用Logging Interceptor打印信息
参考文章:Logging Interceptor
添加依赖库
dependencies {
implementation("com.squareup.okhttp3:logging-interceptor:4.9.0")
}
在构造函数中创建LoggingInterceptor对象
private OkHttpUtils(){
//LoggingInterceptor配置
HttpLoggingInterceptor logging = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(@NotNull String s) {
Log.d("Interceptor", s);
}
});
//设置log等级
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
client = new OkHttpClient.Builder()
//.connectTimeout()
.addInterceptor(logging)//添加拦截器,打印请求结果
.build();
}
这样每次使用OkHttp的请求时就可以查看Log的信息了
运行结果
D/Interceptor: --> GET http://www.imooc.com/api/okhttp/getmethod?username=David&password=123
D/Interceptor: --> END GET
D/Interceptor: <-- 200 OK http://www.imooc.com/api/okhttp/getmethod?username=David&password=123 (134ms)
D/Interceptor: Server: openresty
D/Interceptor: Date: Thu, 03 Dec 2020 23:46:40 GMT
D/Interceptor: Content-Type: text/html; charset=UTF-8
D/Interceptor: Transfer-Encoding: chunked
D/Interceptor: Connection: keep-alive
D/Interceptor: Vary: Accept-Encoding
D/Interceptor: Access-Control-Allow-Origin: *
D/Interceptor: X-Varnish: 1048639445
D/Interceptor: Age: 0
D/Interceptor: Via: 1.1 varnish (Varnish/6.0)
D/Interceptor: X-Cache: MISS from CS42
D/Interceptor: Accept-Ranges: bytes
D/Interceptor: {"errorCode":1,"data":{"ip":"42.80.244.194","headers":{"X-Varnish":"1048639446","X-Forwarded-For":"...}
D/Interceptor: <-- END HTTP (290-byte body)
2.POST 普通 form
OkHttp进行Post请求提交键值对
public void doPost(String url, HashMap<String, String> headers,
HashMap<String, String> params, INetCallBack callBack) {
//1.构建FormBody,传入参数
//FormBody formBody = new FormBody.Builder()
//.add("username", "admin")
//.add("password", "admin")
//.build();
FormBody.Builder formBodyBuilder = new FormBody.Builder();
if (params != null) {
for (String param : params.keySet()) {
formBodyBuilder.add(param, params.get(param));
}
}
//2.添加头部信息
Request.Builder requestBuilder = new Request.Builder();
addHeader(requestBuilder, headers);
//3.构建Request,将FormBody作为Post方法的参数传入
Request request = requestBuilder
.url(url)
.post(formBodyBuilder.build())
.build();
//4.调用请求,重写回调方法
executeRequest(callBack, request);
}
private void addHeader(Request.Builder requestBuilder, HashMap<String, String> headers) {
//requestBuilder = new Request.Builder();
if (headers != null) {
for (String key : headers.keySet()) {
requestBuilder.addHeader(key, headers.get(key));
}
}
}
private void executeRequest(INetCallBack callBack, Request request) {
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
mHandler.post(new Runnable() {
@Override
public void run() {
callBack.onFailed(e);
}
});
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
String respStr = null;
try {
respStr = response.body().string();
} catch (IOException e) {
mHandler.post(new Runnable() {
@Override
public void run() {
callBack.onFailed(e);
}
});
return;
}
String finalRespStr = respStr;
mHandler.post(new Runnable() {
@Override
public void run() {
callBack.onSuccess(finalRespStr);
}
});
}
});
}
执行代码
HashMap<String, String> params = new HashMap<>();
params.put("username", "David");
params.put("password", "123");
HashMap<String, String> headers = new HashMap<>();
headers.put("author_header","David");
OkHttpUtils.getInstance().doPostMultiPart("http://www.imooc.com/api/okhttp/postmethod",
params,
headers,
new INetCallBack() {
@Override
public void onFailed(Throwable ex) {
Toast.makeText(MainActivity.this, "网络发生错误", Toast.LENGTH_SHORT).show();
}
@Override
public void onSuccess(String response) {
mTvContent.setText(response);
}
}
);
运行结果
3.POST 文件上传 form
关于上传文件可以参考:OkHttp进行Post请求上传文件
代码部分和之前的POST差不多
public void doPostMultiPart(String url, HashMap<String, String> headers,
HashMap<String, String> params, INetCallBack callBack) {
//1.构建FormBody,传入参数
MultipartBody.Builder multipartBodyBuilder = new MultipartBody.Builder();
//设置类型
multipartBodyBuilder.setType(MultipartBody.FORM);
if (params != null) {
for (String param : params.keySet()) {
multipartBodyBuilder.addFormDataPart(param, params.get(param));
}
}
//2.添加头部信息
Request.Builder requestBuilder = new Request.Builder();
addHeader(requestBuilder, headers);
//3.构建Request,将multipartBodyBuilder作为Post方法的参数传入
Request request = requestBuilder
.url(url)
.post(multipartBodyBuilder.build())
.build();
//4.调用请求,重写回调方法
executeRequest(callBack, request);
}
执行代码
HashMap<String, String> params = new HashMap<>();
params.put("username", "David");
params.put("password", "123");
HashMap<String, String> headers = new HashMap<>();
headers.put("author_header", "David");
OkHttpUtils.getInstance().doPostMultiPart("http://www.imooc.com/api/okhttp/postmethod",
params,
headers,
new INetCallBack() {
@Override
public void onFailed(Throwable ex) {
Toast.makeText(MainActivity.this, "网络发生错误", Toast.LENGTH_SHORT).show();
}
@Override
public void onSuccess(String response) {
mTvContent.setText(response);
}
}
);
运行结果
可以看出POST的类型为Content-Type: multipart/form-data;
4.POST JSON
public void doPostJson(String url, HashMap<String, String> headers,
String jsonStr, INetCallBack callBack) {
//传入字符串格式
MediaType jsonMediaType = MediaType.get("application/json");
RequestBody requestBody = RequestBody.create(jsonStr, jsonMediaType);
Request.Builder requestBuilder = new Request.Builder();
addHeader(requestBuilder, headers);
Request request = requestBuilder
.url(url)
.post(requestBody)
.build();
executeRequest(callBack, request);
}
HashMap<String,String> headers = new HashMap<>();
headers.put("author_header","david");
OkHttpUtils.getInstance().doPostJson("http://www.imooc.com/api/okhttp/postjson",
headers,
"{\"name\":\"david\",\"age\":12}",
new INetCallBack() {
@Override
public void onSuccess(String response) {
mTvContent.setText(response);
}
@Override
public void onFailed(Throwable ex) {
Toast.makeText(MainActivity.this, "网络发生错误", Toast.LENGTH_SHORT).show();
}
}
);
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210129081133833.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0JsdWUzUmVkMQ==,size_16,color_FFFFFF,t_70)
三、自定义Intercepter
创建MyIntercepter类
public class MyIntercepter implements Interceptor {
@NotNull
@Override
public Response intercept(@NotNull Chain chain) throws IOException {
Request originRequest = chain.request();
Request newRequest = originRequest.newBuilder()
.addHeader("author", "david_intercepter")
.build();
return chain.proceed(newRequest);
}
}
在OkHttpClient.Builder() 中添加自定义Interceptor
client = new OkHttpClient.Builder()
.addInterceptor(new MyInterceptor())
.addInterceptor(logging)//添加拦截器,打印请求结果
.build();
这样就可以统一添加header了
学习回调
下面方法使用到了回调,可以参考这篇文章学习:Android自定义回调函数
例子:
public interface MyInterface {
void sayYourName();
}
public class MyClass {
public MyClass() {
Log.e("WangJ", "MyClass-constructor"); //标注构造函数
}
/* 用接口类型的对象作为输入参数 */
public void sayYourName(MyInterface myInterface){
Log.e("WangJ", "MyClass-sayYourName_start"); //标注方法开始
myInterface.sayYourName(); //遇到不知道具体实现的时候就用接口的抽象方法
Log.e("WangJ", "MyClass-sayYourName_finish"); //方法结束
}
}
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyClass myClass = new MyClass();
myClass.sayYourName(new MyInterface() { //实现接口并作为参数传入
@Override
public void sayYourName() {
Log.e("WangJ", "callBack-interface-implementor"); //具体操作实现
}
});
}
}
扩展阅读:Builder 设计模式