OkHttp3

作者: HOLLE_karry | 来源:发表于2020-03-27 20:14 被阅读0次

1.OkHttp3简介:

①支持http和https协议,api相同,易用(s:ssl安全套接字,易于做适配)
②http使用线程池,https使用多路复用
③okhttp支持同步和异步调用
④支持普通form和文件上传、下载form
⑤操作请求和响应(日志,请求头,body等)
⑥okhttp可以设置缓存
⑦支持透明的gzip压缩响应体

2.OkHttp3配置

①依赖:implementation 'com.squareup.okhttp3:okhttp:3.12.0'
②添加网络权限:<uses-permission android:name="android.permission.INTERNET"/>

3.OkHttp3使用思路

get请求思路

①获取okHttpClient对象
②构建Request对象
③构建Call对象
④通过Call.enqueue(callback)方法来提交异步请求,execute()方法实现同步请求

⑴get异步请求
String URL="http://www.qubaobei.com/ios/cf/dish_list.php?stage_id=1&limit=20&page=1";

//获取okHttpClient对象(构建者模式/建造者模式)
        OkHttpClient build = new OkHttpClient.Builder().build();
        //构建Request对象
        final Request request = new Request.Builder()
                .get()
                .url(Constants.URL)
                .build();
        //构建Call对象
        Call call = build.newCall(request);
        //通过Call.enqueue(callback)方法来提交异步请求
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.e(TAG, "onFailure: "+e.getMessage());
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
//              Log.e(TAG, "当前线程: "+Thread.currentThread().getName());//E/MainActivity: 当前线程: OkHttp http://www.qubaobei.com/...(子线程,主线程打印main)
                String json = response.body().string();
                Gson gson = new GsonBuilder().serializeNulls().create();
                Bean bean = gson.fromJson(json, Bean.class);
                final String title = bean.getData().get(0).getTitle();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(MainActivity.this, title, Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });

⑵get同步请求

......//前三步一样
 //通过Call.execute()方法实现同步请求(获取网络数据是耗时操作,在子线程中执行)
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Response execute = call.execute();
                    Log.e(TAG, "当前线程: " + Thread.currentThread().getName());
                    String json = execute.body().string();
                    Gson gson = new GsonBuilder().serializeNulls().create();
                    Bean bean = gson.fromJson(json, Bean.class);
                    final String title = bean.getData().get(0).getTitle();
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(MainActivity.this, title, Toast.LENGTH_SHORT).show();
                        }
                    });
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
post请求思路

①获取okHttpClient对象
②构建RequestBody
③构建Request对象
④构建Call对象
⑤通过Call.enqueue(callback)方法来提交异步请求,execute()方法实现同步请求

⑴post异步请求
String URL_POST="http://www.qubaobei.com/ios/cf/dish_list.php?stage_id=1&limit=20&";

//获取okHttpClient对象
        OkHttpClient build = new OkHttpClient.Builder().build();
        //构建RequestBody
        FormBody body = new FormBody.Builder()
                .add("page", "1")
                .build();
        //构建Request对象
        Request request = new Request.Builder()
                .post(body)
                .url(Constants.URL_POST)
                .build();
        //构建Call对象
        Call call = build.newCall(request);
        //通过Call.enqueue(callback)方法来提交异步请求
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.e(TAG, "onFailure: " + e.getMessage());
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String json = response.body().string();
                Gson gson = new GsonBuilder().serializeNulls().create();
                Bean bean = gson.fromJson(json, Bean.class);
                final String title = bean.getData().get(0).getTitle();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(MainActivity.this, title, Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });

⑵post同步请求

//通过Call.execute()方法实现同步请求
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Response execute = call.execute();
                    String json = execute.body().string();
                    Gson gson = new GsonBuilder().serializeNulls().create();
                    Bean bean = gson.fromJson(json, Bean.class);
                    final String title = bean.getData().get(1).getTitle();
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(MainActivity.this, title, Toast.LENGTH_SHORT).show();
                        }
                    });
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();

总结:
1.让 OkHttpClient 保持单例,用同一个 OkHttpClient 实例来执行所有请求,因为每一个 OkHttpClient 实例都拥有自己的连接池和线程池,重用这些资源可以减少延时和节省资源,如果为每个请求创建一个 OkHttpClient 实例,显然就是一种资源的浪费。
2.每一个Call(其实现是RealCall)只能执行一次,否则会报异常。
3.response.body().string()只调用一次
4.子线程加载数据后,主线程刷新数据

4.下拉刷新上拉加载

