美文网首页RxJavaAndroid开发积累安卓网络
RxJava<第三十篇>:Android开发必须了解

RxJava<第三十篇>:Android开发必须了解

作者: NoBugException | 来源:发表于2019-04-02 17:02 被阅读17次
    (1)添加依赖库
    implementation 'com.squareup.okhttp3:okhttp:3.14.0'
    
    (2)解决引入依赖库之后的异常

    引入依赖库之后需要对Java8的编译支持,否则会报错,在build.gradle中添加:

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    
    图片.png
    (3)添加网络权限
    <uses-permission android:name="android.permission.INTERNET" />
    
    (4)Get请求
    • 同步调用

    使用call.execute()实现同步调用,但是从Android3.0之后出现一个新的策略,凡是网络请求必须在异步线程执行,不能在UI线程执行,否则抛出异常,所以以下代码的网络请求部分必须在子线程执行

                //(1)创建client对象
                OkHttpClient client = new OkHttpClient.Builder().build();
                //(2)创建Request对象
                Request request = new Request.Builder()
                        .get()
                        .url("https:www.baidu.com")
                        .build();
                //(3)将Request封装成Call
                Call call = client.newCall(request);
    
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            //(4)开始Get请求
                            Response response = call.execute();
                            if(response.isSuccessful()){
    
                            }else{
    
                            }
                            Log.d("aaa", response.body().toString());
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
    

    在平时,我们应当尽量避免在UI线程执行网络请求,因为网络请求必然会阻塞UI线程。
    其实call.execute()是可以在UI线程执行的,我们可以通过代码取消Android3.0对网络请求不能在UI线程执行的限制

        StrictMode.ThreadPolicy policy=new StrictMode.ThreadPolicy.Builder().permitAll().build();
        StrictMode.setThreadPolicy(policy);
    

    onCreate方法中添加这两句代码就可以让call.execute()在主线程执行。

                //(1)创建client对象
                OkHttpClient client = new OkHttpClient.Builder().build();
                //(2)创建Request对象
                Request request = new Request.Builder()
                        .get()
                        .url("https:www.baidu.com")
                        .build();
                //(3)将Request封装成Call
                Call call = client.newCall(request);
    
                //(4)开始Get请求
                Response response = null;
                try {
                    response = call.execute();
                    if(response.isSuccessful()){
                        
                    }else{
                        
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                Log.d("aaa", response.body().toString());
    
    • 异步调用

                //(1)创建client对象
                OkHttpClient client = new OkHttpClient.Builder().build();
                //(2)创建Request对象
                Request request = new Request.Builder()
                        .get()
                        .url("https:www.baidu.com")
                        .build();
                //(3)将Request封装成Call
                Call call = client.newCall(request);
                
                //(4)异步调用,并设置回调函数
                call.enqueue(new Callback() {
                    @Override
                    public void onFailure(Call call, IOException e) {
                    
                    }
      
                    @Override
                    public void onResponse(Call call, final Response response) throws IOException {
                        final String res = response.body().string();
      
                    }
                });
      

    异步调用onFailureonResponse是在非UI线程上执行的。

    (5)Post请求
                //创建client对象
                OkHttpClient client = new OkHttpClient();
                //创建FormBody对象,并传参
                FormBody formBody = new FormBody.Builder()
                        .add("username", "admin")
                        .add("password", "admin")
                        .build();
                //创建request对象
                Request request = new Request.Builder()
                        .url("http://www.jianshu.com/")
                        .post(formBody)
                        .build();
                //将Request封装成Call
                Call call = client.newCall(request);
                //开始网络请求
                call.enqueue(new Callback() {
                    @Override
                    public void onFailure(Call call, IOException e) {
                        Log.d("aaa", "11111111111111");
                    }
    
                    @Override
                    public void onResponse(Call call, Response response) throws IOException {
                        String res = response.body().string();
                        Log.d("aaa", "22222:"+res);
    
                    }
                });
    

    同步调用和Get请求一致。

    (6)OkHttpClient

    OkHttpClient可配置的属性有

            OkHttpClient client=new OkHttpClient.Builder()
                    .connectTimeout(60, TimeUnit.SECONDS)       //设置连接超时
                    .readTimeout(60, TimeUnit.SECONDS)          //设置读超时
                    .writeTimeout(60,TimeUnit.SECONDS)          //设置写超时
                    .retryOnConnectionFailure(true)             //是否自动重连
                    .build();                                   //构建OkHttpClient对象
    

    内存的释放

                client.dispatcher().executorService().shutdown();   //清除并关闭线程池
                client.connectionPool().evictAll();                 //清除并关闭连接池
                client.cache().close();                             //清除cache
    

    newBuilder的使用

    OkHttpClient eagerClient = client.newBuilder() 
           .readTimeout(500, TimeUnit.MILLISECONDS)
           .build();
    

    这样创建的实例与原实例共享线程池、连接池和其他设置项,只需进行少量配置就可以实现特殊需求。

    (7)Request

    Request是网络请求的对象,其本身的构造函数是private的,只能通过Request.Builder来构建(建造者设计模式)。下面代码展示了常用的设置项。

        Request request = new Request.Builder()
                .url("https://api.github.com/repos/square/okhttp/issues")                          //设置访问url
                .get()                                                                             //类似的有post、delete、patch、head、put等方法,对应不同的网络请求方法
                .header("User-Agent", "OkHttp Headers.java")                                       //设置header
                .addHeader("Accept", "application/json; q=0.5")                                    //添加header
                .removeHeader("User-Agent")                                                        //移除header
                .headers(new Headers.Builder().add("User-Agent", "OkHttp Headers.java").build())   //移除原有所有header,并设置新header
                .addHeader("Accept", "application/vnd.github.v3+json")
                .build();
    
    (8)RequestBodyFormBodyMultipartBody
    • RequestBody
    图片.png

    从上图可知,创建RequestBody请求体支持文件、byte数组以及字符串。

    就以字符串为例

                JSONObject jsonObject = new JSONObject();
                try {
                    jsonObject.put("username", "admin");
                    jsonObject.put("password", "admin");
                } catch (JSONException e) {
                    e.printStackTrace();
                }
                RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonObject.toString());
    

    另外,requestBody可以获取以下属性

    图片.png

    contentLength: 消息体长度
    isDuplex: 是否支持双工
    isOneShot: 是否支持单工
    contentType: 获取MediaType

    这里还需要重点介绍一下MediaType,可分为::

    json: application/json
    xml: application/xml
    png: image/png
    jpg: image/jpeg
    gif: imge/gif
    form: application/x-www-form-urlencoded

    使用时最好带上编码:MediaType.parse("application/json; charset=utf-8")

    • FormBody

    FromBody用于提交表单键值对,其作用类似于HTML中的<form>标记。

    FormBody是RequestBody的子类,从源码可以看出它默认MediaType是application/x-www-form-urlencoded

    图片.png

    FormBody支持设置键值对

                RequestBody formBody = new FormBody.Builder()
                        .add("username", "admin")
                        .add("password", "admin")
                        .addEncoded(URLEncoder.encode("A"), URLEncoder.encode("B"))
                        .build();
    

    FormBody 的两个方法addaddEncoded可以添加键值对。

    FormBody对象可以获取到的值如图所示:

    图片.png

    encodedName: 指定角标,获取已编码的key;
    encodedValue: 指定角标,获取已编码的value;
    name: 指定角标获取key;
    value: 指定角标获取value;
    contentLength: 获取请求体的长度;
    contentType: 获取MediaType类型;
    size: 获取键值对的数量

    • MultipartBody

    使用MultipartBody.Builder可以构建与HTML文件上传格式兼容的复杂请求体。多块请求体中每块请求都是一个独立的请求体,都可以定义自己的请求头。这些请求头应该用于描述对应的请求体,例如Content-Disposition,Content-Length,和Content-Type会自动被添加到请求头中。

    它有五种MediaType,分别是:

    MultipartBody.MIXED: "multipart/mixed"
    MultipartBody.ALTERNATIVE: "multipart/alternative"
    MultipartBody.DIGEST: "multipart/digest"
    MultipartBody.PARALLEL: "multipart/parallel"
    MultipartBody.FORM: "multipart/form-data"

    MultipartBody.FORM 比较常用,其他四种查询了很多资料也没有完全理清楚。

    multipartBody可调用的方法有:

    图片.png

    setType: 设置MediaType
    addFormDataPart:

    图片.png
    两个参数:键值对
    三个参数:键值对+文件消息体(一般用于文件上传)

    addPart: 每个Part(块)都会封装一个请求体

    图片.png

    代码如下:

                RequestBody multipartBody = new MultipartBody.Builder()
                        .setType(MultipartBody.FORM)
                        .addPart(
                                Headers.of("Content-Disposition", "form-data; name=\"title\""),
                                RequestBody.create(null, "Logo"))
                        .addPart(
                                Headers.of("Content-Disposition", "form-data; name=\"image\""),
                                RequestBody.create(MediaType.parse("image/png"), new File("website/static/logo.png")))
                        .addFormDataPart("discription","beautiful")
                        .build();
    
    (9)Call

    Call对象表示一个已经准备好可以执行的请求,用这个对象可以查询请求的执行状态,或者取消当前请求。它具有以下方法:

        Call call=client.newCall(request);              //获取Call对象
        Response response=call.execute();               //同步执行网络请求,不要在主线程执行
        call.enqueue(new Callback());                   //异步执行网络请求
        call.cancel();                                  //取消请求
        call.isCanceled();                              //查询是否取消
        call.isExecuted();                              //查询是否被执行过
    

    要注意的是,每个Call对象只能执行一次请求。如果想重复执行相同的请求,可以:

    Call reCall=client.newCall(call.request());     //获取另一个相同配置的Call对象
    
    (10)Response

    Response是网络请求的结果下面是一些常用方法:

        Response response=call.execute();               //获取Response对象
        response.code();                                //请求的状态码
        response.isSuccessful();                        //如果状态码为[200..300),则表明请求成功
        Headers headers=response.headers();              //获取响应头
        List<String> names=response.headers("name");     //获取响应头中的某个字段
        ResponseBody body=response.body();             //获取响应体
    

    其中ResponseBody代表响应体,用于操作网络请求返回的内容。常用方法如下:

        body.contentLength();                           //body的长度
        String content=body.string();                   //以字符串形式解码body
        byte[] byteContent=body.bytes();                //以字节数组形式解码body
        InputStreamReader reader=body.charStream();     //将body以字符流的形式解码
        InputStream inputStream=body.byteStream();      //将body以字节流的形式解码
    

    需要注意的是:

    • ResponseBody必须关闭,不然可能造成资源泄漏,你可以通过以下方法关闭ResponseBody,对同一个ResponseBody只要关闭一次就可以了。

        Response.close();
        Response.body().close();
        Response.body().source().close();
        Response.body().charStream().close();
        Response.body().byteString().close();
        Response.body().bytes();
        Response.body().string();
      
    • ResponseBody只能被消费一次,也就是string(),bytes(),byteStream()或 charStream()方法只能调用其中一个。

    • 如果ResponseBody中的数据很大,则不应该使用bytes() 或 string()方法,它们会将结果一次性读入内存,而应该使用byteStream()或 charStream(),以流的方式读取数据。

    相关文章

      网友评论

        本文标题:RxJava<第三十篇>:Android开发必须了解

        本文链接:https://www.haomeiwen.com/subject/lxibbqtx.html