Retrofit2详解

Retrofit介绍

Retrofit框架: 它是Square公司开发的现在非常流行的网络框架

retrofit2.0它依赖于OkHttp,在这里我们也不需要显示的导入okHttp,在retrofit中已经导入okhttp3
性能好,处理快,使用简单,Retrofit 是安卓上最流行的HTTP Client库之一

github地址

使用步骤:
1、定义一个接口(封装URL地址和数据请求)
2、实例化Retrofit
3、通过Retrofit实例创建接口服务对象
4、接口服务对象调用接口中方法,获得Call对象
5、Call对象执行请求(异步、同步请求)

依赖:

     //retrofit2
    implementation 'com.squareup.retrofit2:retrofit:2.4.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'

入门案例:

测试Url:
https://api.github.com/users/basil2style

  /**
     * 1.get无参请求
     * https://api.github.com/users/basil2style
     */
    @GET("basil2style")
    Call getbasil2style();
public class Main2Activity extends AppCompatActivity {

    public String BASE_URL = "https://api.github.com/users/";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main10);

        //1.创建Retrofit对象
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .build();

        //2.通过Retrofit实例创建接口服务对象
        ApiService apiService = retrofit.create(ApiService.class);

        //3.接口服务对象调用接口中方法,获得Call对象
        Call<ResponseBody> call = apiService.getbasil2style();

        //同步请求
        //Response bodyResponse = call.execute();

        //4.Call对象执行请求(异步、同步请求)
        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                //onResponse方法是运行在主线程也就是UI线程的,所以我们可以在这里直接更新ui
                if (response.isSuccessful()) {
                    try {
                        String string = response.body().string();
                        Log.e("xyh", "onResponse: " + string);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {
                Log.e("xyh", "onFailure: " + t.getMessage());
            }
        });

        //call.cancel(); //取消

    }
}

请求结果:

06-04 09:37:40.604 30233-30233/com.xiaoyehai.retrofit2 E/xyh: onResponse: {"login":"basil2style","id":1285344,"node_id":"MDQ6VXNlcjEyODUzNDQ=","avatar_url":"https://avatars1.githubusercontent.com/u/1285344?v=4","gravatar_id":"","url":"https://api.github.com/users/basil2style","html_url":"https://github.com/basil2style","followers_url":"https://api.github.com/users/basil2style/followers","following_url":"https://api.github.com/users/basil2style/following{/other_user}","gists_url":"https://api.github.com/users/basil2style/gists{/gist_id}","starred_url":"https://api.github.com/users/basil2style/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/basil2style/subscriptions","organizations_url":"https://api.github.com/users/basil2style/orgs","repos_url":"https://api.github.com/users/basil2style/repos","events_url":"https://api.github.com/users/basil2style/events{/privacy}","received_events_url":"https://api.github.com/users/basil2style/received_events","type":"User","site_admin":false,"name":"Basil Alias","company":"https://www.ribitt.io/","blog":"https://www.linkedin.com/in/basilalias92/","location":"Toronto,Canada","email":null,"hireable":true,"bio":"Developer | Marketer | Reader | Cinephile | Entrepreneur","public_repos":90,"public_gists":9,"followers":76,"following":193,"created_at":"2011-12-26T00:17:22Z","updated_at":"2018-05-25T16:30:14Z"}

注解

retrofit通过使用注解来简化请求,大体分为以下几类:
1.用于标注请求方式的注解
2.用于标记请求头的注解
3.用于标记请求参数的注解
4.用于标记请求和响应格式的注解

Retrofit2详解_第1张图片
Retrofit2详解_第2张图片
Retrofit2详解_第3张图片

Retrofit2详解_第4张图片

定义一个接口(封装URL地址和数据请求)

package com.xiaoyehai.retrofit2.api;

import android.app.Activity;

import com.xiaoyehai.retrofit2.bean.BaseEntity;
import com.xiaoyehai.retrofit2.bean.GameInfo;
import com.xiaoyehai.retrofit2.bean.NewsInfo;

import java.util.List;
import java.util.Map;

import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Field;
import retrofit2.http.FieldMap;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.HTTP;
import retrofit2.http.Header;
import retrofit2.http.Headers;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
import retrofit2.http.PartMap;
import retrofit2.http.Path;
import retrofit2.http.Query;
import retrofit2.http.QueryMap;
import retrofit2.http.Streaming;
import retrofit2.http.Url;
import rx.Observable;