①导依赖(implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
②在界面中创建SmartRefreshLayout,包裹 列表组件:RecyclerView或ListView等
③添加OnRefreshListener监听,实现下拉刷新,OnLoadMoreListener监听,实现上拉加载

srl.setOnLoadMoreListener(new OnLoadMoreListener() {
            @Override
            public void onLoadMore(@NonNull RefreshLayout refreshLayout) {
                page++;
                initData();
            }
        });
        srl.setOnRefreshListener(new OnRefreshListener() {
            @Override
            public void onRefresh(@NonNull RefreshLayout refreshLayout) {
                list.clear();
                page=1;
                initData();
            }
        });

5.请求头处理(Header)以及超时和缓冲处理以及响应处理

private void initData() {
         //设置超时
        OkHttpClient build = new OkHttpClient.Builder()
                .connectTimeout(5, TimeUnit.SECONDS)//设置连接时间
                .readTimeout(5, TimeUnit.SECONDS)//设置读取时间
                .writeTimeout(5, TimeUnit.SECONDS)//设置写入时间
                .cache(new Cache(new File(getCacheDir(),"cache"),10*1024*1024))//指定缓存路径,10m大小
                .build();
        //请求头设置
        final Request request = new Request.Builder()
                .addHeader("Content-Type","application/x-www-form-urlencoded;charset=utf-8")
                .header("User-Agent","OkHttp Example")
                .get().url(Constanst.URL)
                .build();
        Call call = build.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.e(TAG, "onFailure: "+e.getMessage());
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //响应信息
                String message = response.message();
                Log.e(TAG, "响应信息: "+message);
                //响应头
                Headers headers = response.headers();
                for (int i = 0; i <headers.size() ; i++) {
                    Log.e(TAG, "响应头: "+headers.name(i)+"------"+headers.value(i));
                }
                //响应体
                Log.e(TAG, "响应体: "+Thread.currentThread().getName());
                String json = response.body().string();
                Gson gson = new GsonBuilder().serializeNulls().create();
                Bean bean = gson.fromJson(json, Bean.class);
                final List<Bean.DataBean> data = bean.getData();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        list.addAll(data);
                        adapter.notifyDataSetChanged();
                    }
                });
            }
        });

补充:都是根目录

File file = getCacheDir();//本应用的缓存目录,缓存删除后就没有了
File file1 = Environment.getExternalStorageDirectory();//手机的应用管理,缓存一直都在

6.请求体处理

form表单,String字符串,流,文件

7.HttpURLconnection及OkHttp3的对比分析

①HttpUrlConnection,google官方提供的用来访问网络,但是实现的比较简单,只支持1.0/1.1
②并没有多路复用,如果碰到app大量网络请求的时候,性能比较差,
③HttpUrlConnection底层也是用Socket来实现的
④OkHttp像HttpUrlConnection一样,实现了一个网络连接的过程。
⑤OkHttp和HttpUrlConnection是一级的,用socket实现了网络连接,OkHttp更强大,
⑥HttpUrlConnection在IO方面用到的是InputStream和OutputStream,但OkHttp用的是sink和source,这两个是在Okio这个开源库里的, feredSink(支持缓冲)、GzipSink(支持Gzip压缩)、ForwardingSink和InflaterSink(后面这两者服务于GzipSink)
⑦多个相同host和port的stream可以共同使用一个socket,而RealConnection就是处理连接的,那也就是说一个RealConnection上可能有很多个Stream
⑧OkHttp代码比HttpURLConnection精简的多

8.拦截器

拦截器.png

OkHttp的拦截器可谓是其整个框架的精髓,用户可传入的 interceptor 分为两类:Application Intercetor和NetworkInterceptor

1.Application interceptors 应用拦截器
 okClient.addInterceptor(new LoggingInterceptor())
Application Interceptor 是第一个 Interceptor 因此它会被第一个执行,因此这里的 request 还是最原始的。而对于 response 而言呢,因为整个过程是递归的调用过程,因此它会在 CallServerInterceptor 执行完毕之后才会将 Response 进行返回,因此在 Application Interceptor 这里得到的 response 就是最终的响应,虽然中间有重定向,但是这里只关心最终的 response
 ①不需要去关心中发生的重定向和重试操作。因为它处于第一个拦截器,会获取到最终的响应
 ②只会被调用一次,即使这个响应是从缓存中获取的。
 ③只关注最原始的请求,不去关系请求的资源是否发生了改变,我只关注最后的 response 结果而已。
 ④因为是第一个被执行的拦截器,因此它有权决定了是否要调用其他拦截,也就是 Chain.proceed() 方法是否要被执行。
 ⑤因为是第一个被执行的拦截器,因此它有可以多次调用 Chain.proceed() 方法,其实也就是相当与重新请求的作用了。
2.Network Interceptors 网络拦截器
 OKClient.addNetworkInterceptor(new LoggingInterceptor())
