Retrofit详细介绍

一.简介

Retrofit是Square公司开发的一款针对Android网络请求的框架,Retrofit2底层基于OkHttp实现。

本文介绍的是Retrofit2.0+版本的使用

一个完整的Get请求

1.创建业务请求接口,具体代码如下:

获取北京天气的接口,get请求,参数是key和cityname,返回数据是WeatherData

public interface WeatherService
{
    @GET("weather/index")
    Call<WeatherData> getWeatherData(@Query("cityname") String cityname,@Query("key") String key);
}

注:@GET注解就表示get请求,@Query表示请求参数,将会以key=value的方式拼接在url后面

Query非必填,如果请求参数非必填,可以传null,如果key非必填可以写成:

Call<WeatherData> call = service.getWeatherData("北京", null);

2.创建一个Retrofit的示例,并完成相应的配置

  Retrofit retrofit = new Retrofit.Builder()
                        .baseUrl("http://v.juhe.cn/")
                        .addConverterFactory(GsonConverterFactory.create())
                        .build();

                WeatherService service = retrofit.create(WeatherService.class);

这里的baseUrl就是网络请求URL相对固定的地址,一般包括请求协议(如Http)、域名或IP地址、端口号等,addConverterFactory方法表示需要用什么转换器来解析返回值,GsonConverterFactory.create()表示调用Gson库来解析json返回值。

3.调用请求方法,并得到Call实例

Call<WeatherData> call = service.getWeatherData("北京", "5c2dd6dd912ba8336889b0325689f809");

注:Call其实在Retrofit中就是行使网络请求并处理返回值的类

4.使用Call实例完成同步或异步请求

同步请求:
WeatherData response = call.execute().body();
注:必须在子线程中执行,否则会报错

异步请求:

  call.enqueue(new Callback<WeatherData>() {
                    @Override
                    public void onResponse(Call<WeatherData> call, Response<WeatherData> response) {

                        mResault.setText(response.body().toString());
                    }
                    @Override
                    public void onFailure(Call<WeatherData> call, Throwable t) {
                        Toast.makeText(MainActivity.this, "请求数据失败", Toast.LENGTH_SHORT).show();
                    }
                });

二.使用

首先需要在build.gradle文件中引入需要的第三包,配置如下:

compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'

Get请求

@Query

Get方法请求参数都会以key=value的方式拼接在url后面,Retrofit提供了两种方式设置请求参数。第一种就是像上文提到的直接在interface中添加@Query注解,还有一种方式是通过Interceptor实现。

public class CustomInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        HttpUrl httpUrl = request.url().newBuilder()
                .addQueryParameter("token", "tokenValue")
                .build();
        request = request.newBuilder().url(httpUrl).build();
        return chain.proceed(request);
    }
}

addQueryParameter就是添加请求参数的具体代码,这种方式比较适用于所有的请求都需要添加的参数,一般现在的网络请求都会添加token作为用户标识,那么这种方式就比较适合。
创建完成自定义的Interceptor后,还需要在Retrofit创建client处完成添加

addInterceptor(new CustomInterceptor())

@QueryMap

如果Query参数比较多,那么可以通过@QueryMap方式将所有的参数集成在一个Map统一传递

public interface WeatherService
{
    @GET("weather/index")
    Call<WeatherData> getWeatherData(@QueryMap Map<String, String> params);
}

调用的时候将所有的参数集合在统一的map中即可

Map<String, String> params = new HashMap<>();
params.put("cityname", "北京");
params.put("key", "5c2dd6dd912ba8336889b0325689f809");
Call<WeatherData> call = service.getWeatherData(params);

Query集合

如果需要添加相同Key值,但是value却有多个的情况,一种方式是添加多个@Query参数,还有一种简便的方式是将所有的value放置在列表中,然后在同一个@Query下完成添加,实例代码如下:

public interface WeatherService
{
    @GET("weather/index")
    Call<WeatherData> getWeatherData(@Query("name") List<String> name);
}

相当于

Map<String, String> params = new HashMap<>();
params.put("name", "北京");
params.put("name", "南京");

@Path

如果请求的相对地址也是需要调用方传递,那么可以使用@Path注解

@GET("weather/{name}")
Call<WeatherData> getWeatherData(@Path("name") String name);
Call<WeatherData> call = WeatherService.getWeatherData("北京");

注:@Path可以用于任何请求方式,包括Get、Post、Put、Delete等

Post请求

@Field

Post请求需要把请求参数放置在请求体中,而非拼接在url后面

public interface NewsDataService
{
    @FormUrlEncoded
    @POST("news/list")
    Call<NewsData> getNewsData(@Field("key") String key);
}

注:@FormUrlEncoded将会自动将请求参数的类型调整为application/x-www-form-urlencoded,假如key传递的参数为”435678”,那么最后得到的请求体就是

key=”435678”

@Field注解将每一个请求参数都存放至请求体中,还可以添加encoded参数,该参数为boolean型,具体的用法为

@Field(value = “key”, encoded = true) String key
encoded参数为true的话,key-value-pair将会被编码,即将中文和特殊字符进行编码转换

@FieldMap

假如有多个请求参数,这个时候就可以用FieldMap

@FormUrlEncoded
@POST("news/list")
Call<NewsData> getNewsData(@FieldMap Map<String, String> params);
Map<String, String> params = new HashMap<>();
params.put("key", "32451");
params.put("name", "北京");
params.put("startindex", 1);
params.put("endindex", 10);
Call<NewsData> getNewsData(params);

@Body

如果Post请求参数有多个,那么统一封装到类中应该会更好,这样维护起来会非常方便

@FormUrlEncoded
@POST("news/list")
Call<NewsData> getNewsData(@Body Entity entity);


