前言
开门见山,本文记录了如何接入并使用square出品的retrofit网络框架。
一、准备工作
- 添加retrofit 的依赖(或直接使用jar包)
这边并不能直接连接网络,所以直接下载jar包并使用了
- 添加okio依赖或引用jar包
添加jar包之后,还要添加okio的jar包,不然会报错
java.lang.NoClassFoundError:okio.Buffer
- 添加converter-gson的依赖或引用jar包
由于代码中要解析数据到实体类里,retrofit是默认用的Gson来转换的,而且官方也给了一个转换的包,使用也很简单,直接用GsonConverterFactory.create()就能直接转换:
Retrofit retrofit = new Retrofit.Builder().baseUrl(THost.Host)
.addConverterFactory(GsonConverterFactory.create())
.build();
- 添加okhttp的依赖或引用jar包
这里直接下载的最新版的okhttp的jar包,版本为okhttp-3.8.0
,如果不加,则会报错:
- 添加Gson的依赖
这里有个坑,就是gson版本不能太低,这边原本使用的是gson 2.2.4,结果请求是正常的,但是解析会出错,无法得到想要的数据,在onFailure方法里打的log如下:
请求是正常的,抓包显示返回正常,但是方法里跑不到onResponse,而是进了onFailure,log显示java.lang.NoSuchMethodError: com.google.gson.Gson.newJsonReader
解决方法是用最新的gson包就行了。这边使用的是gson 2.8.0
总结下,添加的依赖就是这些了:
二、开始使用
以一个简单的登录为例,需求如下:
请求地址为
http://app.test.com/testapp.php
请求方式为POST,参数有username和pwd,以及一个action,通过表单发送到服务器
我们先定义一个接口类,里面有一个login方法
public interface TestApiService {
@FormUrlEncoded
@POST("http://app.test.com/testapp.php")
Call<TPHPResult<TLogin>> login ( @Field("username") String username,
@Field("pwd") String pwd,
@Field("action") String aciton ) ;
}
这里用注解表明了login方法的参数形式
- @Field注解代表参数为表单格式,key为username,value在调用的时候传入。
- @POST注解里面放的是请求的url地址。
由于参数以表单形式提交,所以方法上还要加@FormUrlEncoded注解
然后再在MainActivity里使用这个方法来实现登录:
public class MainActivity extends AppCompatActivity {
private String token ;
private int uid ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://app.test.com/testapp.php/")
.addConverterFactory(GsonConverterFactory.create())
.build();
TestApiService service = retrofit.create(TestApiService.class);
Call<TPHPResult<TLogin>> loginCall = service.login("Naruto", "123456","login");
//enqueue是异步的,所以可以直接在主线程请求网络
loginCall.enqueue(new Callback<TPHPResult<TLogin>>() {
@Override
public void onResponse(Call<TPHPResult<TLogin>> call, Response<TPHPResult<TLogin>> response) {
if (response.code() == 200) {
TLogin loginInfo = response.body().getData() ;
token = loginInfo.getToken() ;
uid = loginInfo.getUid() ;
getUserInfo();
}
}
@Override
public void onFailure(Call<TPHPResult<TLogin>> call, Throwable throwable) {
Toast.makeText(MainActivity.this,"登录错误: " + throwable.toString(),Toast.LENGTH_SHORT).show(); ;
}
});
}
}
在上面可以看到,我们的请求地址末尾是不带”/”的,直接请求带”/”的url会报错404 Not Found
,但是代码里的baseUrl里的url为何又有”/”呢?
这是因为,在retrofit2.0以后,规定baseUrl里面的url,必须以”/”结尾,不然会报错:
这里要打个岔,说明下服务器接口的定义方式了。
一般来说,很多后台开发人员,都会把调用的方法名字直接放到请求url的后面,比如:
http://app.test.com/testapp.php/login
针对这种定义方式,在请求时,就很简单,只需在baseUrl里放http://app.test.com/testapp.php/
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://app.test.com/testapp.php/")
//略...
,然后在login方法的@POST注解里面放上login
,
@FormUrlEncoded
@POST("login")
Call<TPHPResult<TLogin>> login(...) //略......
就可以拼接成
http://app.test.com/testapp.php/login
了
然而,也有的后台人员,定义接口时,让客户端把方法名字当做表单参数传过来,如上面的action
字段,里面的值为login
,就是我们要请求的接口名字。
请求url永远是一样的,通过action来区别各个接口。
这种情况下,如果服务器不做兼容,那么请求的url只能是不带”/”的,不然就会404 Not Found
。 但是由于retrofit 2.0的限制,baseUrl里只能是带”/”的url (http://app.test.com/testapp.php/
),这样是访问不到服务器的。怎么办呢?
我们可以在@POST里面放真正的请求地址,这样就能覆盖掉baseUrl里面的url了。那么,为什么不把.baseUrl("")
去掉,只在@POST里写url呢?这是因为不写会报错呀 :(
总结下使用方法:
首先定义请求的接口类(TestApiService),根据接口参数的格式(表单/键值对/body/etc),请求的格式(POST/GET/etc),以及返回结果T
(TPHPResult),来声明一个返回值为Call<T>
的login方法
然后定义Retrofit 实例retrofig,通过retrofit实例的create方法创建一个接口类的实例service,调用service实例的login方法生成Call对象,然后再enqueue异步将请求发出去,得到结果并在回调中解析。
可以看得出来,实际上retrofit的使用还是很简单的,当然,这也仅仅是入门而已,真正要用到实际业务上,就要考虑架构和封装了。