我的问题是关于使用RxJava for Android来处理来自Retrofit调用的数据的可能性。
我刚开始使用这些库,所以如果我的问题很简单,请耐心等待。
这是我的情景。
我从服务器返回的json看起来像这样
{ <--- ObjectResponse
higher_level: {
data: [
{
...
some fields,
some inner object: {
....
},
other fields
}, <----- obj 1
....,
{
....
}<---- obj n
]<-- array of SingleObjects
}
} <--- whole ObjectResponse
我已经有改造获得此响应并在ObjectResponse中解析。解析这个对象,我可以获得一个我可以像往常一样传递给我的RecyclerView适配器的List。
所以改造返回了ObjectResponse,它是整个服务器答案的模型,在改装回调中我操纵ObjectResponse来提取我的List然后传递给我的适配器。
现在,我有类似的东西
Call<ObjectResponse> call = apiInterface.getMyWholeObject();
call.enqueue(new Callback<ObjectResponse>() {
@Override
public void onResponse(Call<ObjectResponse> call, Response<ObjectResponse> response) {
//various manipulation based on response.body() that in the ends
// lead to a List<SingleObject>
mView.sendToAdapter(listSingleObject)
}
@Override
public void onFailure(Call<ObjectResponse> call,
Throwable t) {
t.printStackTrace();
}
});
我的问题是:
有没有办法从改造中获得一个Observable,它可以最终导致我发出SingleObject列表(并操纵它),而不必像改装回调那样操纵ObjectResponse?或者我是否应该坚持使用改装回调,并且只有在obatin List之后我才可以在将此列表提供给我的适配器之前使用RxJava进行操作?
我想获得这样的东西
apiInterface
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<SingleObject>>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(List<Post> posts) {
mView.sendToAdapter(listSingleObject)
}
});
答案 0 :(得分:5)
改装转换器工厂很好地解决了这个问题。
杰克沃顿在他的"Making Retrofit Work For You"演讲中谈论&#34;信封&#34; 对象,并指出如何解决这个问题。
定义了一个信封类 - 一个包含一些您不关心的额外字段的类:
public class Envelope<T> {
Meta meta;
List<Notification> notifications;
T response;
}
在这个POJO字段meta
和List<Notification>
从后端返回,但在Android应用程序的上下文中,它们对我们并不感兴趣。假设您从响应中获得的实际值是名为response
的字段,该字段可能是任何对象(因为它是通用的)。
特别是在您的示例中,POJO结构将如下所示:
public class OriginalResponse {
HigherLevel higher_level;
}
public class HigherLevel {
Data data;
}
public class Data {
List<ActualData> list;
}
您必须实施自定义Converter.Factory
:
public class EnvelopingConverter extends Converter.Factory {
@Nullable
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
Type envelopedType = TypeToken.getParameterized(Envelope.class, type).getType();
Converter<ResponseBody, Envelope<?>> delegate = retrofit.nextResponseBodyConverter(this, envelopedType, annotations);
return body -> {
Envelope<?> envelope = delegate.convert(body);
// Here return the object that you actually are interested
// in your example it would be:
// originalResponse = delegate.convert(body);
// return originalResponse.higher_level.data.list;
return envelope.response;
};
}
}
将此转换器添加到改装构建器中:
new Retrofit.Builder()
...
.addConverterFactory(new EnvelopingConverter())
...
然后在您的改造api界面中,而不是直接返回Single<OriginalResponse>
返回Single<List<ActualData>>
:
interface Service {
@GET(...)
Single<List<ActualData>> getFoo();
}
答案 1 :(得分:2)
据我所知,没有办法做到这一点。为了获得最佳实践,您应该创建一个Facade
层(可能是ApiManager
类)来管理所有API。在这种情况下,您可以使用map/flatMap
将ObjectResponse
映射到SingleObject
,如:
public Observable<List<SingleObject>> getSingleObjects(){
return ServiceGenerator.getApiMethods().getObjectResponse.map(new Function<ObjectResponse, List<SingleObject>>() {
@Override
public List<SingleObject> apply(ObjectResponse response) throws Exception {
return reponse.getListSingleObjects();
}
})
}
答案 2 :(得分:1)
几天后,我可以发布自己的解决方案。
它的灵感来自azizbekian建议的想法。
中心的想法是在Envelope类上,我用改装注释来表达它,以确保它能适应来自服务器的不同JSON响应,解析
higher_level: {
data: [
mid_level: { .. },
...
]
}
我已经在原帖中解释的结构
public class WrapperResponse<T> {
@SerializedName(value="higher_level", alternate={"mid_level", "other"})
@Expose
DataResponse<T> data;
public DataResponse<T> getData() {
return data;
}
public void setData(DataResponse<T> data) {
this.data = data;
}
}
这里的重点是SerializedName的参数,其中我指定了我的服务器响应中出现的所有可能的JSON对象名称。
然后我
public class UnwrapConverterFactory extends Converter.Factory {
private GsonConverterFactory factory;
public UnwrapConverterFactory(GsonConverterFactory factory) {
this.factory = factory;
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(final Type type,
Annotation[] annotations, Retrofit retrofit) {
Type wrappedType = new ParameterizedType() {
@Override
public Type[] getActualTypeArguments() {
return new Type[] {type};
}
@Override
public Type getOwnerType() {
return null;
}
@Override
public Type getRawType() {
return WrapperResponse.class;
}
};
Converter<ResponseBody, ?> gsonConverter = factory
.responseBodyConverter(wrappedType, annotations, retrofit);
return new WrapperResponseBodyConverter(gsonConverter);
}
}
和
public class WrapperResponseBodyConverter<T>
implements Converter<ResponseBody, T> {
private Converter<ResponseBody, WrapperResponse<T>> converter;
public WrapperResponseBodyConverter(Converter<ResponseBody,
WrapperResponse<T>> converter) {
this.converter = converter;
}
@Override
public T convert(ResponseBody value) throws IOException {
WrapperResponse<T> response = converter.convert(value);
return response.getData().getData();
}
}
在我的Retrofit Module(dagger2)中使用,以确保我的Retrofit客户端使用通用的WrapperResponse从服务器解包任何答案,最后,我可以将Retrofit方法写为
@GET("locations")
Observable<List<Location>> getLocation();
其中List正是我想要获得的结果:直接来自Retrofit响应的对象列表,我可以用RxJava进一步详细说明。
谢谢大家。