NetwrokInterceptor 处于第 6 个拦截器中,它会经过 RetryAndFollowIntercptor 进行重定向并且也会通过 BridgeInterceptor 进行 request 请求头和 响应 resposne 的处理,因此这里可以得到的是更多的信息。在打印结果可以看到它内部是发生了一次重定向操作,所以NetworkInterceptor 可以比 Application Interceptor 得到更多的信息了
 ①因为 NetworkInterceptor 是排在第 6 个拦截器中,因此可以操作经过 RetryAndFollowup 进行失败重试或者重定向之后得到的resposne。
 ②为响应直接从 CacheInterceptor 返回了。
 ③观察数据在网络中的传输。
 ④可以获得装载请求的连接。

9.拦截器的应用

1.日志拦截器

①获取请求
②打印请求信息
③网络请求
④响应

OkHttpClient build = new OkHttpClient.Builder()
                .addInterceptor(new LogingInterceptor())//应用拦截器
                .addNetworkInterceptor(new LogingInterceptor())//网络拦截器
                .connectTimeout(5, TimeUnit.SECONDS)//设置连接时间
                .readTimeout(5, TimeUnit.SECONDS)//设置读取时间
                .writeTimeout(5, TimeUnit.SECONDS)//设置写入时间
                .cache(new Cache(new File(getCacheDir(),"cache"),10*1024*1024))
                .build();
class LogingInterceptor implements Interceptor{
        @Override
        public Response intercept(Chain chain) throws IOException {
            //获取请求
            Request request = chain.request();
            //打印请求信息
            long start = System.nanoTime();
            Log.d(TAG, String.format("Sending request %s on %s%n%s",request.url(),chain.connection(),request.headers()));
            //网络请求
            Response response = chain.proceed(request);
            //响应
            long end = System.nanoTime();
            Log.d(TAG, String.format("Received response for %s in %.1fms%n%s",response.request().url(),(end-start)/1e6d,response.headers()));
            return response;
        }
    }

2.缓存拦截器(通过adb命令查看data数据)
在中主要做(post方式无法缓存)

①设置缓存位置
②无网时:设置缓存协议,加载缓存数据
③有网时:加载网络数据

//获取okHttpClient对象(构建者模式/建造者模式)
        OkHttpClient build = new OkHttpClient.Builder()
                .addInterceptor(new CacheInterceptor())
                .addNetworkInterceptor(new CacheInterceptor())
                .build();
class CacheInterceptor implements Interceptor {
        @Override
        public Response intercept(Chain chain) throws IOException {
            //请求数据
            Request request = chain.request();
            //判断若果无网时,设置缓存协议
            if (!isNetworkAvailable(MainActivity.this)) {
                request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build();
            }
            //开始请求网络,获取响应数据
            Response response = chain.proceed(request);
            //判断是否有网
            if (!isNetworkAvailable(MainActivity.this)) {
                int max = 0;
                return response.newBuilder()
                        .removeHeader("Pragma")
                        .header("CaChe-Control", "public,max-age=" + max)
                        .build();
            }else {
                int min=15*60;
                return response.newBuilder()
                        .removeHeader("Pragma")
                        .header("CaChe-Control", "public,only-if-cached,max-age=" + min)
                        .build();
            }
        }
    }
//检测是否有网
    public static boolean isNetworkAvailable(Context context) {
        if (context != null) {
            ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo info = cm.getActiveNetworkInfo();
            if (info != null) {
                return info.isAvailable();
            }
        }
        return false;
    }

10.OkHttp源码分析

okHttp源码.png

11.OkHttp中的设计模式

设计模式

12.OkHttp中的线程池

线程池

13.RecyclerView布局刷新

⑴一般使用:notifyDataSetChanged();
⑵如果只修改一个子条目的数据,全部刷新浪费性能,所以就有局部刷新
①页面结构不变,直接使用api即可

notifyItemInserted(position);//通知插入了一条数据
notifyItemMoved(fromPosition,toPosition);//通知两条数据换位置
notifyItemRemoved(position);//通知移除了一条数据
notifyItemChanged(position);//通知某个数据发生了改变
notifyItemRangeInserted(positionStart,itemCount);//通知从某个位置开始插入数据,共插入了几条
notifyItemRangeChanged(positionStart,itemCount);//通知某个位置开始数据发生了改变,共有几个改变了
notifyItemRangeRemoved(positionStart,itemCount);//通知从某个位置开始移除itemCount个数据

②对应页面结构会发生改变的刷新,页面刷新后会出现索引越界和混乱的问题,所以需要进行如下处理:

mRlvAdapter.mData.add(mPosition,"新数据:"+mPosition);//插入一条数据
mRlvAdapter.notifyItemInserted(mPosition);//通知局部刷新
mRlvAdapter.notifyItemRangeChanged(mPosition,mRlvAdapter.mData.size()-mPosition);//告诉适配器,某个范围内的数据发送了改变是从我们插入位置开始,以下全部数据都要通知,系统会更新新的索引,这样可以避免索引混乱

相关文章

网友评论

    本文标题:OkHttp3

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