/**
 * Created by xiaoyehai on 2018/4/4 0004.
 * 定义一个接口(封装URL地址和数据请求)
 * 

* 1. @FormUrlEncoded:表示请求实体是一个Form表单,每个键值对需要使用@Field注解 * 2. @Multipart:表示请求实体是一个支持文件上传的Form表单,需要配合使用@Part,适用于 有文件 上传的场景 * 3. @Streaming:表示响应体的数据用流的方式返回,适用于返回的数据比较大,该注解在在下载大文件的特别有用 * 4. @HTTP:通用注解,可以替换以上所有的注解,其拥有三个属性:method,path,hasBody *

* 5. @Query 用于Get中指定参数 * 6. @QueryMap 和Query使用类似 * 7. @Path 用于url中的占位符 * 8. @Url 指定请求路径 *

* 9. @Filed 多用于post请求中表单字段,Filed和FieldMap需要FormUrlEncoded结合使用 * 10. @FiledMap 和@Filed作用一致,用于不确定表单参数 * 11. @Body 多用于post请求发送非表单数据,比如想要以post方式传递json格式数据 *

* 12.@Part 用于表单字段,Part和PartMap与Multipart注解结合使用,适合文件上传的情况 * 13.@PartMap 用于表单字段,默认接受的类型是Map,可用于实现多文件上传 *

* 14 @Headers 用于添加固定请求头,可以同时添加多个。通过该注解添加的请求头不会相互覆盖,而是共同存在 * 15 @Header 作为方法的参数传入,用于添加不固定值的Header,该注解会更新已有的请求头 */ public interface ApiService { /** * 1.get无参请求 * https://api.github.com/users/basil2style */ @GET("basil2style") Call<ResponseBody> getbasil2style(); //Call getbasil2style2(); /** * 2.get有参请求 * http://qt.qq.com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724 */ @GET("news/php/varcache_getnews.php") Call<ResponseBody> getNewsInfo(@Query("id") String id, @Query("page") String page, @Query("plat") String plat, @Query("version") String version); /** * 3.gson转换器 * http://qt.qq.com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724 */ @GET("news/php/varcache_getnews.php") Call<BaseEntity<List<NewsInfo>>> getNewsInfo2(@Query("id") String id, @Query("page") String page, @Query("plat") String plat, @Query("version") String version); /** * 4.@Path:用于url中的占位符,所有在网址中的参数(URL的问号前面) * http://qt.qq.com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724 */ @GET("news/{php}/varcache_getnews.php") Call<BaseEntity<List<NewsInfo>>> getNewsInfoPath(@Path("php") String php, @Query("id") String id, @Query("page") String page, @Query("plat") String plat, @Query("version") String version); /** * 5.@QueryMap:参数太多时可以用@QueryMap封装参数,相当于多个@Query * http://qt.qq.com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724 */ @GET("news/php/varcache_getnews.php") Call<BaseEntity<List<NewsInfo>>> getNewsInfo3(@QueryMap Map<String, String> map); /** * 6.post请求; * url:http://zhushou.72g.com/app/game/game_list/ * params:platform=2&page=1 * FieldMap:多个参数时可以使用,类型@QueryMap * FormUrlEncoded:表示请求实体是一个Form表单,每个键值对需要使用@Field注解 * http://qt.qq.com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724 */ @FormUrlEncoded @POST("app/game/game_list/") Call<GameInfo> getGameInfo(@Field("platform") String platform, @Field("page") String page); /** * 7.body注解:上传json格式的数据 * * @param url * @param Body * @return */ @POST() Call<ResponseBody> getNewsInfoByBody(@Url String url, @Body RequestBody Body); /** * 直接传入实体,它会自行转化成Json,这个转化方式是GsonConverterFactory定义的。 * * @param url * @param newsInfo * @return */ @POST("api/{url}/newsList") Call<BaseEntity<NewsInfo>> login(@Path("url") String url, @Body NewsInfo newsInfo); //上传json格式的数据,也可以使用Map集合,加上body注解 @POST("users/update") Observable<BaseResp<UserInfo>> updateUserInfo(@Header("token") String token, @Body Map<String, Object> map); /** * 8.若需要重新定义接口地址,可以使用@Url,将地址以参数的形式传入即可。如 * * @param url * @param map * @return */ @GET Call<List<Activity>> getActivityList(@Url String url, @QueryMap Map<String, String> map); /** * 9.使用@Headers添加多个请求头 * 用于添加固定请求头,可以同时添加多个。通过该注解添加的请求头不会相互覆盖,而是共同存在 * * @param url * @param map * @return */ @Headers({ "User-Agent:android", "apikey:123456789", "Content-Type:application/json", }) @POST() Call<BaseEntity<NewsInfo>> post(@Url String url, @QueryMap Map<String, String> map); /** * 10.@Header注解: * 作为方法的参数传入,用于添加不固定值的Header,该注解会更新已有的请求头 * * @param token * @param activeId * @return */ @GET("mobile/active") Call<BaseEntity<NewsInfo>> get(@Header("token") String token, @Query("id") int activeId); /** * 11.@HTTP注解: * method 表示请求的方法,区分大小写 * path表示路径 * hasBody表示是否有请求体 */ @HTTP(method = "GET", path = "blog/{id}", hasBody = false) Call<ResponseBody> getBlog(@Path("id") int id); /** * 12.Streaming注解:表示响应体的数据用流的方式返回,适用于返回的数据比较大,该注解在在下载大文件的特别有用 */ @Streaming @GET Call<BaseEntity<NewsInfo>> downloadPicture(@Url String fileUrl); ///////上传单张图片////// /** * Multipart:表示请求实体是一个支持文件上传的Form表单,需要配合使用@Part,适用于 有文件 上传的场景 * Part:用于表单字段,Part和PartMap与Multipart注解结合使用,适合文件上传的情况 * PartMap:用于表单字段,默认接受的类型是Map,可用于实现多文件上传 * Part 后面支持三种类型,{@link RequestBody}、{@link okhttp3.MultipartBody.Part} 、任意类型; * * @param file 服务器指定的上传图片的key值 * @return */ @Multipart @POST("upload/upload") Call<BaseEntity<NewsInfo>> upload1(@Part("file" + "\";filename=\"" + "test.png") RequestBody file); @Multipart @POST("xxxxx") Call<BaseEntity<NewsInfo>> upload2(@Part MultipartBody.Part file); //////////上传多张图片///////// /** * @param map * @return */ @Multipart @POST("upload/upload") Call<BaseEntity<NewsInfo>> upload3(@PartMap Map<String, RequestBody> map); @Multipart @POST("upload/upload") Call<BaseEntity<NewsInfo>> upload4(@PartMap Map<String, MultipartBody.Part> map); //////图文混传///// /** * @param params * @param files * @return */ @Multipart @POST("upload/upload") Call<BaseEntity<NewsInfo>> upload5(@FieldMap() Map<String, String> params, @PartMap() Map<String, RequestBody> files); /** * Part 后面支持三种类型,{@link RequestBody}、{@link okhttp3.MultipartBody.Part} 、任意类型; * * @param userName * @param passWord * @param file * @return */ @Multipart @POST("xxxxx") Call<BaseEntity<NewsInfo>> upload6(@Part("username") RequestBody userName, @Part("password") RequestBody passWord, @Part MultipartBody.Part file); /** * 15.rxjava * http://qt.qq.com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724 */ @GET("news/php/varcache_getnews.php") Observable<BaseEntity<List<NewsInfo>>> getNewsInfoByRxJava(@Query("id") String id, @Query("page") String page, @Query("plat") String plat, @Query("version") String version); }

@GET :表明这是get请求
@Query 用于Get中指定参数
@QueryMap 和Query使用类似,用于不确定表单参数

get有参请求案例:

  /**
     * 2.get有参请求
     * http://qt.qq.com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724
     */
    @GET("news/php/varcache_getnews.php")
    Call<ResponseBody> getNewsInfo(@Query("id") String id,
                                   @Query("page") String page,
                                   @Query("plat") String plat,
                                   @Query("version") String version);
/**
 * get有参请求
 * http://qt.qq.com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724
 */
public class Main3Activity extends AppCompatActivity {

    public String BASE_URL = "http://qt.qq.com/php_cgi/";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main3);

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .build();

        retrofit.create(ApiService.class)
                .getNewsInfo("12", "0", "android", "9724")
                .enqueue(new Callback<ResponseBody>() {
                    @Override
                    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                        if (response.isSuccessful()) {
                            try {
                                String string = response.body().string();
                                Log.e("xyh", "onResponse: " + string);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }

                    @Override
                    public void onFailure(Call<ResponseBody> call, Throwable t) {
                        Log.e("xyh", "onFailure: " + t.getMessage());
                    }
                });
    }
}

