Android网络框架学习之OkHttp

OkHttp官网地址
OkHttp GitHub地址
官网中有很详细的Java doc文档,我们可以很方便的查到OkHttp里面各个API的功能和使用方法。

get方式获取数据
private String doGet(String areaId)  {
        //1.创建一个okClient对象
        OkHttpClient client = new OkHttpClient();
        //2.创建一个Request请求
        String url = String.format(URL_Weather,areaId);
        Request request  = new Request.Builder().url(url).build();

        Call call = client.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.d(TAG,"请求服务器失败");
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                // 注:该回调是子线程,非主线程
                Log.d(TAG,"请求服务器成功:");
                weatherResult = response.body().string();
            }
        });
        return  weatherResult;
    }

以上是OkHttp的异步get请求,是okhttp3异步方法,不需要我们开启工作线程执行网络请求,返回的结果也在工作线程中;

 Response response = client.newCall(request).execute();
    if (response.isSuccessful()) {
        return response.body().string();
    } else {
        throw new IOException("Unexpected code " + response);
    }

以上是同步方法,如果使用同步get方法,需要自行开辟子线程进行网络请求,此处不推荐~

post方式获取数据

OkHttp的post方法可以提交键值对、文本、Stream、file等形式的数据,然后使用Request的post方法来提交请求体RequestBody。

private String doPost(String areaId) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // 请求完整url:http://api.k780.com:88/?app=weather.future&weaid=101281001&&appkey=%s&sign=%s&format=json
                    String url = "http://api.k780.com:88/";
                    OkHttpClient.Builder builder = new OkHttpClient.Builder()
                            .connectTimeout(800, TimeUnit.MILLISECONDS)
                            .cache(new Cache(getExternalCacheDir().getAbsoluteFile(),10*1024*1024))
                            .writeTimeout(5, TimeUnit.SECONDS);

                    OkHttpClient okHttpClient = builder.build();
                    RequestBody formBody = new FormBody.Builder().add("app", "weather.future")
                            .add("weaid", "101281001").add("appkey", APPKEY).add("sign",
                                    SIGN).add("format", "json")
                            .build();
                    Request request = new Request.Builder().url(url).post(formBody).build();
                    okhttp3.Response response = okHttpClient.newCall(request).execute();
                    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
                    weatherResult = response.body().string();
                    Log.i(TAG, weatherResult);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
        return weatherResult;
    }

接口参数
以下代码参考自OkHttp官方示例Http官方教程

post提交流:
public static final MediaType MEDIA_TYPE_MARKDOWN
      = MediaType.parse("text/x-markdown; charset=utf-8");

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
    RequestBody requestBody = new RequestBody() {
      @Override public MediaType contentType() {
        return MEDIA_TYPE_MARKDOWN;
      }

      @Override public void writeTo(BufferedSink sink) throws IOException {
        sink.writeUtf8("Numbers\n");
        sink.writeUtf8("-------\n");
        for (int i = 2; i <= 997; i++) {
          sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i)));
        }
      }

      private String factor(int n) {
        for (int i = 2; i < n; i++) {
          int x = n / i;
          if (x * i == n) return factor(x) + " × " + i;
        }
        return Integer.toString(n);
      }
    };

    Request request = new Request.Builder()
        .url("https://api.github.com/markdown/raw")
        .post(requestBody)
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    System.out.println(response.body().string());
}
post提交文件:
public static final MediaType MEDIA_TYPE_MARKDOWN
  = MediaType.parse("text/x-markdown; charset=utf-8");

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
    File file = new File("README.md");

    Request request = new Request.Builder()
        .url("https://api.github.com/markdown/raw")
        .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    System.out.println(response.body().string());
post提交表单(键值对):
private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
    RequestBody formBody = new FormBody.Builder()
        .add("search", "Jurassic Park")
        .build();
    Request request = new Request.Builder()
        .url("https://en.wikipedia.org/w/index.php")
        .post(formBody)
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    System.out.println(response.body().string());
}

此处需注意:
okHttp2.7用的是new FormEncodingBuilder(),而OkHttp3.x用的是FormBody.Builder();

