Retrofit学习记录

2021-04-16  本文已影响0人  打工崽

Retrofit是Square公司开发的一款针对Android网络请求的框架,底层是基于OkHttp实现的,与OkHttp不同的是,它更多的使用运行时注解的方式提供功能

使用流程

1. 使用前的准备工作

配置依赖

implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'

加入网络权限

<uses-permission android:name="android.permission.INTERNET"/>

2. Retrofit注解的分类

Retrofit的注解分三大类:

  1. HTTP请求方法注解
    1.1 GET
    1.2 POST
    1.3 PUT
    1.4 DELETE
    1.5 HEAD
    1.6 PATCH
    1.7 OPTIONS
    1.8 HTTP

  2. 标记类注解
    2.1 FormUrlEncoded:标明这是一个表单请求
    2.2 Multipart:Multipart注解表示允许多个@Part
    2.3 Streaming:代表响应数据以流形式返回。若不使用,则默认吧全部数据加载到内存,下载大文件时需要加上此注解

  3. 参数类注解
    有Header、Headers、Body、Path、Field、FieldMap、Part、PartMap、Query、QueryMap等


3. 编写实体类

Ip

public class Ip {
    private String ip;
    public Ip(String ip) {
        this.ip = ip;
    }
}

IpData

public class IpData {
        private String country;

        private String country_id;

        private String area;

        private String area_id;

        private String region;

        private String region_id;

        private String city;

        private String city_id;

        private String county;

        private String county_id;

        private String isp;

        private String isp_id;

        private String ip;

        public void setCountry(String country) {
            this.country = country;
        }

        public String getCountry() {
            return this.country;
        }

        public void setCountry_id(String country_id) {
            this.country_id = country_id;
        }

        public String getCountry_id() {
            return this.country_id;
        }

        public void setArea(String area) {
            this.area = area;
        }

        public String getArea() {
            return this.area;
        }

        public void setArea_id(String area_id) {
            this.area_id = area_id;
        }

        public String getArea_id() {
            return this.area_id;
        }

        public void setRegion(String region) {
            this.region = region;
        }

        public String getRegion() {
            return this.region;
        }

        public void setRegion_id(String region_id) {
            this.region_id = region_id;
        }

        public String getRegion_id() {
            return this.region_id;
        }

        public void setCity(String city) {
            this.city = city;
        }

        public String getCity() {
            return this.city;
        }

        public void setCity_id(String city_id) {
            this.city_id = city_id;
        }

        public String getCity_id() {
            return this.city_id;
        }

        public void setCounty(String county) {
            this.county = county;
        }

        public String getCounty() {
            return this.county;
        }

        public void setCounty_id(String county_id) {
            this.county_id = county_id;
        }

        public String getCounty_id() {
            return this.county_id;
        }

        public void setIsp(String isp) {
            this.isp = isp;
        }

        public String getIsp() {
            return this.isp;
        }

        public void setIsp_id(String isp_id) {
            this.isp_id = isp_id;
        }

        public String getIsp_id() {
            return this.isp_id;
        }

        public void setIp(String ip) {
            this.ip = ip;
        }

        public String getIp() {
            return this.ip;
        }

    }

IpModel

public class IpModel {
    private int code;
    private IpData data;
    public void setCode(int code) {
        this.code = code;
    }
    public int getCode() {
        return this.code;
    }
    public void setData(IpData data) {
        this.data = data;
    }
    public IpData getData() {
        return this.data;
    }
}

4. 创建布局

res/values/dimen

<resources>
    <!-- Default screen margins, per the Android Design guidelines. -->
    <dimen name="activity_horizontal_margin">16dp</dimen>
    <dimen name="activity_vertical_margin">16dp</dimen>
</resources>

activity_main

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/bt_request"
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:layout_centerInParent="true"
        android:text="请求网络"
        android:gravity="center"
        android:background="@android:color/darker_gray"/>
</RelativeLayout>

简单写个按钮叭


5. GET请求网络

先使用GET方式来访问网络,创建接口IpService

IpService

public interface IpService {
    @GET("getIpInfo.php?ip=59.108.54.37")
    Call<IpModel> getIpMsg();
}

Retrofit提供的请求方式注解有@GET和@POST等,分别代表GET请求和POST请求,这里使用GET。另外定义了getIpMsg()方法返回Call<IpModel>类型参数,接下来创建Retrofit,创建接口文件

MainActivity

public class MainActivity extends AppCompatActivity {

