Retrofit 2.0 超能实践(一),okHttp完美支持Https传输

时间:2021-02-17 00:43:54

http: //blog.csdn.net/sk719887916/article/details/51597816

Tamic首发


前阵子看到圈子里Retrofit 2.0,RxJava(Android), OkHttp3.3 ,加之支持android和 iOS 的React Native , 火的不要不要的, 2015年新技术一大波来袭 ,看着自己项目还在用HttpClient, AsyncTask的原生开发 感觉自己已成火星人,实在顶不住内心的自卑压力,加之对新技术的追求,入手移动开发新三剑客,虽然目前关于他们的目前介绍的资料一大把,但是自己亲自实践后,发现坑不少,为了能方便其他人安全顺利入坑,今天就先从Retrofit说起,前方高能,准备躲避。

Retrofit 2.0

Retrofit是SQUARE美国一家移动支付公司最近新发布的在Android平台上http访问的开源项目


一 什么Retrofit

官方标语;A type-safe HTTP client for Android and Java

语意很明显一款android安全类型的http客户端, 那么怎么样才算安全?支持https?支持本地线程安全?

发现Rertofit其内部都是支持lambda语法(国内称只链式语法),内部支持okhttp, 并且支持响应式RxJAava,当然jdk1.8 和android studio工具也支持lambda。带着这些疑问 我开始探究一下。

在此之前准备入手资料:

国外博客

https://inthecheesefactory.com/blog/retrofit-2.0/en

官方github

http://square.github.io/retrofit/

二 Retrofit怎么使用

下文之前先给大家看下传统的httpclient(url) + AsyncTask实现的登录功能,这样我们才能发现Retrofit的优雅之处.

不优雅之处请阅读:http://blog.csdn.net/sk719887916/article/details/53613263

传统方式:

  /**
 * Represents an asynchronous login/registration task used to authenticate
 * the user.
 */
public class UserLoginTask extends AsyncTask<Void, Void, Boolean> {

    private final String mEmail;
    private final String mPassword;

    UserLoginTask(String email, String password) {
        mEmail = email;
        mPassword = password;
    }

    @Override
    protected Boolean doInBackground(Void... params) {
        // TODO: attempt authentication against a network service.

        try {
            // Simulate network access.
            String result = "";
            BufferedReader in = null;
            String path ="http://localhost:8080/login/?" +"email =" + mEmail + "& password =" + mPassword;
            URL url =new URL(path);
            HttpURLConnection conn = (HttpURLConnection)url.openConnection();
            conn.setConnectTimeout(5 * 1000);
            conn.setRequestMethod("GET");
            InputStream inStream = conn.getInputStream();
            in = new BufferedReader(
                    new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = in.readLine()) != null)
            {
                result += "\n" + line;
            }

        }catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (ProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        for (String credential : DUMMY_CREDENTIALS) {
            String[] pieces = credential.split(":");
            if (pieces[0].equals(mEmail)) {
                // Account exists, return true if the password matches.
                return pieces[1].equals(mPassword);
            }
        }

        // TODO: register the new account here.
        return true;
    }

    @Override
    protected void onPostExecute(final Boolean success) {
        mAuthTask = null;

        if (success) {
            // do SomeThing
        } else {
            mPasswordView.setError(getString(R.string.error_incorrect_password));
            mPasswordView.requestFocus();
        }
    }

    @Override
    protected void onCancelled() {
        mAuthTask = null;
        showProgress(false);
    }
}

private void enterhome() {
    Intent intent = new Intent(LoginActivity.this, MainListActivity.class);
    startActivity(intent);
}

发现原理也很简单,点击loginbtn开启一个异步线程 在AsyncTask在 doInBackground中访问登录API,在onPostExecute中进行UI更新;也能很简单流畅的解决UI线程请求网络 非UI线程更新UI的问题, 接下来介绍用Retrofit来实现以上相同功能。

2 Rxtrofit

  /**
 * 登录!
 */
private  void getLogin() {
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("http://localhost:8080/")
            .addConverterFactory(GsonConverterFactory.create())
            .build();
    ApiManager apiService = retrofit.create(ApiManager.class);

    Call<LoginResult> call = apiService.getData("lyk", "1234");
   call.enqueue(new Callback<LoginResult>() {
       @Override
       public void onResponse(Call<LoginResult> call, Response<LoginResult> response) {
           if (response.isSuccess()) {
               // do SomeThing
           } else {
              //直接操作UI
           }
       }

       @Override
       public void onFailure(Call<LoginResult> call, Throwable t) {

           // do onFailure代码
       }
   });
}

ApiManager接口