public class Entity {
    public String key;
    public String name;
    public int startindex;
    public int endindex;
}

//get和set方法.....

Entity entity = new Entity();
entity.setKey("32451");
entity.setName("北京");
entity.setStartindex(1);
entity.setEndindex(10);

Call<NewsData> getNewsData(entity);

上传文件

单张图片上传并携带参数

public interface UploadFileService
{
    @Multipart
    @POST("uploaad/file")
    Call<ResponseBody>  uploadFile(@Part MultipartBody.Part photo, @Part("description") RequestBody description);
}

上传代码

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

        UploadFileService service = retrofit.create(UploadFileService.class);

        //sd卡下imgs下的一张图片(拍照或者从相册中选择都可以获取到图片文件)
        File file = new File(Environment.getExternalStorageDirectory()+"/imgs", "demo.png");

        //设置Content-Type:application/octet-stream
        RequestBody photoRequestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);

        //设置Content-Disposition:form-data; name="photo"; filename="demo.png"
        MultipartBody.Part photo = MultipartBody.Part.createFormData("photo", file.getName(), photoRequestBody);

        //添加参数description,并且是文本类型
        RequestBody description = RequestBody.create(MediaType.parse("text/plain"), "图片的描述");

        Call<ResponseBody> mCall = service.uploadFile(photo, description);

        mCall.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {

                Toast.makeText(MainActivity.this, "上传成功", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {
                Toast.makeText(MainActivity.this, "上传失败", Toast.LENGTH_SHORT).show();
            }
        });

多张图片上传并携带参数

@Multipart
@POST("uploaad/files")
Call<ResponseBody> uploadfiles(@PartMap Map<String, RequestBody> images, @Part("description") RequestBody description)

上传代码


        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://192.168.1.68:8080/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        UploadFileService service = retrofit.create(UploadFileService.class);
        //sd卡下imgs下的两张图片(拍照或者从相册中选择都可以获取到图片文件)
        File file1= new File(Environment.getExternalStorageDirectory()+"/imgs", "demo1.png");
        File file2 = new File(Environment.getExternalStorageDirectory()+"/imgs", "demo2.png");

        RequestBody photoRequestBody1 = RequestBody.create(MediaType.parse("application/octet-stream"), file1);
        RequestBody photoRequestBody2 = RequestBody.create(MediaType.parse("application/octet-stream"), file2);
        RequestBody description = RequestBody.create(MediaType.parse("text/plain"), "图片的描述");

        Map<String,RequestBody> images = new HashMap<String,RequestBody>();
        //添加file1
        images.put("images\"; filename=\""+file1.getName(), photoRequestBody1);
        //添加file2
        images.put("images\"; filename=\""+file2.getName(), photoRequestBody2);
        //添加图片描述
        images.put("description",  description);

        Call<ResponseBody> call = service.uploadfiles(images, description);
        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response)
            {
                Toast.makeText(MainActivity.this, "上传成功", Toast.LENGTH_SHORT).show();
            }
            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t)
            {
                t.printStackTrace();
                Toast.makeText(MainActivity.this, "上传失败", Toast.LENGTH_SHORT).show();

            }
        });

三.添加自定义的Headers

静态方法

可以添加多个

public interface WeatherService
{
    @Headers({
        "Accept: application/vnd.yourapi.v1.full+json",
        "User-Agent: zhoujian_retrofit"})
    @GET("weather/index")
    Call<WeatherData> getWeatherData(@QueryMap Map<String, String> params);
}

也可以通过Interceptor来定义静态请求头

public class CustomInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request original = chain.request();
        Request request = original.newBuilder()
            .header("User-Agent", "zhoujian_retrofit")
            .header("Accept", "application/vnd.yourapi.v1.full+json")
            .method(original.method(), original.body())
            .build();
        return chain.proceed(request);
    }
}

然后在OkHttp创建Client实例时,添加RequestInterceptor即可

 HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        logging.setLevel(HttpLoggingInterceptor.Level.HEADERS);
                .addInterceptor(new CustomInterceptor())
                .addInterceptor(logging)
                .connectTimeout(DEFAULT_TIMEOUT,
                TimeUnit.SECONDS)
                .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .build();
}

动态方法

@GET("weather/index")
Call<WeatherData> getWeatherData(@Header("Accept") String Accept,
                                 @Header("User-Agent") String zhoujian_retrofit,
                                 @Query("cityname") String cityname, @Query("key") String key);

四.网络请求日志

Retrofit官方提供了一个很方便查看日志的Interceptor,你可以控制你需要的打印信息类型.

首先需要在build.gradle文件中引入logging-interceptor

compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'

同上文提到的CustomInterceptor一样,添加到OkHttpClient创建处即可

HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.HEADERS);

  private static OkHttpClient getClient(){
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        logging.setLevel(HttpLoggingInterceptor.Level.HEADERS);
        return new OkHttpClient.Builder()
                .addInterceptor(logging)
                .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .build();
    }

HttpLoggingInterceptor提供了4中控制打印信息类型的等级,分别是NONE,BASIC,HEADERS,BODY

  • NONE:没有任何日志信息

  • BASIC:打印请求类型,URL,请求体大小,返回值状态以及返回值的大小

  • HEADERS:打印返回请求和返回值的头部信息,请求类型,URL以及返回值状态码

  • BODY:打印请求和返回值的头部和body信息

    最常用的是BODY

logging.setLevel(HttpLoggingInterceptor.Level.HEADERS);

TAG设置为okhttp

     Log.i("okhttp","返回数据==="+mString);

参考文章:

蓝田大营:Retrofit用法详解

肖赛Soaic:Android 你必须了解的网络框架Retrofit2.0

展开阅读全文

没有更多推荐了,返回首页