gosn转换器:直接返回对象

implementation 'com.squareup.retrofit2:converter-gson:2.1.0'
/**
 * gson转换器:直接返回对象
 */
public class Main4Activity extends AppCompatActivity {

    public String BASE_URL = "http://qt.qq.com/php_cgi/";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main4);


        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create()) //gson转换器
                .build();

        retrofit.create(ApiService.class)
                .getNewsInfo2("12", "0", "android", "9724")
                .enqueue(new Callback<BaseEntity<List<NewsInfo>>>() {
                    @Override
                    public void onResponse(Call<BaseEntity<List<NewsInfo>>> call, Response<BaseEntity<List<NewsInfo>>> response) {
                        BaseEntity<List<NewsInfo>> body = response.body();
                        List<NewsInfo> list = body.getList();
                        Log.e("xyh", "onResponse: " + list.size());
                    }

                    @Override
                    public void onFailure(Call<BaseEntity<List<NewsInfo>>> call, Throwable t) {

                    }
                });
    }
}

使用@QueryMap注解:


    /**
     * 5.@QueryMap:参数太多时可以用@QueryMap封装参数,相当于多个@Query
     * http://qt.qq.com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724
     */

    @GET("news/php/varcache_getnews.php")
    Call<BaseEntity<List<NewsInfo>>> getNewsInfo3(@QueryMap Map<String, String> map);
/**
 * QueryMap注解:参数太多时可以用@QueryMap封装参数,相当于多个@Query
 */
public class Main6Activity extends AppCompatActivity {

    public String BASE_URL = "http://qt.qq.com/php_cgi/";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main6);


        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create()) //gson转换器
                .build();

        Map<String, String> map = new HashMap<>();
        map.put("id", "12");
        map.put("page", "0");
        map.put("plat", "android");
        map.put("version", "9724");

        retrofit.create(ApiService.class)
                .getNewsInfo3(map)
                .enqueue(new Callback<BaseEntity<List<NewsInfo>>>() {
                    @Override
                    public void onResponse(Call<BaseEntity<List<NewsInfo>>> call, Response<BaseEntity<List<NewsInfo>>> response) {
                        BaseEntity<List<NewsInfo>> body = response.body();
                        List<NewsInfo> list = body.getList();
                        Log.e("xyh", "onResponse: " + list.size());
                    }

                    @Override
                    public void onFailure(Call<BaseEntity<List<NewsInfo>>> call, Throwable t) {

                    }
                });
    }
}

