为什么使用Retrofit
在之前做项目的时候,使用的是Volley做网络请求,因为是接手一个学长的项目,底层封装已经做好了,可是在出现新需求要调用时有着诸多不便,一是虽然做了封装,但在调用时结构不清晰;二是每个接口地址都保存在一个专门的api类,只要多一个功能,就要在该类中添加一个api,尽管很多内容十分相似,所以耦合性很高。而接触到Retrofit后让我眼前一亮,使用Retrofit可以完美解决上述问题。Retrofit使用了目前比较流行的链式调用,结构十分清晰,并且将api封装成接口,通过注解的方式声明api的信息,Retrofit也提供了很多注解来适应不同的需求来降低耦合性。
第一个例子
我使用的是xampp搭建的本地服务器,php+mysql+Apache的结构。
创建服务端api,在xampp安装目录下的htdocs文件夹中新建目录/test/test.php文件,文件内容如下,即输出一个json字符串:
<?php
$array = array('msg' => "request success!");
echo json_encode($array);
?>
也就是要请求的api就是http://localhost/test/test.php ,localhost就是本机地址加上Apache的端口号。
打开AS,添加依赖
//Retrofit2所需要的包
compile 'com.squareup.retrofit2:retrofit:2.2.0'
//ConverterFactory的Gson依赖包
compile 'com.squareup.retrofit2:converter-gson:2.2.0'
两个项目的传送门,如果更新了,直接将版本号替换成最新的版本号就行了
Retrofit项目地址
Gson项目地址
注意这里Retrofit与Gson的版本号要一样,否则会出错。
定义api接口与返回对象,将之前的api封装成java中的接口,并根据返回的信息,创建一个用于保存信息的bean。
public interface HelloService {
@GET("test/test.php")
Call<ResponseInfo> testHttpGet();
}
class ResponseInfo {
String msg;
}
这里使用了@GET注解,表示get请求,@GET后的内容便是请求地址,有人要问了,请求地址不是http://localhost/test/test.php 吗?前面的内容哪去了?别急别急,我们可以把前面不变的部分作为baseUrl在初始化Retrofit的时候传进去,这样将url拼装一下就是实际的url地址了。
调用Retrofit,终于到最关键的一步了,url有了,也封装成接口了,怎么通过Retrofit访问url呢?看下面代码:
public class MainActivity extends AppCompatActivity {
private Button btn_req;
private TextView tv_info;
private final static String BASE_URL = "http://192.168.1.2:80/";//服务器的IP地址与端口号
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_req = (Button) findViewById(R.id.button);
tv_info = (TextView) findViewById(R.id.tv_info);
btn_req.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doRequest();
}
});
}
private void doRequest() {
//创建Retrofit实例
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
//将返回值转换成bean
.addConverterFactory(GsonConverterFactory.create())
.build();
//创建调用接口
HelloService helloService = retrofit.create(HelloService.class);
//调用接口方法,访问url
Call<ResponseInfo> call = helloService.testHttpGet();
//请求处理
call.enqueue(new Callback<ResponseInfo>() {
@Override
//请求成功
public void onResponse(Call<ResponseInfo> call, Response<ResponseInfo> response) {
tv_info.setText(response.body().msg);
}
@Override
//请求失败
public void onFailure(Call<ResponseInfo> call, Throwable t) {
tv_info.setText(t.getMessage());
}
});
}
}
需要注意的是,执行call.equeue(…)的方法是异步执行,还有有一种是同步请求,
Response response=call.execute();
Log.d(“123321”,+response.body().getUser_head_imag);
1,直接调用execute(),会返回一个值,
2,这个值就是请求结果,大家直接用就是,但是这个值没有返回的时候,如果网络速度比较慢,手机会卡在哪里,动不了甚至ANR
3,如果用equeue的异步方法请求,就不会有这种问题了。