/**
 * Created by LIUYONGKUI on 2016-05-03.
*/
public interface ApiManager {

 @GET("login/")
 Call<LoginResult> getData(@Query("name") String name, @Query("password") String pw);

好了 看了以上代码 或许你已经看到了他的链式优雅高大上的地方了,也许看不懂,也许会懵逼 但没关系我们继续入门。

1 配置gradle

compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'
compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'

com.squareup.retrofit2:converter-gson:2.0.0-beta4 此依赖非必须,只是方便我对http返回的数据进行解析。

2 定义实例化

1》初始化Retrofit

 Retrofit retrofit = new Retrofit.Builder()
          .baseUrl("http://localhost:8080/")
          .addConverterFactory(GsonConverterFactory.create())
           .build();

通过 Retrofit.Builder 来创建一个retrofit客户端,接着添加host url, 然后制定数据解析器,上面依赖的gson就是用在这里做默认数据返回的, 之后通过build()创建出来

Retrofit也支持且内部自带如下格式:

  • Gson: com.squareup.retrofit2:converter-gson
  • Jackson: com.squareup.retrofit2:converter-jackson
  • Moshi: com.squareup.retrofit2:converter-moshi
  • Protobuf: com.squareup.retrofit2:converter-protobuf
  • Wire: com.squareup.retrofit2:converter-wire
  • Simple XML: com.squareup.retrofit2:converter-simplexml
  • Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars

2》编写API

  @GET("login/")
  Call<LoginResult> getData(@Query("name") String name, @Query("password") String pw);

Call是支持Cloneable序列化的 并支持泛型,且此类Retrofit统一返回对象,支持Callback回调,我们可以传入制定的解析Modle,就会在主线程里返回对应的model数据,这里主要用注解@get @post s设置请求方式,后面“login/”是方法Url, @Query(“name”)来设定body的parameters.

3》 调用API

Retrofit支持异步和同步,这里我们用call.enqueue(new Callback来采用异步请求,如果 call.execute() 则采用同步方式

   Call<LoginResult> call = apiService.getData("lyk", "1234");
   call.enqueue(new Callback<LoginResult>() {
       @Override
       public void onResponse(Call<LoginResult> call, Response<LoginResult> response) {

       }

       @Override
       public void onFailure(Call<LoginResult> call, Throwable t) {

       }
   });
}

取消请求

直接用call实例进行cancel即可

  call.cancel();

如果还未理解请阅读参考入门资料:Retrofit 2.0:有史以来最大的改进

三 进阶拓展

通过以上的介绍和案列,我们了解了怎样运用Retrofit请求网络数据,展现数据更新UI,但实际开发中会存在很多问题,很多同学会遇到:Retrofit的内部Log都无法输出 , header怎么加入,请求怎么支持https,包括怎么结合RxJava.? 不用担心,这些Retrofit 2.0 都给我提供了自定义的Interceptor(拦截器),通过不同的Interceptor可以实现不同的自定义请求形式,比如统一加head,参数,加入证书(ssl)等,前提必须结合okhttp来实现 , 通过给OkHttpClient添加Interceptor,然后给Retrofit设置http客户端即可.Retrofit提供了

.client()方法供我们传入自定义的网络客户端,当然默然就是okhttps. 如果无法自动导包 需要我们自己添加对okhttp的依赖

compile ‘com.squareup.okhttp3:okhttp:3.3.1’

OkHttp入门请移步:

~https://github.com/square/okhttp

~ OKHttp源码解析

1 开启Log

 Retrofit retrofit = new Retrofit.Builder().client(new OkHttpClient.Builder()

                     .addNetworkInterceptor(
                                new   HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.HEADERS))        .build())

2 增加头部信息

