Android框架学习笔记03Retrofit框架

前面我们学习过了Android网络开发中的Okhttp框架和Asynchttpclient框架,这一篇我们学习一个非常强大的框架——Retrofit框架。Retrofit现在最新版本是2.1.0,Retrofit框架是Square公司推出来的,是在Okhttp基础上的进一步封装。

在这里对Okhttp做一些说明:Okhttp是基于HTTP协议封装的,跟HttpClient、HttpUrlConnection的职责是一样的,虽然Okhttp也可以开线程,但它是更多偏向于真正的网络通信。Okhttp是基于NIO和OKio的,这里可能很多人不明白什么是IO、NIO和Okio,这里做一个简单的解释:IO和NIO是Java的概念,IO是一种阻塞式的数据读取,举个例子:我向网络中请求数据,程序就一种在等待,等到网络中返回数据并解析好了,程序才往下执行,这种就是阻塞式的;而NIO则是非阻塞式的,还是刚才那个例子,向网络请求数据,但是程序继续执行,等到数据返回和处理完才通知我,然后通过回调处理,这种就是非阻塞式的,非阻塞式的效率比阻塞式的高。而Okio是Square公司在IO和NIO基础上封装的一个更高效、更简单的数据处理库。

下面我们介绍一下Retrofit这个框架

概述

Retrofit框架是基于Okhttp封装的,在1.0版本的时候是默认采用Okhttp通信,不过在没有添加Okhttp依赖的时候也可以采用自带的网络请求通信,解析数据方式是Gson(一个Google推出的Json数据解析框架,后面我们会介绍),所以我们需要添加Gson的依赖。但是在2.0后的版本中,Retrofit是必须要采用Okhttp请求,还有请求返回数据的解析也不是Gson,需要我们自己手动指定。

Retrofit是通过注解将HTTP请求转化为Java接口,那么,我们了解一下Retrofit提供有哪些注解?

请求方式的注解

  • DELETE:构建一个DELETE请求
  • GET:构建一个GET请求
  • HTTP:可以代替其它的任意一种
  • OPTIONS:构建一个OPTIONS请求
  • PATCH:构建一个PATCH请求
  • POST:构建一个POST请求
  • PUT:构建一个PUT请求

上面列出的都是Retrofit2.0提供的网络请求方式的注解,其中,我们可以通过HTTP这个注解自定义一个请求方式,例如:

 interface Service {
   @HTTP(method = "CUSTOM", path = "custom/endpoint/")
   Call<ResponseBody> customEndpoint();
 }

甚至HTTP注解也可以用于发送一个DELETE请求方式的请求体,例如:

interface Service {
   @HTTP(method = "DELETE", path = "remove/", hasBody = true)
   Call<ResponseBody> deleteObject(@Body RequestBody object);
 }

标记注解

  • FormUrlEncoded注解:与@Field或者@FieldMap一起使用,上传表单数据
  • Multipart注解:与@Part一起使用
  • Streaming注解:用于下载大文件

参数注解

  • Body注解::用于POST请求体,将实例对象根据转换方式转换为对应的json字符串参数,这个转化方式是GsonConverterFactory定义的。

  • Field注解:@Field和@FormURLEncoded一起使用,表示提交一个表单数据给服务器,我们都知道,表单数据是以键值对的形式存在,所以,@Field注解中的参数字符串为“键”,而被@Field标注的参数值为 “值”,例如:

       @FormUrlEncoded
        @POST("/list")
        Call<ResponseBody> example(@Field("name") String... names);
    
  • FieldMap注解:这个注解跟上面的@Field注解差不多,也是和@FieldURLEncoded一起使用。注解的是一个Map,键作为表单数据的"键",值作为表单数据的"值",例如

       @FormUrlEncoded
       @POST("/things")
       Call<ResponseBody> things(@FieldMap Map<String, String> fields);
    
  • Part注解:与@PartMap一样

  • PartMap注解:用于POST文件上传,其中@Part MultipartBody.Part代表文件,@Part(“key”)RequestBody代表参数,需要添加@Multipart表示支持文件上传的表单,Content-Type: multipart/form-data,例如:

       @Multipart
       @POST("/upload")
       Call<ResponseBody> upload(
       @Part("file") RequestBody file,
       @PartMap Map<String, RequestBody> params);
    
  • Query注解:表示查询参数,非常简单

  • QueryMap注解:查询参数,用于GET查询,需要注意的是@QueryMap可以约定是否需要encode