@Path :用于url中的占位符,所有在网址中的参数(URL的问号前面)

  /**
     * 4.@Path:用于url中的占位符,所有在网址中的参数(URL的问号前面)
     * http://qt.qq.com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724
     */
    @GET("news/{php}/varcache_getnews.php")
    Call<BaseEntity<List<NewsInfo>>> getNewsInfoPath(@Path("php") String php,
                                                     @Query("id") String id,
                                                     @Query("page") String page,
                                                     @Query("plat") String plat,
                                                     @Query("version") String version);
/**
 * @Path注解:用于url中的占位符
 */
public class Main5Activity extends AppCompatActivity {

    public String BASE_URL = "http://qt.qq.com/php_cgi/";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main5);


        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create()) //gson转换器
                .build();

        retrofit.create(ApiService.class)
                .getNewsInfoPath("php", "12", "0", "android", "9724")
                .enqueue(new Callback<BaseEntity<List<NewsInfo>>>() {
                    @Override
                    public void onResponse(Call<BaseEntity<List<NewsInfo>>> call, Response<BaseEntity<List<NewsInfo>>> response) {
                        BaseEntity<List<NewsInfo>> body = response.body();
                        List<NewsInfo> list = body.getList();
                        Log.e("xyh", "onResponse: " + list.size());
                    }

                    @Override
                    public void onFailure(Call<BaseEntity<List<NewsInfo>>> call, Throwable t) {

                    }
                });
    }
}

@Url:指定请求路径

    /**
     * 8.若需要重新定义接口地址,可以使用@Url,将地址以参数的形式传入即可。如
     *
     * @param url
     * @param map
     * @return
     */
    @GET
    Call<List<Activity>> getActivityList(@Url String url, @QueryMap Map<String, String> map);

@Filed: 多用于post请求中表单字段,Filed和FieldMap需要FormUrlEncoded结合使用
@FiledMap :和@Filed作用一致,用于不确定表单参数
@FormUrlEncoded:表示请求实体是一个Form表单,每个键值对需要使用@Field注解
@Body:多用于post请求发送非表单数据,比如想要以post方式传递json格式数据

 /**
     * 6.post请求;
     * 

* FieldMap:多个参数时可以使用,类型@QueryMap * FormUrlEncoded:表示请求实体是一个Form表单,每个键值对需要使用@Field注解 * http://qt.qq.com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724 */ @FormUrlEncoded @POST("news/php/varcache_getnews.php") Call<BaseEntity<List<NewsInfo>>> getNewsInfoByPost(@Field("id") String id, @Field("page") String page, @Field("plat") String plat, @Field("version") String version);

body注解:上传json数据

 /**
     * 7.body注解:上传json格式的数据
     *
     * @param url
     * @param Body
     * @return
     */
    @POST()
    Call<ResponseBody> getNewsInfoByBody(@Url String url, @Body RequestBody Body);
  Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("")
                .addConverterFactory(GsonConverterFactory.create()) //gson转换器
                .build();

        String json = "";
        RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json);
        retrofit.create(ApiService.class)
                .getNewsInfoByBody("", body)
                .enqueue(new Callback<ResponseBody>() {
                    @Override
                    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {

                    }

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

                    }
                });

 /**
     * 直接传入实体,它会自行转化成Json,这个转化方式是GsonConverterFactory定义的。
     *
     * @param url
     * @param newsInfo
     * @return
     */
    @POST("api/{url}/newsList")
    Call<BaseEntity<NewsInfo>> login(@Path("url") String url, @Body NewsInfo newsInfo);

@Headers:添加请求头,用于添加固定请求头,可以同时添加多个。通过该注解添加的请求头不会相互覆盖,而是共同存在。

 /**
     * 9.使用@Headers添加多个请求头
     * 用于添加固定请求头,可以同时添加多个。通过该注解添加的请求头不会相互覆盖,而是共同存在
     *
     * @param url
     * @param map
     * @return
     */
    @Headers({
            "User-Agent:android",
            "apikey:123456789",
            "Content-Type:application/json",
    })
    @POST()
    Call<BaseEntity<NewsInfo>> post(@Url String url, @QueryMap Map<String, String> map);

@Header注解:作为方法的参数传入,用于添加不固定值的Header,该注解会更新已有的请求头。

 /**
     * 10.@Header注解:
     * 作为方法的参数传入,用于添加不固定值的Header,该注解会更新已有的请求头
     *
     * @param token
     * @param activeId
     * @return
     */
    @GET("mobile/active")
    Call<BaseEntity<NewsInfo>> get(@Header("token") String token, @Query("id") int activeId);

@HTTP注解:

 /**
     * 11.@HTTP注解:
     * method 表示请求的方法,区分大小写
     * path表示路径
     * hasBody表示是否有请求体
     */
    @HTTP(method = "GET", path = "blog/{id}", hasBody = false)
    Call<ResponseBody> getBlog(@Path("id") int id);

Streaming注解:表示响应体的数据用流的方式返回,适用于返回的数据比较大,该注解在在下载大文件的特别有用

 /**
     * 12.Streaming注解:表示响应体的数据用流的方式返回,适用于返回的数据比较大,该注解在在下载大文件的特别有用
     */
    @Streaming
    @GET
    Call<BaseEntity<NewsInfo>> downloadPicture(@Url String fileUrl);

