zoukankan      html  css  js  c++  java
  • okhttp教程——起步篇

    okhttp教程——起步篇

    这篇文章主要总结Android著名网络框架-okhttp的基础使用,后续可能会有关于他的高级使用。

    okhttp是什么

    okhttp是Android端的一个Http客户端,其基础功能相当于Android自带的HttpURLConnectionApache HTTP Client,但他却比自带的2个Http客户端优越很多,一者是写法简单,二者okhttp处理很多网络复杂问题,如会从很多常用的连接问题中自动恢复。如果您的服务器配置了多个IP地址,当第一个IP连接失败的时候,OkHttp会自动尝试下一个IP。OkHttp还处理了代理服务器问题和SSL握手失败等等很多问题。关于第二者,这篇文章不讨论。

    okhttp的导入

    Gradle导入

     compile 'com.squareup.okhttp3:okhttp:3.2.0'
     compile 'com.squareup.okio:okio:1.6.0'
    

    okhttp基础使用

    这里我们主要介绍简单的使用,介绍内容如下

    1. get请求
    2. post请求,参数是键值对
    3. post请求,多种类型的body
    4. 文件下载
    5. 加入Gson

    get请求

    get请求分为同步get和异步get,两者的区别主要get的方式是工作在另一个线程还是工作在本线程。请求的方式大同小异。
    首先定义一个OkHttpClient对象,如下

    private OkHttpClient client = new OkHttpClient();
    

    然后构建一个Request,构建方式如下:

    Request request = new Request.Builder().
    url("http://www.baidu.com").
    build();
    

    这个是最简单的request的构建方式,当然我们可以构建的很复杂。

     Request request = new Request.Builder().
                   url("http://www.baidu.com").
                   addHeader("User-Agent","android").
                  header("Content-Type","text/html; charset=utf-8").
                  build();
    

    通过addHeader和header方法为请求增加请求头部,注意使用header(name, value)可以设置唯一的name、value。如果已经有值,旧的将被移除,然后添加新的。使用addHeader(name, value)可以添加多值(添加,不移除已有的)。

    同步的get方法,通过client.newCall(request).execute()方法得到请求的response.

     Response response = okHttpClient.newCall(request).execute();
    

    OkHttp封装了很多处理response的方法,比如response.headers()的得到headers.

    Headers headers = response.headers();
     for (int i = 0; i < headers.size(); i++) {
    System.out.println(headers.name(i) + ": " + headers.value(i)); }
    

    结果如下:

    Date: Mon, 18 Apr 2016 05:23:43 GMT
    Content-Type: text/html; charset=utf-8
    Transfer-Encoding: chunked
    Connection: Keep-Alive
    Vary: Accept-Encoding
    Set-Cookie: BAIDUID=A323EC9BF678C0EB78E20741FD71211B:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
    Set-Cookie: BIDUPSID=A323EC9BF678C0EB78E20741FD71211B; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
    Set-Cookie: PSTM=1460957023; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
    Set-Cookie: BDSVRTM=0; path=/
    Set-Cookie: BD_HOME=0; path=/
    Set-Cookie: H_PS_PSSID=1434_19672_18281_19690_17948_18205_19558_15952_12257; path=/; domain=.baidu.com
    P3P: CP=" OTI DSP COR IVA OUR IND COM "
    Cache-Control: private
    Cxy_all: baidu+2db7793e0e32b9f6c20be8f623e1ae43
    Expires: Mon, 18 Apr 2016 05:22:55 GMT
    X-Powered-By: HPHP
    Server: BWS/1.1
    X-UA-Compatible: IE=Edge,chrome=1
    BDPAGETYPE: 1
    BDQID: 0xfacc6fc10004ca96
    BDUSERID: 0
    OkHttp-Sent-Millis: 1460957021226
    OkHttp-Received-Millis: 1460957021430
    

    响应报文的实体可以通过response.body().string()获取;如果希望获得返回的二进制字节数组,则调用response.body().bytes();如果你想拿到返回的inputStream,则调用response.body().byteStream()。

    异步的get请求得到的response方法是通过如下方法

    okHttpClient.newCall(request).enqueue(new Callback() {
                            @Override
                            public void onFailure(Call call, IOException e) {
                            }       
                            @Override
                            public void onResponse(Call call, Response response) throws IOException {
                            }
                  });
    

    在onResponse方法中,执行请求成功的代码,onFailure方法中,执行请求失败的代码,下面给一个完整的异步get的栗子

    import android.os.Bundle;
    import android.os.Handler;
    import android.support.v7.app.AppCompatActivity;
    import android.text.method.ScrollingMovementMethod;
    import android.widget.TextView;
    
    import java.io.IOException;
    
    import okhttp3.Call;
    import okhttp3.Callback;
    import okhttp3.Headers;
    import okhttp3.OkHttpClient;
    import okhttp3.Request;
    import okhttp3.Response;
    
    public class MainActivity extends AppCompatActivity {
    
        private OkHttpClient okHttpClient = new OkHttpClient();
        public TextView show;
        public Handler handler = new Handler();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            show = (TextView) findViewById(R.id.show);
            show.setMovementMethod(ScrollingMovementMethod.getInstance());
            Request request = new Request.Builder().
                    url("http://www.baidu.com").
                    addHeader("User-Agent", "android").
                    header("Content-Type", "text/html; charset=utf-8").
                    get().
                    build();
            okHttpClient.newCall(request).enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
    
                }
                @Override
                public void onResponse(Call call, final Response response) throws IOException {
                    final Headers headers = response.headers();
                    final String str = response.body().string();
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            for (int i = 0; i < headers.size(); i++) {
                                show.append(headers.name(i) + ": " + headers.value(i));
                                show.append(str);
                            }
                        }
                    });
    
                }
            });
    
    
        }
    
    }
    

    其实按照官网说的,回调是发生在response 的headers准备好就发生的,所以有时候请求response的实体部分会发生阻塞。

    post请求——键值对为参数。

    post请求和get请求除了在构建request上面不同以外,在处理response上面都是一样的,所以下面我们只讨论一下post构建request,当然post也是支持同步post和异步post的,可以参考get方法。
    在构建post的request时候,首先用FormBody.Builder去构建request的body部分,栗子如下,当然这是OKHttp3的方法.

    FormBody.Builder builder = new FormBody.Builder();
            for(int i = 0 ; i < key.size() ;i ++){
                builder.add(key.get(i),value.get(i));
            }
            RequestBody body = builder.build();
    

    builder中add的是要加入的参数键值对。得到要构造的body后用

    Request request = new Request.Builder().url(url).post(body).build();
    

    获得请求的request,后面的操作就和get方法是一样的,这里可以参考异步get的栗子,构建一个post的request.下面的写法原封不变。

    post请求--多种类型的body

    上文已经说了post和get的用法主要在构建不同的request上面,所以接下来我们主要讨论的也是如何构建request.
    参考上面,我们首先要创建一个requestBody,我们可以用下面的方式去构建,当然这也是okhttp3的方法

    MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM);
    

    已表单上传的形式去提交post。我们看一下builder的方法

     /** Add a part to the body. */
        public Builder addPart(RequestBody body) {
          return addPart(Part.create(body));
        }
    
        /** Add a part to the body. */
        public Builder addPart(Headers headers, RequestBody body) {
          return addPart(Part.create(headers, body));
        }
    
        /** Add a form data part to the body. */
        public Builder addFormDataPart(String name, String value) {
          return addPart(Part.createFormData(name, value));
        }
    
        /** Add a form data part to the body. */
        public Builder addFormDataPart(String name, String filename, RequestBody body) {
          return addPart(Part.createFormData(name, filename, body));
        }
    

    从这里我们可以看出可以直接用 public Builder addFormDataPart(String name, String filename, RequestBody body)上传一个File,最后一个参数是请求的实体,可以通过 RequestBody.create(final MediaType contentType, final File file) 获得,而MediaType则可以通过下面方法获得

    //调用judgeType方法
    private static final MediaType MEDIA_TYPE = MediaType.parse(judgeType(fileName);
    //judge方法如下
    private String judgeType(String path) {
            FileNameMap fileNameMap = URLConnection.getFileNameMap();
            String contentTypeFor = fileNameMap.getContentTypeFor(path);
            if (contentTypeFor == null) {
                contentTypeFor = "application/octet-stream";
            }
            return contentTypeFor;
        }
    

    由于我后台能力比较渣,这里用一个官网的例子来实现一遍我刚才讨论的方法。

    MultipartBody.Builder builder = new MultipartBody.Builder()
            .setType(MultipartBody.FORM)
            .addFormDataPart("image", "logo-square.png",
                RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png")));
    RequestBody requestBody = builder.build();
     
     Request request = new Request.Builder()
           .header("Authorization", "Client-ID " +"9199fdef135c122")
            .url("https://api.imgur.com/3/image")
            .post(requestBody)
            .build();
    

    当然除了这个方法以外,调用如下方法也是可以的,我们可以利用name和filename自己构造Header传上去。

    public Builder addPart(Headers headers, RequestBody body) {
          return addPart(Part.create(headers, body))
    

    栗子如下:

    builder.addPart(Headers.of("Content-Disposition", "form-data; name="" + name + ""; filename="" + fileName + """),fileBody);
    

    后面的写法和上面类似,这样我们就实现了文件上传的写法。

    文件下载

    刚才我们上面已经说了,希望获得返回的二进制字节数组,则调用response.body().bytes();如果你想拿到返回的inputStream,则调用response.body().byteStream()。换句话说,文件的下载可以简单的通过get请求,得到相应的response,在把他的实体转换成二进制流写入文件,就是实现了文件的下载。主要的写法就是文件的读写,跟OKHttp关系不大,当然我们也可以用okio来实现文件的读写,这里水平有限就不介绍了。下面给一个简单的例子。

    private void _download(final String url, final String destFileDir, final ResultCallback callback) {
            final Request request = new Request.Builder().url(url).build();
            final 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 {
                    InputStream is = null;
                    byte[] buf = new byte[2048];
                    int len = 0;
                    FileOutputStream fos = null;
                    try {
                        is = response.body().byteStream();
                        File file = new File(destFileDir, getFileName(url));
                        fos = new FileOutputStream(file);
                        while((len = is.read(buf)) != -1){
                            fos.write(buf,0,len);
                        }
                        fos.flush();
                       //....省略后续对已经保存的文件的操作
                       
                    } catch (IOException e) {
                        e.printStackTrace();
                    }finally {
                        try {
                            if (is != null) is.close();
                        } catch (IOException e) {
    
                        }
                        try
                        {
                            if (fos != null) fos.close();
                        } catch (IOException e)
                        {
                        }
                    }
                }
            });
        }
    

    加入Gson

    接下来,我们讨论一个很实际的问题,Android的网络请求一般不会去请求一个网站的Html,更多的是请求后台接口的Json文件,所以我们用Gson来处理json的解析。这一部分和前面就不同了,前面多数讲的是如何构建不同的request来得到response,而对响应的结果,处理都是一致的。但这里主要的写法就是用Gson去处理response,而request的构建则根据上面介绍的方法去构建,无需改变。

    Gson的导入

    compile 'com.google.code.gson:gson:2.6.2'
    

    比如我们后台给出的api是这样一个json文件

    {
        "status": 0,
        "intro": "你好",
        "shopName": "byhieg",    
        "message": "查询成功",
    }
    

    则我们可以简单的构建这样的一个Test.java文件,如下所示:

    public class Test {
    
    
        /**
         * status : 0
         * intro : byhieg
         * shopName : byhige
         * message : 查询成功
         */
    
        private int status;
        private String intro;
        private String shopName;
        private String message;
    
        public int getStatus() {
            return status;
        }
    
        public void setStatus(int status) {
            this.status = status;
        }
    
        public String getIntro() {
            return intro;
        }
    
        public void setIntro(String intro) {
            this.intro = intro;
        }
    
        public String getShopName() {
            return shopName;
        }
    
        public void setShopName(String shopName) {
            this.shopName = shopName;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    }
    

    在获得到response之后,用如下代码把Json文件解析成result对象。然后调用result对象的get方法就可以得到json文件中的intro的值和shopname的值,以及status和message.这里就不多介绍了

    Test result = new Gson().fromJson(response.body().string,Test.class);
    

    总结

    简单介绍了okHttp的使用,对于一些高级用法请关注下篇文章,本人水平有限,如有错误,还望指正。

  • 相关阅读:
    Nodejs下载和第一个Nodejs示例
    永久关闭Win10工具栏的TaskbarSearch控件
    对称加密,非对称加密,散列算法,签名算法
    【转】TTL和RS232之间的详细对比
    zlg核心板linux系统中查看系统内存等使用信息
    Power BI后台自动刷新数据报错 The operation was throttled by Power BI Premium because there were too many datasets being processed concurrently.
    剪切板和上传文件内容获取
    CSS, LESS, SCSS, SASS总结
    文字程序
    electron 打包“ERR_ELECTRON_BUILDER_CANNOT_EXECUTE”
  • 原文地址:https://www.cnblogs.com/qifengshi/p/5405550.html
Copyright © 2011-2022 走看看