Retrofit2使用详解

本文详细介绍了Retrofit的使用方法,包括项目导入、网络请求步骤、GET与POST请求的参数设置、不同请求方式的注解及参数类型关键字等。并通过一个具体的登录验证案例展示了如何用Retrofit发送请求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介

A type-safe HTTP client for Android and Java,一种类型安全的Http联网构架。
出品公司Square,项目地址


项目导入

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

需android 2.3以上


网络请求步骤

1.创建一个接口,设置请求的类型与参数

接口中创建一个方法,返回一个Call对象,泛型是网络请求返回的数据转换成实体类对象集合,用注解来确定请求是GET还是POST

public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

2.创建一个Retrofit对象

baseUrl()方法一般放置地址的主体部分,以/结尾

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();


3.生成一个接口实现对象

调用retrofit的create方法,传入第一步创建的请求接口,生成一个请求接口的实现对象

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

4.生成一个Call实例对象

调用请求接口的对象方法可以生成一个Call对象实例,到这一步就可以得到最后的请求地址了。

Call<List<Repo>> repos = service.listRepos("octocat");

这是一个Get请求,传的是Path的参数,所以最后的请求地址拼接起来就是https://api.github.com/users/octocat/repos

5.发送请求

调用Call实例对象的方法就可以发送联网请求了,有两种方式,一种是同步请求,一种是异步请求。

同步请求

调用execute()方法,需要捕捉异常,返回结果的响应体,泛型为接口类中定义方法里返回类的泛型一样

	 try {
       Response<List<Repo>> response= repos.execute();
     } catch (IOException e) {
           e.printStackTrace();
        }

该方法源码解释

/**
   * Synchronously send the request and return its response.
   *
   * @throws IOException if a problem occurred talking to the server.
   * @throws RuntimeException (and subclasses) if an unexpected error occurs creating the request
   * or decoding the response.
   */
  Response<T> execute() throws IOException;

异步请求

调用enqueue()方法,参数是一个回调,实现两个方法,一是请求成功调用的onResponse,可以得到响应体;二是请求失败调用的onFailure,可以查看异常。

  repos.enqueue(new Callback<List<Repo>>() {
                @Override
                public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
                //调用响应的body()方法,即可得到返回的数据
                    List<Repo> repoList=response.body();
                }

                @Override
                public void onFailure(Call<String> call, Throwable t) {
                //t即是异常类,可以打印异常信息
					  t.printStackTrace();
                }
            });
           

该方法源码解释

/**
   * Asynchronously send the request and notify {@code callback} of its response or if an error
   * occurred talking to the server, creating the request, or processing the response.
   */
  void enqueue(Callback<T> callback);

##6.添加返回数据自动转换功能
上述第五步中有一句关键代码,实现了返回数据自动转换成实体类集合的过程,简单快捷。

 List<Repo> repoList=response.body();

要实现该功能,有两步。
第一,添加Gson依赖,注意对应当前Retrofix版本

com.squareup.retrofit2:converter-gson:2.2.0

第二,构建Retrofit时,加上这句关键代码

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    //关键代码,添加转换工厂
    .addConverterFactory(GsonConverterFactory.create())
    .build();

也可以添加其它转换工厂,注意要在后面加上**:2.2.0**,当前的版本号

  • 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 //String类型的支持

所有请求方式的注解及参数类型关键字

retrofix内置有五个请求方式注解 GET, POST, PUT, DELETE, and HEAD.
其中GET请求与POST请求是Http经常使用的.

GET & POST请求方式传参数

以GET为例进行说明

@GET("group/{id}/users")
Call<List<User>> groupList(
@Path("id") int groupId,
 @Query("sort") String sort);

参数说明:
@Path:请求URL的字符替代,将网址中的{id}替换成自己传递的id
@Query:要传递的参数
如Call方法为:groupList(“9527”,“order by age desc”);
则最后发送的请求网址为:
https://api.github.com/group/9527/users?sort=order by age desc
注意@Path只能用于地址的通配,如要传参数必须用@Query

传递多个参数

要实现的请求地址如下:
https://api.github.com/group/9527/users?name=android&sort=order by age desc
方法1:添加多个@Query注解的参数

@GET("group/{id}/users")
Call<List<User>> groupList(
@Path("id") int groupId, 
@Query("name") String name);
@Query("sort") String sort);

方法2:添加一个@QueryMap注解参数,里面包含多个@Query注解的参数

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);

Map<String,String> options=new HashMap();
options.put("name","android");
options.put("sort","order by age desc");
Call call=service.groupList("9527",options);

另也可以用同一个key值来传递多个数据

Map<String,List<String>> options=new HashMap();
list.add("android");
list.add("IOS");
options.put("name",list);

得到的URL地址为

https://api.github.com/group/9527/users?name=android&name=IOS&sort=order by age desc

方法3:用@Body直接添加一个实体类对象,只适用于POST方式

@POST("users/new")
Call<User> createUser(@Body User user);

Form-encoded(URL编码)

可以指定http请求URL编码,application/x-www-form-urlencoded,传递表单数据是键值对形式,适用于POST请求方式

@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);

在带有@FormURLEncode的POST请求中,只能用@Filed来传递参数,用法与@Query一致.

上传文件

指定编码方式为@Multipart,即是multipart/form-data,只有这种编码格式才可上传文件,可以用@PUT,也可以用@POST方式,参数只能用@Part注解

@Multipart
@PUT("user/photo")
Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);

添加表头@Headers