上传单张图片案例:

 ///////上传单张图片//////

    /**
     * Multipart:表示请求实体是一个支持文件上传的Form表单,需要配合使用@Part,适用于 有文件 上传的场景
     * Part:用于表单字段,Part和PartMap与Multipart注解结合使用,适合文件上传的情况
     * PartMap:用于表单字段,默认接受的类型是Map,可用于实现多文件上传
     * Part 后面支持三种类型,{@link RequestBody}、{@link okhttp3.MultipartBody.Part} 、任意类型;
     *
     * @param file 服务器指定的上传图片的key值
     * @return
     */

    @Multipart
    @POST("upload/upload")
    Call<BaseEntity<NewsInfo>> upload1(@Part("file" + "\";filename=\"" + "test.png") RequestBody file);

    @Multipart
    @POST("xxxxx")
    Call<BaseEntity<NewsInfo>> upload2(@Part MultipartBody.Part file);


 private void upLoadImage1() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("")
                .addConverterFactory(GsonConverterFactory.create()) //gson转换器
                .build();

        File file = new File("");
        RequestBody requestBody = RequestBody.create(MediaType.parse("image/png"), file);

        retrofit.create(ApiService.class)
                .upload1(requestBody)
                .enqueue(new Callback<BaseEntity<NewsInfo>>() {
                    @Override
                    public void onResponse(Call<BaseEntity<NewsInfo>> call, Response<BaseEntity<NewsInfo>> response) {

                    }

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

                    }
                });
    }

    private void upLoadImage2() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("")
                .addConverterFactory(GsonConverterFactory.create()) //gson转换器
                .build();

        File file = new File("");
        RequestBody photoRequestBody = RequestBody.create(MediaType.parse("image/png"), file);
        MultipartBody.Part photo = MultipartBody.Part.createFormData("上传的key", file.getName(), photoRequestBody);

        retrofit.create(ApiService.class)
                .upload2(photo)
                .enqueue(new Callback<BaseEntity<NewsInfo>>() {
                    @Override
                    public void onResponse(Call<BaseEntity<NewsInfo>> call, Response<BaseEntity<NewsInfo>> response) {

                    }

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

                    }
                });
    }

上传多张图片

  //////////上传多张图片/////////

    /**
     * @param map
     * @return
     */
    @Multipart
    @POST("upload/upload")
    Call<BaseEntity<NewsInfo>> upload3(@PartMap Map<String, RequestBody> map);

    @Multipart
    @POST("upload/upload")
    Call<BaseEntity<NewsInfo>> upload4(@PartMap Map<String, MultipartBody.Part> map);

  private void upLoadImage3() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("")
                .addConverterFactory(GsonConverterFactory.create()) //gson转换器
                .build();

        //图片集合
        List<File> files = new ArrayList<>();

        Map<String, RequestBody> map = new HashMap<>();
        for (int i = 0; i < files.size(); i++) {
            RequestBody requestBody = RequestBody.create(MediaType.parse("image/png"), files.get(i));
            map.put("file" + i + "\";filename=\"" + files.get(i).getName(), requestBody);
        }

        retrofit.create(ApiService.class)
                .upload3(map)
                .enqueue(new Callback<BaseEntity<NewsInfo>>() {
                    @Override
                    public void onResponse(Call<BaseEntity<NewsInfo>> call, Response<BaseEntity<NewsInfo>> response) {

                    }

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

                    }
                });
    }

    private void upLoadImage4() {

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("")
                .addConverterFactory(GsonConverterFactory.create()) //gson转换器
                .build();

        Map<String, MultipartBody.Part> map = new HashMap<>();

        File file1 = new File("");
        RequestBody photoRequestBody = RequestBody.create(MediaType.parse("image/png"), file1);
        MultipartBody.Part photo1 = MultipartBody.Part.createFormData("上传的key1", file1.getName(), photoRequestBody);
        map.put("上传的key1", photo1);

        File file2 = new File("");
        RequestBody photoRequestBody2 = RequestBody.create(MediaType.parse("image/png"), file2);
        MultipartBody.Part photo2 = MultipartBody.Part.createFormData("上传的key2", file2.getName(), photoRequestBody2);
        map.put("上传的key2", photo2);

        retrofit.create(ApiService.class)
                .upload4(map)
                .enqueue(new Callback<BaseEntity<NewsInfo>>() {
                    @Override
                    public void onResponse(Call<BaseEntity<NewsInfo>> call, Response<BaseEntity<NewsInfo>> response) {

                    }

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

                    }
                });
    }

