资讯专栏INFORMATION COLUMN

Android网络编程5之OkHttp2.x用法全解析

wemall / 3142人阅读

摘要:需要注意的是回调并不是在线程。也可以通过来同时取消多个请求。在开始创建的时候配置好,在请求网络的时候用将请求的结果回调给线程。最后调用这个的方法请求成功使用起来简单多了,而且请求结果回调是在线程的。

前言

讲完了Volley,我们接下来看看目前比较火的网络框架OkHttp, 它处理了很多网络疑难杂症:会从很多常用的连接问题中自动恢复。如果您的服务器配置了多个IP地址,当第一个IP连接失败的时候,OkHttp会自动尝试下一个IP,此外OkHttp还处理了代理服务器问题和SSL握手失败问题。

1.使用前准备

eclipse引入jar包地址:
okhttp-2.7.5.jar
okio-1.7.0.jar

Android Studio 配置gradle:

  compile "com.squareup.okhttp:okhttp:2.7.5"
  compile "com.squareup.okio:okio:1.7.0"
2.异步GET请求

最简单的get请求,老规矩请求百度:

      private void getAsynHttp() {
        //创建okHttpClient对象
        OkHttpClient mOkHttpClient = new OkHttpClient();
        final Request request = new Request.Builder()
                .url("http://www.baidu.com")
                .build();
        Call call = mOkHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
            }

            @Override
            public void onResponse(final Response response) throws IOException {
                String str = response.body().string();
                Log.i("wangshu", str);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(getApplication(), "请求成功", Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });
    }

运行程序log打印出来的是百度首页的html文件,基本的步骤很简单,就是创建OkHttpClient、Request和Call,最后调用Call的enqueue()方法。但是每次这么写肯定是很麻烦,肯定是要进行封装的。需要注意的是onResponse回调并不是在UI线程。

3.同步GET请求
 private String getSyncHttp() throws IOException{
        OkHttpClient mOkHttpClient = new OkHttpClient();
        //创建请求Request
        final Request request = new Request.Builder()
                .url("http://www.baidu.com")
                .build();
        Call call = mOkHttpClient.newCall(request);
        Response mResponse=call.execute();
        if (mResponse.isSuccessful()) {
            return mResponse.body().string();
        } else {
            throw new IOException("Unexpected code " + mResponse);
        }
    }

同步Get请求和异步调用区别就是调用了call的execute()方法。

4.异步POST请求
    private void postAsynHttp() {
        OkHttpClient mOkHttpClient = new OkHttpClient();
        RequestBody formBody = new FormEncodingBuilder()
                .add("size", "10")
                .build();

        Request request = new Request.Builder()
                .url("http://api.1-blog.com/biz/bizserver/article/list.do")
                .post(formBody)
                .build();
        Call call = mOkHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {

            }

            @Override
            public void onResponse(Response response) throws IOException {
                String str = response.body().string();
                Log.i("wangshu", str);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(getApplicationContext(), "请求成功", Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });
    }

post与get不同的就是要要创建RequestBody并传进Request中,同样onResponse回调不是在UI线程。

5.请求缓存设置

首先我们设置缓存路径和大小并设置给OkHttpClient:

 mOkHttpClient = new OkHttpClient();
 File sdcache = getExternalCacheDir();
 int cacheSize = 10 * 1024 * 1024;
 mOkHttpClient.setCache(new Cache(sdcache.getAbsoluteFile(), cacheSize));

接下来异步GET请求baidu:

 private void getAsynHttp() {
        //创建请求Request
        final Request request = new Request.Builder()
                .url("http://www.baidu.com")
                .build();
        Call call = mOkHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
            }

            @Override
            public void onResponse(final Response response) throws IOException {
                if (null != response.cacheResponse()) {
                    String str = response.cacheResponse().toString();
                    Log.i("wangshu", "cache---" + str);
                } else {
                    response.body().string();
                    String str=response.networkResponse().toString();
                    Log.i("wangshu", "network---" + str);
                }
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(getApplicationContext(), "请求成功", Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });
    }

第一次请求会请求网络得到数据,第二次以及后面的请求则会从缓存中取出数据:

当然也有种情况是有的请求每次都需要最新的数据,则在创建Request,来设置cacheControl为“CacheControl.FORCE_NETWORK”,用来表示请求会一直请求网络得到数据:

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

运行程序结果为:

6.设置超时时间

另外我们也需要设置超时的时间用来处理各种网络超时的情况,超时的原因可能是网络问题也可能是服务器响应慢等问题,OkHttp当然不会忽略这一点,它支持连接、读取和写入超时的时间设置:

        mOkHttpClient = new OkHttpClient();
        mOkHttpClient.setConnectTimeout(15, TimeUnit.SECONDS);
        mOkHttpClient.setWriteTimeout(20, TimeUnit.SECONDS);
        mOkHttpClient.setReadTimeout(20, TimeUnit.SECONDS);
7.取消请求

使用call.cancel()可以立即停止掉一个正在执行的call。如果一个线程正在写请求或者读响应,将会引发IOException。当用户离开一个应用时或者跳到其他界面时,使用Call.cancel()可以节约网络资源,另外不管同步还是异步的call都可以取消。
也可以通过tags来同时取消多个请求。当你构建一请求时,使用RequestBuilder.tag(tag)来分配一个标签。之后你就可以用OkHttpClient.cancel(tag)来取消所有带有这个tag的call。

为了模拟这个场景我们首先创建一个定时的线程池:

   private ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);

接下来的代码为:

 private  void cancel(){
        final Request request = new Request.Builder()
                .url("http://www.baidu.com")
                .cacheControl(CacheControl.FORCE_NETWORK)
                .build();
        Call call=null;
        call = mOkHttpClient.newCall(request);
        final Call finalCall = call;
        //100毫秒后取消call
        executor.schedule(new Runnable() {
            @Override public void run() {
                finalCall.cancel();
            }
        }, 100, TimeUnit.MILLISECONDS);

        call.enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
            }

            @Override
            public void onResponse(final Response response) {
                if (null != response.cacheResponse()) {
                    String str = response.cacheResponse().toString();
                    Log.i("wangshu", "cache---" + str);
                } else {
                    try {
                        response.body().string();

                    } catch (IOException e) {
                        Log.i("wangshu", "IOException");
                        e.printStackTrace();
                    }
                    String str = response.networkResponse().toString();
                    Log.i("wangshu", "network---" + str);
                }
            }
        });
           Log.i("wangshu", "是否取消成功"+call.isCanceled());
    }

100毫秒后调用call.cancel(),为了能让请求耗时,我们设置每次请求都要请求网络,运行程序并且不断的快速点击发送请求按钮:

很明显每次cancel()都失败了,仍旧成功的访问了网络,在cancel()时已经有读写操作了所以会报IOException。每隔100毫秒来调用call.cancel()显然时间间隔太长,我们设置为1毫秒并不断的快速的点击发送请求按钮:

没有请求网络的log,几乎每次都取消成功了。

8.关于封装

如果每次请求网络都需要写重复的代码绝对是令人头疼的,网上也有很多对OkHttp封装的优秀开源项目,功能也非常强大,封装的意义就在于更加方便的使用,具有拓展性,但是对OkHttp封装最需要解决的是以下的两点:

避免重复代码调用

将请求结果回调改为UI线程

根据以上两点,我们也简单封装一下,在此只是举个例子,如果想要使用OkHttp封装的开源库,推荐使用OkHttpFinal。
首先呢我们写一个抽象类用于请求回调:

public abstract class ResultCallback
{
    public abstract void onError(Request request, Exception e);

    public abstract void onResponse(Response response);
}

接下来封装OkHttp,并实现了异步GET请求:

public class OkHttpEngine {
    private static OkHttpEngine mInstance;
    private OkHttpClient mOkHttpClient;
    private Handler mHandler;

    public static OkHttpEngine getInstance() {
        if (mInstance == null) {
            synchronized (OkHttpEngine.class) {
                if (mInstance == null) {
                    mInstance = new OkHttpEngine();
                }
            }
        }
        return mInstance;
    }