Post提交String
public static final MediaType MEDIA_TYPE_MARKDOWN
  = MediaType.parse("text/x-markdown; charset=utf-8");

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
    String postBody = ""
        + "Releases\n"
        + "--------\n"
        + "\n"
        + " * _1.0_ May 6, 2013\n"
        + " * _1.1_ June 15, 2013\n"
        + " * _1.2_ August 11, 2013\n";

    Request request = new Request.Builder()
        .url("https://api.github.com/markdown/raw")
        .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody))
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    System.out.println(response.body().string());
}
Post提交分块请求

MultipartBuilder可以构建复杂的请求体,与HTML文件上传形式兼容。多块请求体中每块请求都是一个请求体,可以定义自己的请求头。这些请求头可以用来描述这块请求,例如他的Content-Disposition。如果Content-Length和Content-Type可用的话,他们会被自动添加到请求头中。

private static final String IMGUR_CLIENT_ID = "...";
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
   // Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image
                    RequestBody requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM)
                            .addPart(
                                    Headers.of("Content-Disposition", "form-data; name=\"title\""),
                                    RequestBody.create(null, "Square Logo"))
                            .addPart(
                                    Headers.of("Content-Disposition", "form-data; name=\"image\""),
                                    RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png")))
                            .build();

                    Request request = new Request.Builder()
                            .header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)
                            .url("https://api.imgur.com/3/image")
                            .post(requestBody)
                            .build();

                    Response response = client.newCall(request).execute();
                    if (!response.isSuccessful())
                        throw new IOException("Unexpected code " + response);

                    System.out.println(response.body().string());
}

此处OkHttp3也有和之前版本不一样的地方,OkHttp3使用了MultipartBody.Builder()的方法来创建RequestBody,而之前是使用MultipartBuilder()。
更多OkHttp的改动详见:okhttp3与旧版本okhttp的区别分析

关于MediaType
public static final MediaType MEDIA_TYPE_MARKDOWN
            = MediaType.parse("text/x-markdown; charset=utf-8");

text/html : HTML格式
text/plain :纯文本格式
text/xml : XML格式
image/gif :gif图片格式
image/jpeg :jpg图片格式
image/png:png图片格式
以application开头的媒体格式类型:

application/xhtml+xml :XHTML格式
application/xml : XML数据格式
application/atom+xml :Atom XML聚合格式
application/json : JSON数据格式
application/pdf :pdf格式
application/msword : Word文档格式
application/octet-stream : 二进制流数据(如常见的文件下载)
application/x-www-form-urlencoded : 中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式)

另外一种常见的媒体格式是上传文件之时使用的:
multipart/form-data : 需要在表单中进行文件上传时,就需要使用该格式
注意:MediaType.parse(“image/png”)里的”image/png”不知道该填什么,可以参考—》http://www.w3school.com.cn/media/media_mimeref.asp
如何使用呢?(在请求体里面写入类型和需要写入的数据,通过post请求)
String body = “hdsoifhjoihdsfh”;
RequestBody body = RequestBody.create(MEDIA_TYPE_MARKDOWN, body);

设置缓存目录和大小:

响应缓存使用HTTP头作为配置。你可以在请求头中添加Cache-Control: max-stale=3600 ,OkHttp缓存会支持。你的服务通过响应头确定响应缓存多长时间,例如使用Cache-Control: max-age=9600。

public CacheResponse(File cacheDirectory) throws Exception {
    int cacheSize = 10 * 1024 * 1024; // 10 MiB
    Cache cache = new Cache(cacheDirectory, cacheSize);

    client = new OkHttpClient();
    client.setCache(cache);
}
设置超时时间和缓存

和OkHttp2.x有区别的是不能通过OkHttpClient直接设置超时时间和缓存了,而是通过OkHttpClient.Builder来设置,通过builder配置好OkHttpClient后用builder.build()来返回OkHttpClient,所以我们通常不会调用new OkHttpClient()来得到OkHttpClient,而是通过builder.build():

