-1-什么是retrofit2
Retrofit是一个不错的网络请求库
A type-safe REST client for Android and Java
好吧,说人话就是:一个用于网络请求的库。跟我们用到的OKhttp、Vellory、AysncHttp是一样一样的作用。
-2-废话
此demo采用mvp+retrofit2的形式展示,如果不熟悉mvp的同学请参考
有的好事者肯定就问了,现在主流不是mvp+retrofit2+rxjava吗,怎么你只介绍前面2个的结合呢。是的我就只介绍mvp+retrofit2原因是什么不怕告诉你,因为看了rxjava能大概知道原理和作用,但是能直接上手的案例真的不多,也因为本人天资愚钝所以还是打算一步一步来比较好。
如果有人同学想学习rxjava,请参考扔物线这位大神的给 Android 开发者的 RxJava 详解文章,说实话我至少看了3遍,已经了解一些了,下一步就会上手写demo,也相信自己能搞定。
-3-实战
1 项目包结构
项目包结构熟悉mvp的同学都知道model、presenter、view就不做介绍了。同时我自己新建了一个retrofit包名负责retrofit的所有功能。
当然此结构由优化的地方,因demo,所以不做过度累述。
这是一个请求通过网络获取网络天气信息的demo。请求连接为
http://api.avatardata.cn/Weather/Query?key=xxxxxxx&cityname=长沙
先看效果图
运行前 运行中 运行后
2 添加依赖
在使用之前,你必须先导入必要的jar包,以androidStudio为例:
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
注意:本demo使用retrofit2,而不是retrofit1.x
3 mvp中的M
/**
* 描述:mvp中的m
* 作者:dc on 2016/11/15 14:48
* 邮箱:597210600@qq.com
*/
public interface WeatherModel {
/** 异步加载数据 **/
void loadWeather(String key , String cityId);
/** 同步加载数据 **/
void synLoadWeather(String key, String cityName);
/** 设置监听器 **/
void setWeatherListener(WeatherModelImp.LoadWeatherInfoListener listener);
}
里面有3个方法,一个异步加载数据、一个同步加载数据。一个监听器方法(作用是通过interface返回请求结果,如果用上Rxjava就不需要这么麻烦了)。
然后新建一个类实现该接口
import android.content.Context;
import android.util.Log;
import com.souv.rxjava.Constan;
import com.souv.rxjava.bean.WeatherInfoBean;
import com.souv.rxjava.retrofit.LoadWeatherServer;
import java.io.IOException;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
/**
* 描述:mvp中的m实现接口
* 作者:dc on 2016/11/15 14:48
* 邮箱:597210600@qq.com
*/
public class WeatherModelImp implements WeatherModel {
private static final String TAG = WeatherModelImp.class.getSimpleName();
/** 对象定义 **/
private Context mContext = null;
private LoadWeatherInfoListener loadWeatherInfoListener = null;
public WeatherModelImp(Context context){
mContext = context;
}
@Override
public void loadWeather(String key, String cityId) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Constan.WHETHER_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
LoadWeatherServer apiService = retrofit.create(LoadWeatherServer.class);
Call<WeatherInfoBean> call = apiService.loadRetrofitWeatherInfo(key,cityId);
call.enqueue(new Callback<WeatherInfoBean>() {
@Override
public void onResponse(Call<WeatherInfoBean> call, Response<WeatherInfoBean> response) {
String result = "";
try{
String errorResult = response.errorBody().string();
}catch (Exception e){
e.printStackTrace();
}
WeatherInfoBean weatherBean = response.body();
Log.e(TAG, "请求天气返回结果:" + weatherBean.getResult().getRealtime().getWeather().getTemperature());
loadWeatherInfoListener.loadWeatherSuccess(weatherBean);
}
@Override
public void onFailure(Call<WeatherInfoBean> call, Throwable t) {
//请求失败
t.printStackTrace();
loadWeatherInfoListener.loadWeatherFial();
}
});
}
@Override
public void synLoadWeather(String key, String cityId) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Constan.WHETHER_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
LoadWeatherServer apiService = retrofit.create(LoadWeatherServer.class);
Call<WeatherInfoBean> call = apiService.loadRetrofitWeatherInfo(key, cityId);
try {
Response<WeatherInfoBean> responseBody = call.execute();
WeatherInfoBean weatherBean = responseBody.body();
Log.e(TAG, "请求天气返回结果:" + weatherBean.getResult().getRealtime().getWeather().getTemperature());
loadWeatherInfoListener.synLoadWeatherSuccess(weatherBean);
} catch (IOException e) {
e.printStackTrace();
loadWeatherInfoListener.synLoadWeatherFail();
}
}
@Override
public void setWeatherListener(LoadWeatherInfoListener listener) {
loadWeatherInfoListener = listener;
}
public interface LoadWeatherInfoListener{
void loadWeatherSuccess(WeatherInfoBean bean); //异步加载数据成功
void loadWeatherFial(); //异步加载数据失败
void synLoadWeatherSuccess(WeatherInfoBean bean); //同步加载数据成功
void synLoadWeatherFail(); //同步加载数据失败
}
我们仔细看下loadWeather方法和synLoadWeather方法。分别是异步请求和同步请求
Retrofit2使用
异步请求
Call<WeatherInfoBean> call = apiService.loadRetrofitWeatherInfo(key,cityId);
call.enqueue(new Callback<WeatherInfoBean>() {
@Override
public void onResponse(Call<WeatherInfoBean> call, Response<WeatherInfoBean> response) {
}
@Override
public void onFailure(Call<WeatherInfoBean> call, Throwable t) {
//请求失败
t.printStackTrace();
}
以上代码发起了一个在后台线程的请求并从response 的response.body()方法中获取一个结果对象。注意这里的onResponse和onFailure方法是在主线程中调用的。
我建议你使用enqueue,它最符合 Android OS的习惯。
同步请求
Call<WeatherInfoBean> call = apiService.loadRetrofitWeatherInfo(key, cityId);
try {
Response<WeatherInfoBean> responseBody = call.execute();
WeatherInfoBean weatherBean = responseBody.body();
} catch (IOException e) {
e.printStackTrace();
}
以上的代码会阻塞线程,因此你不能在安卓的主线程中调用,不然会面临NetworkOnMainThreadException。如果你想调用execute方法,请在后台线程执行。
然后我们来看下retrofit的URL定义方式。
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
/**
* 描述:retrofit
* 作者:dc on 2016/11/15 15:16
* 邮箱:597210600@qq.com
*/
public interface LoadWeatherServer {
//http://api.avatardata.cn/
//http://api.avatardata.cn/Weather/Query?key=75bfe88f27a34311a41591291b7191ce&cityname=%E9%95%BF%E6%B2%99
@GET("Weather/Query?")
Call<WeatherInfoBean> loadRetrofitWeatherInfo(@Query("key") String key,@Query("cityname") String cityName);
}
......
//引用
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Constan.WHETHER_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
......
//URL
public static final String WHETHER_URL = "http://api.avatardata.cn/";
因retrofit重新定义了新的URL形式,所以URL形式有不同的表示。
下面引用几个案例
URL案例-1URL案例-2
URL案例-3
显然我喜欢第二个案例
对于 Retrofit 2.0中新的URL定义方式,这里是我的建议:
-
Base URL: 总是以 /结尾
-
@Url: 不要以 / 开头
如:
@GET("Weather/Query?")
Call<WeatherInfoBean> loadRetrofitWeatherInfo(@Query("key") String key,@Query("cityname") String cityName);
//引用
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(http://api.avatardata.cn/)
.addConverterFactory(GsonConverterFactory.create())
.build();
Retrofit请求
LoadWeatherServer apiService = retrofit.create(LoadWeatherServer.class);
Call<WeatherInfoBean> call = apiService.loadRetrofitWeatherInfo(key,cityId);
call.enqueue(new Callback<WeatherInfoBean>() {
@Override
public void onResponse(Call<WeatherInfoBean> call, Response<WeatherInfoBean> response) {
String result = "";
try{
String errorResult = response.errorBody().string();
}catch (Exception e){
e.printStackTrace();
}
WeatherInfoBean weatherBean = response.body();
Log.e(TAG, "请求天气返回结果:" + weatherBean.getResult().getRealtime().getWeather().getTemperature());
loadWeatherInfoListener.loadWeatherSuccess(weatherBean);
}
@Override
public void onFailure(Call<WeatherInfoBean> call, Throwable t) {
//请求失败
t.printStackTrace();
loadWeatherInfoListener.loadWeatherFial();
}
});
前面说到enqueue是异步请求,其中Response是请求响应的结果体。
在Retrofit 2.0中,不管 response 是否能被解析。onResponse总是会被调用。但是在结果不能被解析的情况下,response.body()会返回null。别忘了处理这种情况。
如果response存在什么问题,比如404什么的,onResponse也会被调用。你可以从response.errorBody().string()中获取错误信息的主体。
好了,然后我们在来看
MVP中的V
/**
* 描述:MVP中的V
* 作者:dc on 2016/11/15 16:02
* 邮箱:597210600@qq.com
*/
public interface WeatherView {
void showWeather(String weather); //异步显示天气信息
void showWeatherTemp(String weatherTemp); //异步显示天气温度信息
void loadWeatherFail(); //异步加载天气信息失败
void startLoadWeatherInfo(); //开始加载天气信息
void synShowWeather(String weather); //同步显示天气信息
void synShowWeatherTemp(String weatherTemp);//同步显示天气温度信息
void synoadWeatherFail(); //同步加载天气信息失败
}
也没什么好解释的,只要是提供接口给MVP中的P,让其调用。接口主要是处理控件事件。因为上面提到过retrofit异步加载是在主线程中的,同步加载时开启了一个新线程的所以我在不同的请求方式下也分了不同的显示方法。这之后我们会在MVP中的P看到
MVP中的P
/**
* 描述:
* 作者:dc on 2016/11/15 16:02
* 邮箱:597210600@qq.com
*/
public class WeatherPresenter implements WeatherModelImp.LoadWeatherInfoListener{
private static final String TAG = WeatherPresenter.class.getSimpleName();
private WeatherModelImp weatherModelImp = null;
private WeatherView weatherView = null;
private Handler handler = new Handler();
public WeatherPresenter(Context context , WeatherView view){
weatherModelImp = new WeatherModelImp(context);
weatherView = view;
weatherModelImp.setWeatherListener(this);
}
/**
* 异步获取天气数据
* @param key
* @param cityName
*/
public void loadWeatherInfo(String key ,String cityName){
weatherView.startLoadWeatherInfo();
weatherModelImp.loadWeather(key, cityName);
}
/**
* 同步获取天气数据
* @param key
* @param cityName
*/
public void synLoadWeatherInfo(String key ,String cityName){
weatherView.startLoadWeatherInfo();
weatherModelImp.synLoadWeather(key, cityName);
}
@Override
public void loadWeatherSuccess(WeatherInfoBean bean) {
if(null != bean){
//显示当前温度
String temperature = bean.getResult().getRealtime().getWeather().getTemperature();
String info = bean.getResult().getRealtime().getWeather().getInfo();
weatherView.showWeather(info);
weatherView.showWeatherTemp(temperature);
}
}
@Override
public void loadWeatherFial() {
weatherView.loadWeatherFail();
}
@Override
public void synLoadWeatherSuccess(WeatherInfoBean bean) {
//显示当前温度
final String temperature = bean.getResult().getRealtime().getWeather().getTemperature();
final String info = bean.getResult().getRealtime().getWeather().getInfo();
//同步请求,启动的新线程,所以在现实界面的时候需要在主线程中
handler.post(new Runnable() {
@Override
public void run() {
weatherView.synShowWeather(info);
weatherView.synShowWeatherTemp(temperature);
}
});
}
@Override
public void synLoadWeatherFail() {
//同步请求,启动的新线程,所以在现实界面的时候需要在主线程中
handler.post(new Runnable() {
@Override
public void run() {
weatherView.synoadWeatherFail();
}
});
}
}
也不过大解释,MVP中的P主要是M和V的桥接。这里面引用了接口的方式来返回请求天气结果。个人觉得还是过于麻烦,所以说Rxjava还是很有必要学习的
MainActivity
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.souv.rxjava.presenter.WeatherPresenter;
import com.souv.rxjava.view.WeatherView;
import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class MainActivity extends AppCompatActivity implements WeatherView {
private static final String TAG = MainActivity.class.getSimpleName();
@Bind(R.id.main_weather_tv)
TextView mainWeatherTv;
@Bind(R.id.main_weather_temp_tv)
TextView mainWeatherTempTv;
@Bind(R.id.main_weather_getweather_btn)
Button mainWeatherGetweatherBtn;
@Bind(R.id.main_weather_syn_getweather_btn)
Button mainWeatherSynGetweatherBtn;
/**
* 对象定义
**/
private WeatherPresenter weatherPresenter = null;
private WaitDialog waitDialog = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
init();
}
private void init() {
waitDialog = new WaitDialog(MainActivity.this, android.R.style.Theme_Translucent_NoTitleBar);
waitDialog.setCancelable(true);
weatherPresenter = new WeatherPresenter(MainActivity.this, this);
}
@OnClick(R.id.main_weather_getweather_btn)
public void loadWeather() {
weatherPresenter.loadWeatherInfo("75bfe88f27a34311a41591291b7191ce", "长沙");
}
@OnClick(R.id.main_weather_syn_getweather_btn)
public void synLoadWeather() {
//以上的代码会阻塞线程,因此你不能在安卓的主线程中调用,不然会面临NetworkOnMainThreadException。如果你想调用execute方法,请在后台线程执行。
new Thread(new Runnable() {
@Override
public void run() {
weatherPresenter.synLoadWeatherInfo("75bfe88f27a34311a41591291b7191ce", "长沙");
}
}).start();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
@Override
public void showWeather(String weather) {
mainWeatherTv.setText(weather);
}
@Override
public void showWeatherTemp(String weatherTemp) {
waitDialog.cancel();
waitDialog.dismiss();
mainWeatherTempTv.setText(weatherTemp + "℃");
}
@Override
public void loadWeatherFail() {
Toast.makeText(MainActivity.this, "获取天气信息失败", Toast.LENGTH_SHORT).show();
}
@Override
public void startLoadWeatherInfo() {
waitDialog.setWaitText("开始请求天气信息");
waitDialog.show();
}
@Override
public void synShowWeather(String weather) {
mainWeatherTv.setText(weather);
}
@Override
public void synShowWeatherTemp(String weatherTemp) {
waitDialog.cancel();
waitDialog.dismiss();
mainWeatherTempTv.setText(weatherTemp + "℃");
}
@Override
public void synoadWeatherFail() {
Toast.makeText(MainActivity.this, "获取天气信息失败", Toast.LENGTH_SHORT).show();
}
}
也没什么好解释的,主要是extends MVP中的V,用来实现视图的操作。同时使用到MVP中的P的实例来调用P中的方法。
总结
Retrofit2还有很多知识需要学习,上面的demo只是冰山一角,但是却能清晰的知道他的使用方式,如何使用。只要是入了门那么之后的深入我相信是不会很难的。因为学习了Retrofit所以我觉得RXjava真的很有必要,也很适合跟Retrofit一起使用。如果可以你也一定要学习新的知识。
慢慢努力做好身边所有的事
求知若饥,虚心若愚
网友评论
还有这篇文章已经很low了,我重新写了一篇,你看下。http://www.jianshu.com/p/426864584518