    private OkHttpEngine() {
        mOkHttpClient = new OkHttpClient();
        mOkHttpClient.setConnectTimeout(15, TimeUnit.SECONDS);
        mOkHttpClient.setWriteTimeout(20, TimeUnit.SECONDS);
        mOkHttpClient.setReadTimeout(20, TimeUnit.SECONDS);
        mHandler = new Handler();

    }

    public OkHttpEngine setCache(Context mContext) {
        File sdcache = mContext.getExternalCacheDir();
        int cacheSize = 10 * 1024 * 1024;
        mOkHttpClient.setCache(new Cache(sdcache.getAbsoluteFile(), cacheSize));
        return mInstance;
    }

    /**
     * 异步get请求
     * @param url
     * @param callback
     */
    public void getAsynHttp(String url, ResultCallback callback) {

        final Request request = new Request.Builder()
                .url(url)
                .build();
        Call call = mOkHttpClient.newCall(request);
        dealResult(call, callback);

    }

    private void dealResult(Call call, final ResultCallback callback) {
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
                sendFailedCallback(request, e, callback);
            }

            @Override
            public void onResponse(final Response response) throws IOException {
                sendSuccessCallback(response, callback);
            }

            private void sendSuccessCallback(final Response object, final ResultCallback callback) {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        if (callback != null) {
                            callback.onResponse(object);
                        }
                    }
                });
            }

            private void sendFailedCallback(final Request request, final Exception e, final ResultCallback callback) {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        if (callback != null)
                            callback.onError(request, e);
                    }
                });
            }

        });
    }
}

原理很简单就是,写一个双重检查模式的单例,不了解双重检查模式的请查看设计模式之单例模式的七种写法这篇文章。在开始创建的时候配置好OkHttpClient,在请求网络的时候用Handler将请求的结果回调给UI线程。

最后调用这个OkHttpEngine的getAsynHttp()方法:

  OkHttpEngine.getInstance().getAsynHttp("http://www.baidu.com", new ResultCallback() {
            @Override
            public void onError(Request request, Exception e) {

            }

            @Override
            public void onResponse(Response response) {
                String str = response.networkResponse().toString();
                Log.i("wangshu", str);
                Toast.makeText(getApplicationContext(), "请求成功", Toast.LENGTH_SHORT).show();
            }
        });

使用起来简单多了,而且请求结果回调是在UI线程的。下一篇我们会讲到OkHttp3,来看看它与OkHttp2.x之间的使用方式上有什么区别。

github源码下载

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70589.html

相关文章

  • Android网络编程6OkHttp3用法解析

    摘要:使用前准备配置添加网络权限异步请求惯例,请求百度可以省略,默认是请求请求成功与版本并没有什么不同,比较郁闷的是回调仍然不在线程。 前言 上一篇介绍了OkHttp2.x的用法,这一篇文章我们来对照OkHttp2.x版本来看看,OkHttp3使用起来有那些变化。当然,看这篇文章前建议看一下前一篇文章Android网络编程(五)OkHttp2.x用法全解析。 1.使用前准备 Android ...

    Amos 评论0 收藏0
  • Android网络编程7源码解析OkHttp前篇[请求网络]

    摘要:异步请求当正在运行的异步请求队列中的数量小于并且正在运行的请求主机数小于时则把请求加载到中并在线程池中执行,否则就再入到中进行缓存等待。通常情况下拦截器用来添加,移除或者转换请求或者响应的头部信息。 前言 学会了OkHttp3的用法后,我们当然有必要来了解下OkHttp3的源码,当然现在网上的文章很多,我仍旧希望我这一系列文章篇是最简洁易懂的。 1.从请求处理开始分析 首先OKHttp...

    blastz 评论0 收藏0
  • Android网络编程3Volley用法解析

    摘要:前言想必很多人都用过,为了建立网络编程的知识体系,是必须要讲的知识点,所以我这里有必要再次介绍一下的使用。简介在年大会上推出了一个新的网络通信框架。在使用前请下载库并放在目录下并到工程中。 前言 Volley想必很多人都用过,为了建立网络编程的知识体系,Volley是必须要讲的知识点,所以我这里有必要再次介绍一下Volley的使用。 1.Volley简介 在2013年Google I/...

    Code4App 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<