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

传统方式:

  1. /**
  2. * Represents an asynchronous login/registration task used to authenticate
  3. * the user.
  4. */
  5. public class UserLoginTask extends AsyncTask<Void, Void, Boolean> {
  6. private final String mEmail;
  7. private final String mPassword;
  8. UserLoginTask(String email, String password) {
  9. mEmail = email;
  10. mPassword = password;
  11. }
  12. @Override
  13. protected Boolean doInBackground(Void... params) {
  14. // TODO: attempt authentication against a network service.
  15. try {
  16. // Simulate network access.
  17. String result = "";
  18. BufferedReader in = null;
  19. String path ="http://localhost:8080/login/?" +"email =" + mEmail + "& password =" + mPassword;
  20. URL url =new URL(path);
  21. HttpURLConnection conn = (HttpURLConnection)url.openConnection();
  22. conn.setConnectTimeout(5 * 1000);
  23. conn.setRequestMethod("GET");
  24. InputStream inStream = conn.getInputStream();
  25. in = new BufferedReader(
  26. new InputStreamReader(conn.getInputStream()));
  27. String line;
  28. while ((line = in.readLine()) != null)
  29. {
  30. result += "\n" + line;
  31. }
  32. }catch (MalformedURLException e) {
  33. e.printStackTrace();
  34. } catch (UnsupportedEncodingException e) {
  35. e.printStackTrace();
  36. } catch (ProtocolException e) {
  37. e.printStackTrace();
  38. } catch (IOException e) {
  39. e.printStackTrace();
  40. }
  41. for (String credential : DUMMY_CREDENTIALS) {
  42. String[] pieces = credential.split(":");
  43. if (pieces[0].equals(mEmail)) {
  44. // Account exists, return true if the password matches.
  45. return pieces[1].equals(mPassword);
  46. }
  47. }
  48. // TODO: register the new account here.
  49. return true;
  50. }
  51. @Override
  52. protected void onPostExecute(final Boolean success) {
  53. mAuthTask = null;
  54. if (success) {
  55. // do SomeThing
  56. } else {
  57. mPasswordView.setError(getString(R.string.error_incorrect_password));
  58. mPasswordView.requestFocus();
  59. }
  60. }
  61. @Override
  62. protected void onCancelled() {
  63. mAuthTask = null;
  64. showProgress(false);
  65. }
  66. }
  67. private void enterhome() {
  68. Intent intent = new Intent(LoginActivity.this, MainListActivity.class);
  69. startActivity(intent);
  70. }

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

2 Rxtrofit

  1. /**
  2. * 登录!
  3. */
  4. private void getLogin() {
  5. Retrofit retrofit = new Retrofit.Builder()
  6. .baseUrl("http://localhost:8080/")
  7. .addConverterFactory(GsonConverterFactory.create())
  8. .build();
  9. ApiManager apiService = retrofit.create(ApiManager.class);
  10. Call<LoginResult> call = apiService.getData("lyk", "1234");
  11. call.enqueue(new Callback<LoginResult>() {
  12. @Override
  13. public void onResponse(Call<LoginResult> call, Response<LoginResult> response) {
  14. if (response.isSuccess()) {
  15. // do SomeThing
  16. } else {
  17. //直接操作UI
  18. }
  19. }
  20. @Override
  21. public void onFailure(Call<LoginResult> call, Throwable t) {
  22. // do onFailure代码
  23. }
  24. });
  25. }

ApiManager接口

  1. /**
  2. * Created by LIUYONGKUI on 2016-05-03.
  3. */
  4. public interface ApiManager {
  5. @GET("login/")
  6. Call<LoginResult> getData(@Query("name") String name, @Query("password") String pw);

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

1 配置gradle

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

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

2 定义实例化

1》初始化Retrofit

  1. Retrofit retrofit = new Retrofit.Builder()
  2. .baseUrl("http://localhost:8080/")
  3. .addConverterFactory(GsonConverterFactory.create())
  4. .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

  1. @GET("login/")
  2. 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() 则采用同步方式

  1. Call<LoginResult> call = apiService.getData("lyk", "1234");
  2. call.enqueue(new Callback<LoginResult>() {
  3. @Override
  4. public void onResponse(Call<LoginResult> call, Response<LoginResult> response) {
  5. }
  6. @Override
  7. public void onFailure(Call<LoginResult> call, Throwable t) {
  8. }
  9. });
  10. }

取消请求

直接用call实例进行cancel即可

  1. 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

  1. Retrofit retrofit = new Retrofit.Builder().client(new OkHttpClient.Builder()
  2. .addNetworkInterceptor(
  3. new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.HEADERS)) .build())

2 增加头部信息

统一通用header

  1. new Retrofit.Builder()
  2. .addConverterFactory(GsonConverterFactory.create())
  3. .client(new OkHttpClient.Builder()
  4. .addInterceptor(new Interceptor() {
  5. @Override
  6. public Response intercept(Chain chain) throws IOException {
  7. Request request = chain.request()
  8. .newBuilder()
  9. .addHeader("mac", "f8:00:ea:10:45")
  10. .addHeader("uuid", "gdeflatfgfg5454545e")
  11. .addHeader("userId", "Fea2405144")
  12. .addHeader("netWork", "wifi")
  13. .build();
  14. return chain.proceed(request);
  15. }
  16. })
  17. .build()

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

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

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

3设置代理

  1. Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));
  2. OkHttpClient client = new OkHttpClient.Builder().proxy(proxy).build();
  3. Retrofit.Builder builder = new Retrofit.Builder().client(client);
  4. Retrofit retrofit = builder.build();