图文混传


   //////图文混传/////

    /**
     * @param params
     * @param files
     * @return
     */
    @Multipart
    @POST("upload/upload")
    Call<BaseEntity<NewsInfo>> upload5(@FieldMap() Map<String, String> params,
                                       @PartMap() Map<String, RequestBody> files);

    /**
     * Part 后面支持三种类型,{@link RequestBody}、{@link okhttp3.MultipartBody.Part} 、任意类型;
     *
     * @param userName
     * @param passWord
     * @param file
     * @return
     */
    @Multipart
    @POST("xxxxx")
    Call<BaseEntity<NewsInfo>> upload6(@Part("username") RequestBody userName,
                                       @Part("password") RequestBody passWord,
                                       @Part MultipartBody.Part file);

  private void upload6() {

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("")
                .addConverterFactory(GsonConverterFactory.create()) //gson转换器
                .build();

        //RequestBody startTowerId = RequestBody.create(MediaType.parse("multipart/form-data"), "xx");

        MediaType textType = MediaType.parse("text/plain");
        RequestBody name = RequestBody.create(textType, "二傻子");
        RequestBody pass = RequestBody.create(textType, "123456");

        File file = new File("");
        RequestBody photoRequestBody = RequestBody.create(MediaType.parse("image/png"), file);
        MultipartBody.Part photo = MultipartBody.Part.createFormData("上传的key", file.getName(), photoRequestBody);

        //multipart/form-data : 需要在表单中进行文件上传时,就需要使用该格式
        //        RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
        //        MultipartBody.Part body = MultipartBody.Part.createFormData("image", file.getName(), requestFile);
        //
        //        String descriptionString = "hello, 这是文件描述";
        //        RequestBody description = RequestBody.create(MediaType.parse("multipart/form-data"), descriptionString);


        retrofit.create(ApiService.class)
                .upload6(name, pass, photo)
                .enqueue(new Callback<BaseEntity<NewsInfo>>() {
                    @Override
                    public void onResponse(Call<BaseEntity<NewsInfo>> call, Response<BaseEntity<NewsInfo>> response) {

                    }

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

                    }
                });
    }
   /**
     * 15.rxjava
     * http://qt.qq.com/php_cgi/news/php/varcache_getnews.php?id=12&page=0&plat=android&version=9724
     */

    @GET("news/php/varcache_getnews.php")
    Observable<BaseEntity<List<NewsInfo>>> getNewsInfoByRxJava(@Query("id") String id,
                                                               @Query("page") String page,
                                                               @Query("plat") String plat,
                                                               @Query("version") String version);

创建Retrofit实例时需要通过Retrofit.Builder,并调用baseUrl方法设置URL。

Retrofit2 的baseUlr 必须以 /(斜线) 结束,不然会抛出一个IllegalArgumentException,所以如果你看到别的教程没有以 / 结束,那么多半是直接从Retrofit 1.X 照搬过来的。

有些特殊情况可以不以/结尾,比如 其实这个 URL https://www.baidu.com?key=value用来作为baseUrl其实是可行的,因为这个URL隐含的路径就是 /(斜线,代表根目录) ,而后面的?key=value在拼装请求时会被丢掉所以写上也没用。之所以 Retrofit 2 在文档上要求必须以 /(斜线) 结尾的要求想必是要消除歧义以及简化规则。

自定义一个转换器,把请求到的数据转换成字符串

   @GET("news/php/varcache_getnews.php")
    Call<String> getNewsInfoStr(@Query("id") String id,
                                   @Query("page") String page,
                                   @Query("plat") String plat,
                                   @Query("version") String version);
/**
 * 自定义返回数据转换器
 */
public class Main11Activity extends AppCompatActivity {

    public String BASE_URL = "http://qt.qq.com/php_cgi/";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main11);

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(new ToStringConverterFactory())
                .build();

        ApiService apiService = retrofit.create(ApiService.class);

        apiService.getNewsInfoStr("12", "0", "android", "9724")
                .enqueue(new Callback<String>() {
                    @Override
                    public void onResponse(Call<String> call, Response<String> response) {
                        String body = response.body();
                    }

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

                    }
                });
    }

    /**
     * 定义一个转换器,把请求到的数据转换成字符串
     */
    private class ToStringConverterFactory extends Converter.Factory {

        private final MediaType MEDIA_TYPE = MediaType.parse("text/plain");

        @Override
        public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
            if (String.class.equals(type)) {
                return new Converter<ResponseBody, String>() {
                    @Override
                    public String convert(ResponseBody value) throws IOException {
                        return value.string();
                    }
                };
            }
            return null;
        }

        @Override
        public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations,
                                                              Annotation[] methodAnnotations, Retrofit retrofit) {
            if (String.class.equals(type)) {
                return new Converter<String, RequestBody>() {
                    @Override
                    public RequestBody convert(String value) throws IOException {
                        return RequestBody.create(MEDIA_TYPE, value);
                    }
                };
            }
            return null;
        }
    }
}

在默认情况下Retrofit只支持将HTTP的响应体转换换为ResponseBody,
这也是为什么我在前面的例子接口的返回值都是 Call,
但如果响应体只是支持转换为ResponseBody的话何必要引入泛型呢,
返回值直接用一个Call就行了嘛,既然支持泛型,那说明泛型参数可以是其它类型的,
而Converter就是Retrofit为我们提供用于将ResponseBody转换为我们想要的类型.

Retrofit封装

/**
 * Created by : xiaoyehai
 * description :Retrofit的封装
 */
public class RetrofitManager {

    private static RetrofitManager mInstance;

    private final Retrofit mRetrofit;