    private Button bt_request;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bt_request = (Button) findViewById(R.id.bt_request);
        bt_request.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                getIpInformation("59.108.54.37");
            }
        });
    }

    private void getIpInformation(String ip) {
        String url = "https://ip.taobao.com/service/";
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(url)
                //增加返回值为String的支持
                .addConverterFactory(GsonConverterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        IpService ipService = retrofit.create(IpService.class);
        Call<IpModel> call = ipService.getIpMsg();
        call.enqueue(new Callback<IpModel>() {
            @Override
            public void onResponse(Call<IpModel> call, Response<IpModel> response) {
                String country = response.body().getData().getCountry();
                Toast.makeText(getApplicationContext(), country, Toast.LENGTH_SHORT).show();
            }

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

            }
        });
    }
}

一步一步分解来看

Retrofit是通过建造者模式建造出来的,请求URL是拼接而成的,是由baseUrl传入的URL加入请求网络接口的@GET("getIpInfo.php?ip=59.108.54.37")中的URL拼接而来的,如下

String url = "http://ip.taobao.com/service/";
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(url)
                //增加返回值为String的支持
                .addConverterFactory(GsonConverterFactory.create())
                .build();

接下来用Retrofit的动态代理获取之前定义的接口,并调用该接口定义的getIpMsg()方法得到Call对象,如下

IpService ipService = retrofit.create(IpService.class);
Call<IpModel> call = ipService.getIpMsg();

最后异步请求网络,回调Callback是运行在UI线程的,得到返回的Response后将返回数据的country用Toast显示出来,同步请求的话就调用execute()方法,中断网络请求就调用cancel()方法即可,如下

call.enqueue(new Callback<IpModel>() {
            @Override
            public void onResponse(Call<IpModel> call, Response<IpModel> response) {
                String country = response.body().getData().getCountry();
                Toast.makeText(getApplicationContext(), country, Toast.LENGTH_SHORT).show();
            }

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

            }
        });

5.1 动态配置URL地址:@Path

Retrofit提供很多请求参数的注解,使得请求网络时更加便捷,其中,@Path用来动态配置URL地址

IpServiceForPath

public interface IpServiceForPath {
    @GET("{path}/getIpInfo.php?ip=59.108.54.37")
    Call<IpModel> getIpMsg(@Path("path") String path);
}

GET注解中包含了{path},对应着@Path注解中的"path",而用来替换{path}的正是需要传入的"StringPath"的值。在getIpMsg()方法中添加path。因此,上面GET请求的方法修改如下

private void getIpInformationForPath(String path) {
        //GET请求网络,动态配置Path
        String url = "https://ip.taobao.com/service/";
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(url)
                //增加返回值为String的支持
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        IpServiceForPath ipService = retrofit.create(IpServiceForPath.class);
        Call<IpModel> call = ipService.getIpMsg(path);//这里相比上面加入了参数
        call.enqueue(new Callback<IpModel>() {
            @Override
            public void onResponse(Call<IpModel> call, Response<IpModel> response) {
                String country = response.body().getData().getCountry();
                Toast.makeText(getApplicationContext(), country, Toast.LENGTH_SHORT).show();
            }

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

            }
        });
    }

5.2 动态指定查询条件:@Query

之前的例子就是为了查询ip的地址位置,每次查询更换不同的ip即可,可以用@Query来动态指定ip地址,编写如下接口

IpServiceForQuery

public interface IpServiceForQuery{
    @GET("getIpInfo.php")
    Call<IpModel> getIpMsg(@Query("ip")String ip);
}

请求网络时,传入想要查询的IP即可

getIpInformationForQuery

private void getIpInformationForQuery(String ip) {
        String url = "https://ip.taobao.com/service/";
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(url)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        IpServiceForQuery ipService = retrofit.create(IpServiceForQuery.class);
        Call<IpModel> call = ipService.getIpMsg(ip);
        call.enqueue(new Callback<IpModel>() {
            @Override
            public void onResponse(Call<IpModel> call, Response<IpModel> response) {
                String country = response.body().getData().getCountry();
                Toast.makeText(getApplicationContext(), country, Toast.LENGTH_SHORT).show();
            }

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

            }
        });
    }

5.3 动态指定查询条件组:@QueryMap

网络请求中一般为了更精确的查找我们需要的数据,需要传入很多查询参数,如果使用@Query会比较麻烦,这时我们可以采用@QueryMap,将所有参数集成在一个Map中统一传递