File sdcache = getExternalCacheDir();
        int cacheSize = 10 * 1024 * 1024;
        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .connectTimeout(15, TimeUnit.SECONDS)
                .writeTimeout(20, TimeUnit.SECONDS)
                .readTimeout(20, TimeUnit.SECONDS)
                .cache(new Cache(sdcache.getAbsoluteFile(), cacheSize));
        OkHttpClient mOkHttpClient=builder.build();      

代码示例:

private String doGet(String areaId) {
        String url = String.format(URL_Weather, areaId);
        Request request = new Request.Builder().url(url).build();

        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.d(TAG, "请求服务器失败");
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.d(TAG, "请求服务器成功:\n"+response.body().string());
            }
        });
        return weatherResult;
    }

    private String doPost(String areaId) {
        try {
            // 请求完整url:http://api.k780.com:88/?app=weather.future&weaid=101281001&&appkey=10003&sign=b59bc3ef6191eb9f747dd4e83c99f2a4&format=json
            String url = "http://api.k780.com:88/";

            RequestBody formBody = new FormBody.Builder().add("app", "weather.future")
                    .add("weaid", areaId).add("appkey", "10003").add("sign",
                            "b59bc3ef6191eb9f747dd4e83c99f2a4").add("format", "json")
                    .build();
            Request request = new Request.Builder().url(url).post(formBody).build();
            Call call = okHttpClient.newCall(request);
            call.enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {

                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    weatherResult = response.body().string();
                    Log.i(TAG, weatherResult);
                }
            });

        } catch (Exception e) {
            e.printStackTrace();
        }
        return weatherResult;
    }

    private void doUpload() {
        File file = new File("/sdcard/zoky.txt");
        Request request = new Request.Builder()
                .url("https://api.github.com/markdown/raw")
                .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
                .build();
        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.d(TAG,"上传失败");
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.i(TAG, "上传成功:\n" + response.body().string());
            }
        });
    }
    private void doDownload() {

        String url = "http://avatar.csdn.net/5/8/5/1_zoky_ze.jpg";
        Request request = new Request.Builder().url(url).build();
        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.d(TAG,"下载失败");
            }

            @Override
            public void onResponse(Call call, Response response) {
                int len;
                InputStream inputStream = response.body().byteStream();
                FileOutputStream fileOutputStream;
                try {
                    fileOutputStream = new FileOutputStream(new File("/sdcard/zoky.jpg"));
                    byte[] buffer = new byte[2048];

                    while ((len = inputStream.read(buffer)) != -1) {
                        fileOutputStream.write(buffer, 0, len);
                    }
                    fileOutputStream.flush();
                    Log.d(TAG, "文件下载成功"+ response.body().string());
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        });

    }

github示例

内部调用流程

内部调用流程
有上图可以看出,OkHttp不管是异步调用还是同步调用,最终都是通过getResponseWithInterceptorChain()方法来进行网络请求

Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
  interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));

Interceptor.Chain chain = new RealInterceptorChain(
    interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}

在getResponseWithInterceptorChain()方法中加了一些拦截器,然后启动一个拦截器调用链,拦截器递归调用之后最后返回请求的响应Response。这里的拦截器分层的思想就是借鉴的网络里的分层模型的思想。请求从最上面一层到最下一层,响应从最下一层到最上一层,每一层只负责自己的任务,对请求或响应做各自的操作。OkHttp还支持用户自定义拦截器,插入到最顶层和CallServerInterceptor上一层的位置。square官方就有一个Logging Interceptor,用于打印网络请求日志的拦截器。

下图是拦截器的调用流程及各自的执行的操作。
拦截器

RealInterceptorChain的proceed(),每次重新创建一个RealInterceptorChain对象,然后调用下一层的拦截器的interceptor.intercept()方法。

@Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    // 1、该拦截器在Request阶段负责的事情

    // 2、调用RealInterceptorChain.proceed(),其实是递归调用下一层拦截器的intercept方法
    response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);

    //3、该拦截器在Response阶段负责的事情,然后返回到上一层拦截器的 response阶段
    return  response;     
    }
  }

参考文章:
OkHttp使用进阶 译自OkHttp Github官方教程
OkHttp3用法全解析
okhttp3与旧版本okhttp的区别分析
Android OkHttp完全解析 是时候来了解OkHttp了
OkHTT分析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值