其它注解

  • Path注解:URL占位符,我们看一个例子:

       @GET("/image/{id}")
       Call<ResponseBody> example(@Path("id") int id);
      @GET("/user/{name}")
       Call<ResponseBody> encoded(@Path("name") String name);
      就是将例子中用@Path标注的参数用来替换服务器地址中使用“{}”括起来的字符串,简单说就是,这是一个占位符。
    
      但是注意的是,不能替换URL中的字符串,例如:在http://www.hitomis.com/user.do?action=findUser&username="zuck"中,不能替换action=findUser&username="zuck"路径中的内容
    
  • Url注解:使用全路径复写baseUrl,用于不是同一baseUrl的场景

  • Header注解:设置请求头,可以不设置。设置的话就是@Header里面注解参数为"键",被@Header注解的参数作为"值",例如:

       @GET("/")
       Call<ResponseBody> foo(@Header("Accept-Language") String lang);
    
  • Headers注解:用于修饰方法,用于设置多个Header值

  • HeaderMap注解:与Header类似

好了,Retrofit就简单介绍到这里,下面我们还是学习一下怎么使用这个框架!

使用

Android Studio使用需要在Gradle中添加依赖,建议先去GitHub上获取最新的依赖,在这里给出Retrofit的GitHub地址和官网地址:

GItHub地址:https://github.com/square/retrofit

官网地址:http://square.github.io/retrofit/

compile 'com.squareup.retrofit2:retrofit:2.1.0'

这是Gradle依赖,我们需要用到java/Json转换器工厂,所以需要添加转换器的依赖,可以直接在AS里面搜索最新,包括上面的Retrofit依赖也可以在AS里面搜索

compile 'com.squareup.retrofit2:converter-gson:2.1.0'

如果你想用RxJava支持的适配器的话,需要添加适配器的依赖,也可以直接在AS里面搜索,这里我们没有用到,所以就不需要添加支持RxJava适配器的依赖。

简单描述一下使用Retrofit框架的基本步骤:

  1. 第一步当然就是添加依赖了,Android开发的话基本都是用Gradle的,如果是Java可以用Maven。

  2. 第二步则是定义一些接口,Retrofit是面向接口编程的。

  3. 第三步是创建Retrofit对象,一般是使用Builder模式创建这个对象

  4. 第四步就是使用Retrofit。

上面列出的就是使用Retrofit的基本步骤,下面我们具体使用一下这个强大的网络请求框架

我们说过Retrofit是面向接口编程的,所以使用之前我们需要定义一些我们用到的接口,那么我们先看一下我们定义的接口:

package com.example.frame.common;

import com.example.frame.bean.ContributorsBean;

import java.util.List;

import okhttp3.MultipartBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
import retrofit2.http.Query;
import retrofit2.http.Url;

/**
 * Created by :Huawen
 * Created time : 2016/9/22 16:15
 * Description :
 * Github: https://github.com/Devin1102
 */

public interface RetrofitAPI {
/**
 * 普通的请求
 *
 * @return
 */
@GET("repos/square/retrofit/contributors")
Call<List<ContributorsBean>> getData();

/**
 * 查询
 *
 * @param username
 * @return
 */
@GET("sayHello")
Call<ResponseBody> getQuery(@Query("username") String username);

/**
 * 上传文件
 *
 * @param fileUrl
 * @return
 */
@GET
Call<ResponseBody> downloadFiel(@Url String fileUrl);

/**
 * 上传文件
 *
 * @param description
 * @param file
 * @return
 */
@Multipart
@POST("upload")
Call<ResponseBody> uploadFile(@Part("description") ResponseBody description, @Part MultipartBody.Part file);

/**
 * 上传表单数据
 *
 * @param username
 * @param password
 * @return
 */
@FormUrlEncoded
@POST("sayHello")
Call<ResponseBody> postForm(@Field("username") String username, @Field("password") String password);
}

除了接口之外,我们还将Retrofit对象的获取封装了,看一下封装的代码:

public class RetrofitService {
    protected Retrofit getRetrofit() {
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(UrlUtils.GET_BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build();
    return retrofit;
    }

    public RetrofitAPI createRetrofitAPI() {
        return getRetrofit().create(RetrofitAPI.class);
    }

}

这里也没有什么好解析,我们这里没有添加OkhttpClient端,Retrofit会自动创建一个,当然我们也可以自己手动设置一个。

使用Retrofit实现简单的GET请求

使用Retrofit框架完成GET请求非常简单,我们先看一下代码:

 private void retrofitGet() {
    Call<List<ContributorsBean>> call = mRetrofit.getData();
    call.enqueue(new Callback<List<ContributorsBean>>() {
        @Override
        public void onResponse(Call<List<ContributorsBean>> call, Response<List<ContributorsBean>> response) {
            Log.i(TAG, "onResponse请求成功");
            List<ContributorsBean> beans = response.body();
            for (ContributorsBean contributorsBen : beans) {
                Log.i(TAG, "onResponse" + contributorsBen);
            }
        }

        @Override
        public void onFailure(Call<List<ContributorsBean>> call, Throwable t) {
            Log.i(TAG, "onFailure请求失败");
        }
    });
}

这里对应的接口里面的GET请求,里面我们没有设置参数。这种请求跟Okhttp框架请求方式差不多,使用的也是异步的请求。

Retrofit实现Query参数请求

这个是一个简单的GET请求,不过设置一些参数,上面我们说过@Query和@QueryMap是用于GET请求的设置参数,我们看一下具体的代码:

private void retrofitGetQuery() {
    Call<ResponseBody> call = mRetrofit.getQuery("Devin");
    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
            Log.i(TAG, getResources().getString(R.string.req_success));
        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            Log.i(TAG, getResources().getString(R.string.req_failed));
        }
    });

}

这里我们设置一个参数,与接口一致。

Retrofit实现POST上传表单数据

我们在做实际项目的时候,需要向服务器传递一些表单数据,比如登录、注册功能的时候,Retrofit也给我们提供了实现表单数据上传的方法,我们看一下具体的例子:

private void retrofitPostForm() {
    Call<ResponseBody> call = mRetrofit.postForm("Devin", "Devin");
    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
            Log.i(TAG, getResources().getString(R.string.req_success));
        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            Log.i(TAG, getResources().getString(R.string.req_failed));
        }
    });

}

设置表单数据上传的时候需要在接口中设置@FormUrlEncoded这个方法标记,通过@Field或者@FieldMap实现设置数据。

Retrofit实现文件下载

文件下载是经常使用的网络操作,比如图片和视频下载、APP更新等。我们看一下Retrofit框架提供的文件下载功能:

 private void retrofitDownloadFile() {
    Call<ResponseBody> call = mRetrofit.downloadFiel("http://repo1.maven.org/maven2/com/squareup/retrofit2/retrofit/2.1.0/retrofit-2.1.0.jar");
    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
            InputStream is = response.body().byteStream();
            try {
                FileOutputStream fos = new FileOutputStream(new File("/sdcard/retrofit-2.1.0.jar"));
                byte[] bytes = new byte[1024];
                int len = 0;
                while ((len = is.read(bytes)) != -1) {
                    fos.write(bytes, 0, len);
                }
                fos.flush();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            Log.i(TAG, "onResponse" + getResources().getString(R.string.req_success));
        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            Log.i(TAG, "onFailure" + getResources().getString(R.string.req_failed));
        }
    });
}

在定义文件下载接口的时候,我们设置了@Url参数,这个是复写BaseUrl的,用于我们不使用baseUrl的情况,里面被@Url标志的数据是字符串类型,就是文件下载的URL。然后下载成功之后,以读流的形式将文件保存起来。

Retrofit实现文件上传

文件上传也是经常使用的功能,我们看一下Retrofit框架实现文件上传的功能:

private void retrofitUploadFile() {
    String descriptionString = "UploadFile";
    ResponseBody description = ResponseBody.create(MediaType.parse("multipart/form-data"), descriptionString);

    RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), new File("/sdcard/retrofit-2.1.0.jar"));
    MultipartBody.Part body = MultipartBody.Part.createFormData("picture", "retrofit-2.1.0.jar", requestFile);
    Call<ResponseBody> call = mRetrofit.uploadFile(description, body);
    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
            Log.i(TAG, "onResponse" + getResources().getString(R.string.req_success));
        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            Log.i(TAG, "onFailure" + getResources().getString(R.string.req_failed));
        }
    });
}

文件上传需要在接口中设置@Multipart标记,在接口中我们设置了两个参数:@Part("description") ResponseBody description, @Part MultipartBody.Part file,我们需要构建两个参数,然后实现上传。

以上是我对Retrofit框架的基本理解和具体的使用,可能写法不是很好,如果大家有更好的写法,欢迎大家留言交流。

上面Demo已经上传到GitHub了,当然还有其它框架的使用Demo,Demo的GitHub地址:https://github.com/Devin1102/AndroidFrameDemo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容