4 添加证书Pinning

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

  1. OkHttpClient client = new OkHttpClient.Builder()
  2. .certificatePinner(new CertificatePinner.Builder()
  3. .add("YOU API.com", "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=")
  4. .add("YOU API..com", "sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw=")
  5. .add("YOU API..com", "sha1/blhOM3W9V/bVQhsWAcLYwPU6n24=")
  6. .add("YOU API..com", "sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c=")
  7. .build())

5 支持https

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

1 CertificateFactory 得到Context.getSocketFactory

2 添加证书源文件

3 绑定到okhttpClient

4设置okhttpClient到retrofit中

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

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

准备证书源文件:

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

绑定证书

  1. protected static SSLSocketFactory getSSLSocketFactory(Context context, int[] certificates) {
  2. if (context == null) {
  3. throw new NullPointerException("context == null");
  4. }
  5. CertificateFactory certificateFactory;
  6. try {
  7. certificateFactory = CertificateFactory.getInstance("X.509");
  8. KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
  9. keyStore.load(null, null);
  10. for (int i = 0; i < certificates.length; i++) {
  11. InputStream certificate = context.getResources().openRawResource(certificates[i]);
  12. keyStore.setCertificateEntry(String.valueOf(i), certificateFactory.generateCertificate(certificate));
  13. if (certificate != null) {
  14. certificate.close();
  15. }
  16. }
  17. SSLContext sslContext = SSLContext.getInstance("TLS");
  18. TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
  19. trustManagerFactory.init(keyStore);
  20. sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
  21. return sslContext.getSocketFactory();

指定支持的host

/**

* set HostnameVerifier

* {@link HostnameVerifier}

*/

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

  1. HostnameVerifier TRUSTED_VERIFIER = new HostnameVerifier() {
  2. public boolean verify(String hostname, SSLSession session) {
  3. boolean ret = false;
  4. for (String host : hostUrls) {
  5. if (host.equalsIgnoreCase(hostname)) {
  6. ret = true;
  7. }
  8. }
  9. return ret;
  10. }
  11. };
  12. return TRUSTED_VERIFIER;
  13. }

设置setSocketFactory

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

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

设置setNameVerifie

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

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

实现自定义 添加到Retrofit

  1. okHttpClient = okhttpBuilder.build();
  2. retrofit = new Retrofit.Builder()
  3. .client(okHttpClient)
  4. .build();

总结

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

  1. retrofit .addCallAdapterFactory(RxJavaCallAdapterFactory.create())

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


参考文章:

Retrofit系列:

强烈推荐:


欢迎关注个人公众号:

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

  1. Retrofit 2.0 超能实践(三),轻松实现文件/多图片上传/Json字符串

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

  2. Retrofit 2.0 超能实践,完美支持Https传输

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

  3. Retrofit 2.0 超能实践(四),完成大文件断点下载

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

  4. Retrofit 2.0 轻松实现多文件/图片上传/Json字符串/表单

    如果嫌麻烦直接可以用我封装好的库: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.0基于OKHttp更高效更快的网络框架 以及自定义转换器

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

  7. Android Retrofit 2.0 使用-补充篇

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

  8. Retrofit 2.0 使用详细教程

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

  9. 这是一份很详细的 Retrofit 2.0 使用教程(含实例讲解)(转)

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

随机推荐

  1. codeforces 809E Surprise me!

    Tired of boring dates, Leha and Noora decided to play a game. Leha found a tree with n vertices numb ...

  2. 51nod 1770 数数字

    1770 数数字 基准时间限制:1 秒 空间限制:262144 KB 分值: 20 难度:3级算法题  收藏  关注 统计一下 aaa ⋯ aaan个a × b 的结果里面 ...

  3. hdu 2896 病毒侵袭 AC自动机(查找包含哪些子串)

    病毒侵袭 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  4. hdu 1828 线段树扫描线(周长)

    Picture Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Sub ...

  5. [bzoj4850][Jsoi2016]灯塔

    来自FallDream的博客,未经允许,请勿转载,谢谢. JSOI的国境线上有N一座连续的山峰,其中第ii座的高度是hi??.为了简单起见,我们认为这N座山峰排成了连续一条 直线.如果在第ii座山峰上 ...

  6. spine - unity3D(摘自博主softimagewht)

    摘自:(博主 http://www.cnblogs.com/softimagewht/p/4149118.html) //skeletonDataSkeletonAnimation skeletonA ...

  7. Linux下双网卡Firewalld的配置流程

    实验室拟态存储的项目需要通过LVS-NAT模式通过LVS服务器来区隔内外网的服务,所以安全防护的重心则落在了LVS服务器之上.笔者最终选择通过firewalld放行端口的方式来实现需求,由于firew ...

  8. SVN冲突解决

    问题一.执行SVN commit时候,会生成除正常文件之外.mine..r3439 ..r3368的三个文件 .mine:是自己要提交的版本 .r3439:在别人之前提交的版本 .r3368:初始版本 ...

  9. Python处理正则表达式超时的办法

    最近在项目中遇到一个问题,就是需要采用正则匹配一些疑似暗链和挂马的HTML代码,而公司的老大给的正则表达式有的地方写的不够严谨,导致在匹配的时候发生卡死的现象,而后面的逻辑自然无法执行了.虽然用正则表 ...

  10. 使用ffmpeg转码时遇到aac报错

    今天尝试用ffmpeg转一个视频的格式,结果报出这个错误: The encoder 'aac' is experimental but experimental codecs are not enab ...