IpServiceForQueryMap

public interface IpServiceForQueryMap {
    @GET("getIpInfo.php")
    Call<IpModel> getIpMsg(@QueryMap Map<String, String> options);
}

6. POST请求访问网络

6.1 传输数据类型为键值对:@Field

传输数据类型为键值对是我们最常用的POST请求数据类型,淘宝IP库支持数据类型为键值对的POST请求,编写接口如下

IpServiceForPost

public interface IpServiceForPost {
    @FormUrlEncoded
    @POST("getIpInfo.php")
    Call<IpModel> getIpMsg(@Field("ip") String first);
}

首先用@FormUrlEncoded注解来标明这是一个表单请求,然后在getIpMsg()方法中使用@Field注解来标识对应的String类型数据的键,从而组成一组键值对进行传递,请求网络代码如下

postIpInformation

private void postIpInformation(String ip) {
        String url = "https://ip.taobao.com/service/";
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(url)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        IpServiceForPost ipService = retrofit.create(IpServiceForPost.class);
        Call<IpModel> call = ipService.getIpMsg(ip);
        call.enqueue(new Callback<IpModel>() {
            @Override
            public void onResponse(Call<IpModel> call, Response<IpModel> response) {
                String country = response.body().getData().getCountry();
                Toast.makeText(getApplicationContext(), country, Toast.LENGTH_SHORT).show();
            }

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

            }
        });
    }

6.2 传输数据类型JSON字符串:@Body

我们也可以用POST方式将JSON字符串作为请求体发送到服务器,请求网络接口如下

IpServiceForPostBody

public interface IpServiceForPostBody {
    @POST("getIpInfo.php")
    Call<IpModel> getIpMsg(@Body Ip ip);
}

用@Body这个注解标识参数对象即可,Retrofirt会将Ip对象转为字符串对象

请求网络如下

postInformationForBody

private void postIpInformationForBody(String ip) {
        String url = "https://ip.taobao.com/service/";
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(url)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        IpServiceForPostBody ipService = retrofit.create(IpServiceForPostBody.class);
        Call<IpModel> call = ipService.getIpMsg(new Ip(ip));
        call.enqueue(new Callback<IpModel>() {
            @Override
            public void onResponse(Call<IpModel> call, Response<IpModel> response) {
                String country = response.body().getData().getCountry();
                Toast.makeText(getApplicationContext(), country, Toast.LENGTH_SHORT).show();
            }

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

            }
        });
    }

6.3 单个文件上传:@Part

public interface UploadFileForPart{
        @Multipart
        @POST("user/photo")
        Call<User> updateUser(@Part MultipartBody.Part photo, 
        @Part("description") RequestBody description)
}

Multipart注解表示允许多个@Part。updateUser方法的第一个参数是准备上传的图片文件,使用了MultipartBody.Part类型,另一个参数是RequestBody类型,用来传递简单的键值对。


6.4 多个文件上传:@PartMap

与单文件上传类似,只不过用Map封装了上传文件,并用@PartMap注解来标识,其余和单文件上传一致

public interface UploadFileForPartMap{
        @Multipart
        @POST("user/photo")
        Call<User> updateUser(@PartMap Map<String, RequestBody> photos, 
        @Part("description") RequestBody description)
}

6.5 消息报头Header

在Http请求中,为了防止攻击或过滤掉不安全的访问,或者添加特殊加密的访问等,以便减轻服务器的压力和保证请求的安全,通常都会在消息报头中携带一些特殊的消息头作处理,Retrofit也提供了@Header添加消息报头。添加消息报头的方式有两种,一种是静态的,一种是动态的,先来看静态方式

静态添加

interface SomeService{
    @GET("some/endpoint")
    @Headers("Accept-Encoding: application/json")
    Call<ResponseBody> getCarType();
}

使用@Header注解添加消息报头,如果想要添加多个消息报头,则使用{ }包含起来

interface SomeService{
    @GET("some/endpoint")
    @Headers({
            "Accept-Encoding: application/json",
            "User-Agent: RetrofitTest"
          })
    Call<ResponseBody> getCarType();
}

动态添加

interface SomeService{
    @GET("some/endpoint")
    Call<ResponseBody> getCarType(@Headers("Location") String Location);
}

调用getCarType()方法动态添加报头


本文摘自《进阶之光——刘望舒》,学习路线上的总结记录,不以盈利为目的


欢迎指正。

上一篇 下一篇

猜你喜欢

热点阅读