Retrofit初接触
引言
最近的一个项目里使用到了现在很火的 Retrofit ,刚接触时就是简单的使用。在需要请求接口的地方定义一个方法,方法体里面写 Retrofit 相关代码,所以基本上每一个需要请求接口的地方都要这样做一次。这样做的后果就是后期再回头看这些代码的时候,就觉得类很乱,代码冗余,难于维护。所以我做了一个简单的封装,经一位大神指点过后(这位大神说我这个封装有点MVP的味道,内心还有点小窃喜),现将整理后的代码贴在这儿,分享给大家。如有觉得不足之处,请大家多多指点,在评论区积极留言,以做讨论。我这也是一个抛砖引玉之举,如果哪位大神还有什么好的使用方法,欢迎在此分享给大家,一起学习,一起进步,谢谢!
一般来说我们使用 Retrofit 必不可少的就是定义一个 interface ,里面定义获取 Call 的方法。之前都是随用随建,几乎每个类里面都有一个。现在我把它拿了出来,作为一个公共的 interface ,在里面写各个接口需要的获取 Call 的方法。
public interface Parser {
/**
* 添加游客身份
*
* @param mParams
* @return
*/
@FormUrlEncoded
@POST("surfers")
Call<SurferEntry> doParserSurferEntry(@FieldMap Map<String, String> mParams);
/**
* 获取文章列表
*
* @param params
* @return
*/
@GET("articles")
Call<RecommendEntry> doGetArticles(@QueryMap Map<String, String> params);
}
这步做完之后,我们就该去创建 Retrofit 了,用它来请求接口,获取数据。这里我也是把这个操作放在了一个公共类里,这样我们就只需要创建一次就可以了,不需要在每个地方都去创建一次了。
public class EntryDataParser {
private Context mContext;
private Parser mParser;
private Map<String, String> mParams;
private int DATA_TYPE;
private EntryDataListener mListener;
public EntryDataParser(Context mContext, EntryDataListener mListener) {
this.mContext = mContext;
this.mListener = mListener;
}
/**
* 请求接口,接收返回数据
*
* @param DATA_TYPE 接口类型
* @param mParams 接口参数
*/
public void doParser(int DATA_TYPE, Map<String, String> mParams) {
this.DATA_TYPE = DATA_TYPE;
this.mParams = mParams;
Retrofit retrofit = new Retrofit.Builder().baseUrl(ConstantUtil.BASE_URL)
.addConverterFactory(GsonConverterFactory.create()).build();
mParser = retrofit.create(Parser.class);
switch (DATA_TYPE) {
case ParserEntry.SURFER_TYPE://添加游客身份
doCreateSurfer();
break;
case ParserEntry.ARTICLE_TYPE://获取文章列表
doGetArticles();
break;
}
}
/**
* 添加游客身份
*/
private void doCreateSurfer() {
Call<SurferEntry> call = mParser.doCreateSurfer(mParams);
call.enqueue(new Callback<SurferEntry>() {
@Override
public void onResponse(Call<SurferEntry> call, Response<SurferEntry> response) {
if (response.isSuccessful()) {
if (response.body().code == 200) {
mListener.doEntryData(DATA_TYPE, response.body());
} else {
PrintUtil.toast(mContext, response.body().msg);
}
} else {
PrintUtil.i("添加游客身份失败: response is unsuccessful");
}
}
@Override
public void onFailure(Call<SurferEntry> call, Throwable t) {
PrintUtil.i("添加游客身份失败: " + t.getMessage());
}
});
}
/**
* CommonTestActivity.java
* 获取文章列表
*/
private void doGetArticles() {
Call<RecommendEntry> call = mParser.doGetArticles(mParams);
call.enqueue(new Callback<RecommendEntry>() {
@Override
public void onResponse(Call<RecommendEntry> call, Response<RecommendEntry> response) {
if (response.isSuccessful()) {
if (response.body().code == 200) {
mListener.doEntryData(DATA_TYPE, response.body());
} else {
PrintUtil.toast(mContext, response.body().msg);
}
} else {
PrintUtil.i("获取文章列表失败: response is unsuccessful");
}
}
@Override
public void onFailure(Call<RecommendEntry> call, Throwable t) {
PrintUtil.i("获取文章列表失败: " + t.getMessage());
}
});
}
}
解释一下各个变量:
Context mContext; 就是上下文,不多解释;
Parser mParser; 就是上面定义的公共 interface ,用来获取 Call ;
Map<String, String> mParams; 就是请求接口所需要的参数;
int DATA_TYPE; 这个参数是用来区分你是要做哪个操作的,比如说添加游客身份和获取文章列表,要传入一个用以区分的 tag 。
EntryDataListener mListener; 这个参数是用来回调接口返回的数据通过 Gson 解析后的实体类的,后面会给出定义。
public class ParserEntry {
//添加游客身份
public static final int SURFER_TYPE = 0;
//获取文章列表
public static final int ARTICLE_TYPE = 1;
}
ParserEntry 类,这个类里定义了上面 EntryDataParser 类里 doParser() 方法里面的 switch() 里对应的 DATA_TYPE ,用以区分你要做什么操作。
public class ParserData {
private Context mContext;
private EntryDataParser parser;
public ParserData(Context mContext, EntryDataListener mListener) {
this.mContext = mContext;
this.parser = new EntryDataParser(mContext, mListener);
}
/**
* 添加游客身份
*/
public void doCreateSurfer() {
Map<String, String> params = new HashMap<>();
params.put("deviceId", DeviceUtil.doGetIMEI(mContext));
parser.doParser(ParserEntry.SURFER_TYPE, params);
}
/**
* 获取文章列表
*/
public void doGetArticles() {
Map<String, String> params = new HashMap<>();
params.put("user", "surfer");
params.put("fromUid", ShareSaveUtil.doGetSurferId(mContext));
params.put("flag", "index2");
params.put("page", String.valueOf(1));
params.put("limit", ConstantUtil.COMMON_LIMIT);
parser.doParser(ParserEntry.ARTICLE_TYPE, params);
}
}
ParserData 类,用来实现具体接口的请求操作,里面添加需要的参数。
最后,在 Activity 里面调用,实现接口数据的获取,然后填充界面。
public class CommonTestActivity extends Activity implements EntryDataListener {
private TextView show;
private ParserData data;
private SurferEntry surferEntry;
private RecommendEntry recommendEntry;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_common_test);
init();
showView();
}
/**
* 初始化
*/
private void init() {
show = findViewById(R.id.text_show);
data = new ParserData(this, this);
surferEntry = new SurferEntry();
recommendEntry = new RecommendEntry();
}
/**
* 显示UI
*/
private void showView() {
data.doCreateSurfer();//添加游客身份
}
/**
* 接收回调实体类
*
* @param DATA_TYPE 实体类类型
*/
@Override
public void doEntryData(int DATA_TYPE, Object mObject) {
switch (DATA_TYPE) {
case ParserEntry.SURFER_TYPE:
surferEntry = (SurferEntry) mObject;
data.doGetArticles();//获取文章列表
break;
case ParserEntry.ARTICLE_TYPE:
recommendEntry = (RecommendEntry) mObject;
show.setText(surferEntry.toString() + "\n\n" + recommendEntry.toString());
break;
}
}
}
其中 SurferEntry 和 RecommendEntry 都是接口数据的实体类,大家看情况自行定义。到此,封装使用完毕。
结束语
这个东西做出来之后自己内心还是有点小欣喜的,因为觉得耦合度降低了很多,Activity 层看上去也简洁了很多。但是一开始用了静态方法和静态变量,经大神指点后,知道了这样做会很容易导致内存泄漏。所以改成了现在这样,这应该是我写代码以来做的一件非常非常有成就感觉的事儿了,哈哈哈,自喜一下。
这里分享出来给大家,一是想方便大家使用 Retrofit ,二来也是希望大家看到之后,如果觉得哪儿还可以改进,能在评论区积极留言,多多指点,因为我也期待着进步嘛,嘿嘿。谢谢大家!