//单项表头
@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call<List<Widget>> widgetList();

//多项表头
@Headers({
    "Accept: application/vnd.github.v3.full+json",
    "User-Agent: Retrofit-Sample-App"
})
@GET("users/{username}")
Call<User> getUser(@Path("username") String username);

//动态表头
@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)

用retrofit2实现网络请求案例

1.构建一个小型服务器

使用eclipse创建一个web server项目HttpRequestDemo,然后创建一个继承HttpServlet的实现类。
这是一个用户登录的验证服务,正确的用户名qq123456,密码666666,返回一个json字符串。
这里处理的是GET请求,post请求用的也是一模一样的代码。

public class WebServerDemo extends HttpServlet {
	private static final long serialVersionUID = 29328731L;

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String name =request.getParameter("name");
		String password=request.getParameter("pwd");
		String result="";
		String msg="";
		if(!"qq123456".equalsIgnoreCase(name)){
			result="fail";
			msg="user name is fail";
		}else{
			if(!"666666".equals(password)){
				result="fail";
				msg="password is fail";
			}else{
				result="success";
				msg="mession success";
			}
		}
		String returnJson="{\"result\":\""+result+"\",\"msg\":\""+msg+"\"}";
		response.setContentType("text/html");
		PrintWriter out = response.getWriter();
		out.println("<HTML>");
		out.println("  <HEAD><TITLE>login page</TITLE></HEAD>");
		out.println("  <BODY>");
		out.println(returnJson);
		out.println("  </BODY>");
		out.println("</HTML>");
		out.flush();
		out.close();
	}
}

在WebRoot/WEB_INF/web.xml里配置servlet与外部访问的路径地址

<?xml version="1.0" encoding="UTF-8"?>
....
  <!--配置servlet对应的类名与路径名 -->
  <servlet>
    <servlet-name>WebServerDemo</servlet-name>
    <servlet-class>server.WebServerDemo</servlet-class>
  </servlet>
	<!--将servlet与web对应 -->
  <servlet-mapping>
    <servlet-name>WebServerDemo</servlet-name>
    <!--/login即是外部访问的路径-->
    <url-pattern>/login</url-pattern>
  </servlet-mapping>	
.....
</web-app>

最后将HttpRequestDemo部署至Tomcat中即可完成这个用户登录验证的小型服务器。 下载地址

2.发送登录请求

首先创建一个as自带的loginactivity登录界面,然后简化一些代码,减少登录过程中的一些验证。
然后运用上文步骤来发送联网请求。

public interface LoginService {
        @GET("{filepath}/login")
        Call<String> login4Path(@Path("filepath") String filepath);
    }
//注意这里的baseUrl是本机的IP地址,端口是tomcat默认的8080
//这里并没有添加任何转换器
mRetrofit=new Retrofit.Builder()
                .baseUrl("http://192.168.0.199:8080/")
                .build();
  Call<String> call=mRetrofit.create(LoginService.class).login4Path("HttpRequestDemo");
 call.enqueue(new Callback<String>() {
            @Override
            public void onResponse(Call<String> call, Response<String> response) {
                String resultStr=response.body();
                mResultTextView.setText(resultStr);
                showProgress(false);
                if(resultStr.contains("fail")){
                    mPasswordView.requestFocus();
                    Toast.makeText(getApplicationContext(),"登录失败",Toast.LENGTH_SHORT).show();
                }else if(resultStr.contains("success")){
                    startActivity(new Intent(RetrofixDemoActivity.this,LitepalDemoActivity.class));
                }

            }

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

点击登录按钮,程序崩溃,一直报如下异常,无法创建一个转换器,LoginService.login4Path这个方法的结果无法转换成String.

java.lang.IllegalArgumentException: Unable to create converter for class java.lang.String for method LoginService.login4Path at retrofit2.ServiceMethod$Builder.methodError(ServiceMethod.java:751) at retrofit2.ServiceMethod$Builder.createResponseConverter(ServiceMethod.java:737) at retrofit2.ServiceMethod$Builder.build(ServiceMethod.java:168) at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:169) at retrofit2.Retrofit$1.invoke(Retrofit.java:146) at $Proxy0.login4Path(Native Method)

这个错误的原因在于如果构建时retrofix实例时没有添加转换器则Call对象的泛型只能使用RequestBody,这是比较容易忽略的异常。
将所有Call里的泛型换成RequestBody类,然后再运行程序,登录成功会跳转至另一个界面。

Call<ResponseBody> call=service.login4Path("HttpRequestDemo");
        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                ResponseBody body=response.body();
                String resultStr= "";
                try {
                    resultStr = body.string();
                } catch (IOException e) {
                    e.printStackTrace();
                }
......

这里不管输入什么都是返回用户名错误,因为这个请求并没有传参数,只是简单地返回了用户名不相同时的result与msg.
这里写图片描述
用一个Textview来显示服务器返回的所有数据。
###3.用Post方式进行请求

 public interface LoginService {
        @POST("{filepath}/login")
        Call<ResponseBody> login4Path(@Path("filepath") String filePath,
                                      @Query("name")String name,
                                      @Query("pwd")String pwd);
    }
  String email = mEmailView.getText().toString();
   String password = mPasswordView.getText().toString();
 LoginService service=mRetrofit.create(LoginService.class);
 Call<ResponseBody>call=
 service.login4Path("HttpRequestDemo",email,password);
  call.enqueue(new Callback<ResponseBody>() {...}

运行结果:
这里写图片描述

demo地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值