    private RetrofitManager() {
        mRetrofit = new Retrofit.Builder()
                .baseUrl(ConstantUrls.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .client(getOkhttpClient())
                .build();

    }

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

    private OkHttpClient getOkhttpClient() {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        if (BuildConfig.DEBUG) {
            builder.addInterceptor(getHttpLoggingInterceptor()); //日志拦截器
        }
        return builder
                // .addInterceptor(getInterceptor()) //通用拦截器
                .connectTimeout(20, TimeUnit.SECONDS) //设置连接超时时间
                .readTimeout(20, TimeUnit.SECONDS) //设置读取超时时间
                .retryOnConnectionFailure(true)
                .build();

    }


    /**
     * 日志拦截器
     *
     * @return
     */
    private Interceptor getHttpLoggingInterceptor() {
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        return interceptor;
    }

    /**
     * 通用拦截器
     * 根据自己项目的需求添加公共的请求头和请求参数
     *
     * @return
     */
    private Interceptor getInterceptor() {
        Interceptor interceptor = new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request request = chain.request()
                        .newBuilder()
                        .addHeader("token", "xxx")
                        .addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
                        .addHeader("Accept-Encoding", "gzip, deflate")
                        .addHeader("Connection", "keep-alive")
                        .addHeader("Accept", "*/*")
                        .addHeader("Cookie", "add cookies here")
                        .build();
                return chain.proceed(request);
            }
        };
        return interceptor;
    }

    public <T> T create(Class<T> clazz) {
        return mRetrofit.create(clazz);
    }
}

使用retrofit做为网络请求时,解决多个BaseURL切换的问题

项目中使用Retrofit进行请求时,后台接口的域名有多个:

 public static final String BASE_URL_APP = "https://app.tjinzhu.com/";
    public static final String BASE_URL_H5 = "https://res.tjinzhu.com/";
    public static final String BASE_URL_MAT = "https://mat.tjinzhu.com/";
    public static final String BASE_URL_LOGIN = "https://login.tjinzhu.com/";
    public static final String BASE_URL_HOME_MGI = "https://mgi.sitezt.cn/";
    public static final String BASE_URL_HOME_MGAPP = "https://mgapp.sitezt.cn/";

在service代码中添加@Headers():

//默认baseurl
@GET("api/tjz/v1/tao/assets")
Observable<BaseResp<UserAssets>> getUserAssets(@Header("token") String token);

 @Headers({"baseurl:mat"})
@GET("api/tjzadmin/v1/appver/new")
Observable<BaseResp<AppVersionResp>> getAppVersionInfo();

 @Headers({"baseurl:homeapp"})
@GET("api/info/item/getdetailpics")
Observable<List<GoodsDetailPicInfo>> getDetailpics(@Query("itemId") String itemId);

添加okhttpclient拦截器,捕获添加的Headers,然后修改baseURL

/**
 * Cerated by xiaoyehai
 * Create date : 2020/4/3 11:17
 * description : okhttpclient拦截器,捕获添加的Headers,然后修改baseURL
 */
public class BaseUrlInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        //获取request
        Request request = chain.request();
        //从request中获取原有的HttpUrl实例oldHttpUrl
        HttpUrl oldHttpUrl = request.url();
        //获取request的创建者builder
        Request.Builder builder = request.newBuilder();
        //从request中获取headers,通过给定的键url_name
        List<String> headerValues = request.headers("baseurl");
        if (headerValues != null && headerValues.size() > 0) {
            //如果有这个header,先将配置的header删除,因此header仅用作app和okhttp之间使用
            builder.removeHeader("baseurl");
            //匹配获得新的BaseUrl
            String headerValue = headerValues.get(0);
            HttpUrl newBaseUrl = null;
            if ("game".equals(headerValue)) {
                newBaseUrl = HttpUrl.parse(ConstantUrls.BASE_URL_LOGIN);
            } else if ("homeapp".equals(headerValue)) {
                newBaseUrl = HttpUrl.parse(ConstantUrls.BASE_URL_HOME_MGAPP);
            } else if ("homeagi".equals(headerValue)) {
                newBaseUrl = HttpUrl.parse(ConstantUrls.BASE_URL_HOME_MGI);
            } else if ("mat".equals(headerValue)) {
                newBaseUrl = HttpUrl.parse(ConstantUrls.BASE_URL_MAT);
            } else {
                newBaseUrl = oldHttpUrl;
            }
            //重建新的HttpUrl,修改需要修改的url部分
            HttpUrl newFullUrl = oldHttpUrl
                    .newBuilder()
                    .scheme("https")//更换网络协议
                    .host(newBaseUrl.host())//更换主机名
                    .port(newBaseUrl.port())//更换端口
                    //.removePathSegment(0)//移除第一个参数(根据baseurl移除相关参数)
                    //.removePathSegment(1)//移除第二个参数(根据baseurl移除相关参数)
                    //.removePathSegment(2)//移除第三个参数(根据baseurl移除相关参数)
                    .build();


            //重建这个request,通过builder.url(newFullUrl).build();
            // 然后返回一个response至此结束修改
            Log.e("xyh1", "intercept: " + newFullUrl.toString());
            return chain.proceed(builder.url(newFullUrl).build());
        }
        return chain.proceed(request);
    }

}

