Android网络 OkHttp简易用法/回调/Builder设计模式

这么多网络框架为什么使用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 设计模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>