统一通用header

 new Retrofit.Builder()
           .addConverterFactory(GsonConverterFactory.create())

           .client(new OkHttpClient.Builder()
                   .addInterceptor(new Interceptor() {
                       @Override
                       public Response intercept(Chain chain) throws IOException {
                           Request request = chain.request()
                                   .newBuilder()
                                   .addHeader("mac", "f8:00:ea:10:45")
                                   .addHeader("uuid", "gdeflatfgfg5454545e")
                                   .addHeader("userId", "Fea2405144")
                                   .addHeader("netWork", "wifi")
                                   .build();
                           return chain.proceed(request);
                       }
                   })

                   .build()

当然可以对单一的某个API加入header

@Headers({
"Accept: application/vnd.github.v3.full+json",
"User-Agent: Retrofit-your-App"})‘
@get("users/{username}")
Call<User> getUser(@Path("username") String username);

添加参数见:http://blog.csdn.net/sk719887916/article/details/52189602

3设置代理

   Proxy proxy = new Proxy(Proxy.Type.HTTP,  new InetSocketAddress(proxyHost, proxyPort));
OkHttpClient client = new OkHttpClient.Builder().proxy(proxy).build();

Retrofit.Builder builder = new Retrofit.Builder().client(client);
Retrofit retrofit = builder.build();

4 添加证书Pinning

证书可以在自定义的OkHttpClient加入certificatePinner 实现

OkHttpClient client = new OkHttpClient.Builder()
    .certificatePinner(new CertificatePinner.Builder()
            .add("YOU API.com", "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=")
            .add("YOU API..com", "sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw=")
            .add("YOU API..com", "sha1/blhOM3W9V/bVQhsWAcLYwPU6n24=")
            .add("YOU API..com", "sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c=")
            .build())

5 支持https

加密和普通http客户端请求支持https一样,步骤如下:

1 CertificateFactory 得到Context.getSocketFactory

2 添加证书源文件

3 绑定到okhttpClient

4设置okhttpClient到retrofit中

证书同样可以设置到okhttpclient中,我们可以把证书放到raw路径下

   SLSocketFactory sslSocketFactory =getSSLSocketFactory_Certificate(context,"BKS", R.raw.XXX);

准备证书源文件:

加入证书源文件,我的证书是放在Raw下面的:

Retrofit 2.0 超能实践(一),okHttp完美支持Https传输

绑定证书

protected static SSLSocketFactory getSSLSocketFactory(Context context, int[] certificates) {

    if (context == null) {
        throw new NullPointerException("context == null");
    }

    CertificateFactory certificateFactory;
    try {
        certificateFactory = CertificateFactory.getInstance("X.509");
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(null, null);

        for (int i = 0; i < certificates.length; i++) {
            InputStream certificate = context.getResources().openRawResource(certificates[i]);
            keyStore.setCertificateEntry(String.valueOf(i), certificateFactory.generateCertificate(certificate));

            if (certificate != null) {
                certificate.close();
            }
        }
        SSLContext sslContext = SSLContext.getInstance("TLS");
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(keyStore);
        sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
       return sslContext.getSocketFactory();

指定支持的host

/**

* set HostnameVerifier

* {@link HostnameVerifier}

*/

protected static HostnameVerifier getHostnameVerifier(final String[] hostUrls) {

        HostnameVerifier TRUSTED_VERIFIER = new HostnameVerifier() {

            public boolean verify(String hostname, SSLSession session) {
                boolean ret = false;
                for (String host : hostUrls) {
                    if (host.equalsIgnoreCase(hostname)) {
                        ret = true;
                    }
                }
                return ret;
            }
        };

    return TRUSTED_VERIFIER;
}

设置setSocketFactory

okhttpBuilder.socketFactory(HttpsFactroy.getSSLSocketFactory(context,   certificates));

certificates 是你raw下证书源ID, int[] certificates = {R.raw.myssl}

设置setNameVerifie

 okhttpBuilder.hostnameVerifier(HttpsFactroy.getHostnameVerifier(hosts));

hosts是你的host数据 列如 String hosts[]`= {“https//:aaaa,com”, “https//:bbb.com”}

实现自定义 添加到Retrofit

        okHttpClient = okhttpBuilder.build();

        retrofit = new Retrofit.Builder()
            .client(okHttpClient)
            .build();

总结

看了以上的知识点你发现Retrofit同等支持RxJava,通过以下Call适配模式.就可以关联RxJava

 retrofit .addCallAdapterFactory(RxJavaCallAdapterFactory.create())

关于 Retrofit+ RxJava的案列,以及实际遇到的坑下篇再介绍。RxJava也是一款强大的多线程通讯利器,让你的应用开发中无时无刻,随心所欲进行多线程响应式编程开发。


参考文章:

Retrofit系列:

强烈推荐:


欢迎关注个人公众号:

Retrofit 2.0 超能实践(一),okHttp完美支持Https传输

Retrofit 2.0 超能实践(一),okHttp完美支持Https传输的更多相关文章

  1. Retrofit 2&period;0 超能实践(三),轻松实现文件&sol;多图片上传&sol;Json字符串

    文:http://blog.csdn.net/sk719887916/article/details/51755427 Tamic 简书&csdn同步 通过前两篇姿势的入门 Retrofit ...

  2. Retrofit 2&period;0 超能实践,完美支持Https传输

    http://blog.csdn.NET/sk719887916/article/details/51597816 前阵子看到圈子里Retrofit 2.0,RxJava(Android), OkHt ...

  3. Retrofit 2&period;0 超能实践(四),完成大文件断点下载

    作者:码小白 文/CSDN 博客 本文出自:http://blog.csdn.net/sk719887916/article/details/51988507 码小白 通过前几篇系统的介绍和综合运用, ...

  4. Retrofit 2&period;0 轻松实现多文件&sol;图片上传&sol;Json字符串&sol;表单

    如果嫌麻烦直接可以用我封装好的库:Novate: https://github.com/Tamicer/Novate 通过对Retrofit2.0的前两篇的基础入门和案例实践,掌握了怎么样使用Retr ...

  5. Android Okhttp完美同步持久Cookie实现免登录

    通过对Retrofit2.0的<Retrofit 2.0 超能实践,完美支持Https传输>基础入门和案例实践,掌握了怎么样使用Retrofit访问网络,加入自定义header,包括加入S ...

  6. Retrofit 2&period;0基于OKHttp更高效更快的网络框架 以及自定义转换器

    时间关系,本文就 Retrofit 2.0的简单使用 做讲解  至于原理以后有空再去分析 项目全面.简单.易懂  地址: 关于Retrofit 2.0的简单使用如下:  https://gitee.c ...

  7. Android Retrofit 2&period;0 使用-补充篇

    推荐阅读,猛戳: 1.Android MVP 实例 2.Android Retrofit 2.0使用 3.RxJava 4.RxBus 5.Android MVP+Retrofit+RxJava实践小 ...

  8. Retrofit 2&period;0 使用详细教程

    文章来自:https://blog.csdn.net/carson_ho/article/details/73732076 前言 在Andrroid开发中,网络请求十分常用 而在Android网络请求 ...

  9. 这是一份很详细的 Retrofit 2&period;0 使用教程(含实例讲解)(转)

    前言 在Andrroid开发中,网络请求十分常用 而在Android网络请求库中,Retrofit是当下最热的一个网络请求库 今天,我将献上一份非常详细Retrofit v2.0的使用教程,希望你们会 ...

随机推荐

  1. 一些比较实用的css片段

    新看了一个帖子,里面好多实用的css代码块,可拿出来当做功能库.先附上该文地址http://segmentfault.com/a/1190000002773955 里面的内容很多我挑了几个经过我验证的 ...

  2. 错误:The superclass &quot&semi;javax&period;servlet&period;http&period;HttpServlet&quot&semi; was not found on the Java Build Path

    我们在利用Eclipse进行Java web开发时,可能会出现这样的错误:The superclass javax.servlet.http.HttpServlet was not found on ...

  3. img 是内联元素

    图片是内联元素 ,同时是内联替换元素(替换元素是能设置宽和高的) 取消图片的magin display:block;(一般初始化标签中会把图片设置成块状) replaced element <i ...

  4. 最流行的android组件大全

    目录 [−] 工具和教程 UI组件 类库 游戏引擎 Android HTML5应用 Android 是目前最流行的移动操作系统(还需要加之一吗?). 随着新版本的不断发布, Android的功能也日益 ...

  5. django-查询语句&lpar;一&rpar;

    1.model 假设我们的model如下: 某个JobType下有很多Job. class JobType(models.Model): name = models.CharField(max_len ...

  6. 两个无序数组分别叫A和B,长度分别是m和n,求中位数,要求时间复杂度O&lpar;m&plus;n&rpar;,空间复杂度O&lpar;1&rpar; 。

    #include <iostream> using namespace std; /*函数作用:取待排序序列中low.mid.high三个位置上数据,选取他们中间的那个数据作为枢轴*/ i ...

  7. Java IO--字符流--BufferedReader和BufferedWriter

    从昨天开始没事情干时,决定梳理梳理Java IO流,因为觉得太混乱这个东西,妈的,咋就这么多类型,想累死谁啊,这里并不是埋怨创造者,而是气自己看着看着老跑偏,实在看不进去,太多了,想睡觉,所以现在决定 ...

  8. &lbrack;MCM&rsqb; K-mean聚类与DBSCAN聚类 Python

    import matplotlib.pyplot as plt X=[56.70466067,56.70466067,56.70466067,56.70466067,56.70466067,58.03 ...

  9. 使用WebBrowser控件播放Flash网页相关问题解决方法(转)

    就是写一个类继承WebBrower控件,重写 protected   override   void   WndProc(ref   System.Windows.Forms.Message   m) ...

  10. AOP 实现自定义注解

    1.自定义注解2.编写 AOP3.测试 1.自定义注解 package com.base.yun.spring.aop; import java.lang.annotation.Documented; ...