在okhttpclient中设置

 private OkHttpClient getOkhttpClient() {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        if (BuildConfig.DEBUG) {
            builder.addInterceptor(getHttpLoggingInterceptor()); //日志拦截器
        }
        return builder
               // .addInterceptor(getInterceptor()) //通用拦截器
                .addInterceptor(new BaseUrlInterceptor()) //多个baseurl动态切换
                .connectTimeout(20, TimeUnit.SECONDS) //设置连接超时时间
                .readTimeout(20, TimeUnit.SECONDS) //设置读取超时时间
                .retryOnConnectionFailure(true)
                .build();

    }

使用okhttp拦截器添加公共的参数

/**
 * 公共参数的封装
 */

public class CommonParamsInterceptor implements Interceptor {

    public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");


    private Gson mGson;
    private Context mContext;

    public CommonParamsInterceptor(Context context, Gson gson) {
        this.mContext = context;
        this.mGson = gson;

    }


    @Override
    public Response intercept(Chain chain) throws IOException {

        //拦截请求,获取到原始请求的request
        Request request = chain.request(); // 112.124.22.238:8081/course_api/cniaoplay/featured?p={'page':0}

        try {
            String method = request.method();

            //公共参数
            HashMap<String, Object> commomParamsMap = new HashMap<>();
            //commomParamsMap.put(Constants.IMEI, DeviceUtils.getIMEI(mContext));
            commomParamsMap.put(Constants.IMEI, "23b12e30ec8a2f17");
            commomParamsMap.put(Constants.MODEL, DeviceUtils.getModel());
            commomParamsMap.put(Constants.LANGUAGE, DeviceUtils.getLanguage());
            commomParamsMap.put(Constants.os, DeviceUtils.getBuildVersionIncremental());
            commomParamsMap.put(Constants.RESOLUTION, DensityUtil.getScreenW(mContext) + "*" + DensityUtil.getScreenH(mContext));
            commomParamsMap.put(Constants.SDK, DeviceUtils.getBuildVersionSDK() + "");
            commomParamsMap.put(Constants.DENSITY_SCALE_FACTOR, mContext.getResources().getDisplayMetrics().density + "");

            //token一般写在header
            String token = ACache.get(mContext).getAsString(Constants.TOKEN);
            commomParamsMap.put(Constants.TOKEN, token == null ? "" : token);

            if (method.equals("GET")) {

                HttpUrl httpUrl = request.url();

                HashMap<String, Object> rootMap = new HashMap<>();

                Set<String> paramNames = httpUrl.queryParameterNames();

                for (String key : paramNames) {

                    if (Constants.PARAM.equals(key)) {

                        //{'page':0}
                        String oldParamJson = httpUrl.queryParameter(Constants.PARAM);
                        if (oldParamJson != null) {
                            HashMap<String, Object> p = mGson.fromJson(oldParamJson, HashMap.class); // 原始参数

                            if (p != null) {
                                for (Map.Entry<String, Object> entry : p.entrySet()) {

                                    rootMap.put(entry.getKey(), entry.getValue());
                                }
                            }
                        }
                    } else {
                        rootMap.put(key, httpUrl.queryParameter(key));
                    }
                }

                //{"publicParams":{"resolution":"1080*1776","sdk":"23","la":"zh","densityScaleFactor":"3.0","imei":"A0000059953B34","os":"C92B437","model":"KIW-CL00"}}

                rootMap.put("publicParams", commomParamsMap); // 重新组装
                String newJsonParams = mGson.toJson(rootMap); // {"page":0,"publicParams":{"imei":'xxxxx',"sdk":14,.....}}


                String url = httpUrl.toString();

                int index = url.indexOf("?");
                if (index > 0) {
                    url = url.substring(0, index);
                }

                url = url + "?" + Constants.PARAM + "=" + newJsonParams; //  http://112.124.22.238:8081/course_api/cniaoplay/featured?p= {"page":0,"publicParams":{"imei":'xxxxx',"sdk":14,.....}}

                Log.e("xyh", "intercept: " + url);

                request = request.newBuilder().url(url).build();

            } else if (method.equals("POST")) {

                RequestBody body = request.body();

                HashMap<String, Object> rootMap = new HashMap<>();
                if (body instanceof FormBody) { // form 表单

                    for (int i = 0; i < ((FormBody) body).size(); i++) {

                        rootMap.put(((FormBody) body).encodedName(i), ((FormBody) body).encodedValue(i));
                    }

                } else {  //提交json

                    Buffer buffer = new Buffer();

                    body.writeTo(buffer);

                    String oldJsonParams = buffer.readUtf8();

                    if (!TextUtils.isEmpty(oldJsonParams)) {

                        rootMap = mGson.fromJson(oldJsonParams, HashMap.class); // 原始参数

                        if (rootMap != null) {
                            rootMap.put("publicParams", commomParamsMap); // 重新组装
                            String newJsonParams = mGson.toJson(rootMap); // {"page":0,"publicParams":{"imei":'xxxxx',"sdk":14,.....}}
                            Log.e("xyh", "newJsonParams: " + newJsonParams);
                            request = request.newBuilder().post(RequestBody.create(JSON, newJsonParams)).build();
                        }
                    }

                }

            }
        } catch (JsonSyntaxException e) {
            e.printStackTrace();
        }

        return chain.proceed(request);
    }
}

你可能感兴趣的:(android)