我正在使用Retrofit2。到目前为止,我尝试发送和接收java.util.List<MyCustomClass>
并且它工作得非常好。然而,我想知道真正的大名单会发生什么。有没有办法做更多&#34;流媒体&#34;接口
例如,我可以解析为api Iterator<MyCustomClass>
而不是List
并在飞行时创建每个下一个实例,以便在向API发送数据时节省内存。相同的Iterator<MyCustomClass>
用于相反的方向。
有没有办法实现这个目标?
我可以看到Gson能够进行流序列化和反序列化。 Can Retrofit可以利用它吗?
编辑:澄清一点问题。
答案 0 :(得分:2)
一个有趣的问题。首先,您必须考虑到List
和Iterable
在语义上有所不同,尤其是在I / O范围内。如果从I / O角度来看,列表用于将元素收集到内存中对象中,然后立即关闭底层资源。我认为,Itera不会引入Gson,因为它太懒惰并且从输入流中推迟了元素评估:iterables应该能够返回新的迭代器按设计。除非请求另一个I / O资源读取,否则无法创建新的迭代器。这就是混乱的来源。我最近为Spring Framework(MVC模块)和Java 8流解决了一个非常类似的问题,我似乎也有一个Retrofit解决方案。但是,需要非常小心地使用Retrofit才能正确使用并防止资源泄漏。请注意,您不能在此处使用迭代,但您可以使用迭代器(Iterator
):它们在语义上非常类似于Java 8流(Stream
)和Java I / O流({ {1}}和InputStream
),因为不能重复使用。但是,了解差异,您仍然可以使用OutputStream
个实例。
如果您想进行更多实验,请在Python中创建一个简单的静态HTTP Web服务器:
Iterator
此网络服务始终会返回from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer
PORT_NUMBER = 8080
class myHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type','text/json')
self.end_headers()
# Send the html message
self.wfile.write('["foo","bar","baz"]')
return
try:
server = HTTPServer(('', PORT_NUMBER), myHandler)
print 'Started httpserver on port ' , PORT_NUMBER
server.serve_forever()
except KeyboardInterrupt:
print '^C received, shutting down the web server'
server.socket.close()
未考虑的请求。现在创建一个示例服务来使用它:
["foo","bar","baz"]
其次,Gson默认不使用iterables或迭代器,因此需要实现自定义(de)序列化模块。下面的类型适配器工厂为迭代器创建一个特殊类型的适配器,或者让Gson选择另一个先前的策略(如果存在):
interface IService {
@GET("/")
Call<Iterator<String>> get();
}
接下来,迭代器类型适配器实现如下。请注意,将迭代器写入流非常容易,但读取要复杂得多:
final class IteratorTypeAdapterFactory
implements TypeAdapterFactory {
private static final TypeAdapterFactory iteratorTypeAdapterFactory = new IteratorTypeAdapterFactory();
private IteratorTypeAdapterFactory() {
}
static TypeAdapterFactory getIteratorTypeAdapterFactory() {
return iteratorTypeAdapterFactory;
}
@Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type) {
if ( Iterator.class.isAssignableFrom(type.getRawType()) ) {
@SuppressWarnings("unchecked")
final TypeAdapter<T> castTypeAdapter = (TypeAdapter<T>) getIteratorTypeAdapter(getIteratorParameterType(type.getType()), gson);
return castTypeAdapter;
}
return null;
}
}
阅读迭代器:
final class IteratorTypeAdapter<T>
extends TypeAdapter<Iterator<T>> {
private final Type elementType;
private final Gson gson;
private IteratorTypeAdapter(final Type elementType, final Gson gson) {
this.elementType = elementType;
this.gson = gson;
}
static <T> IteratorTypeAdapter<Iterator<T>> getIteratorTypeAdapter(final Type elementType, final Gson gson) {
return new IteratorTypeAdapter<>(elementType, gson);
}
@Override
@SuppressWarnings("resource")
public void write(final JsonWriter out, final Iterator<T> iterator)
throws IOException {
out.beginArray();
while ( iterator.hasNext() ) {
final T next = iterator.next();
gson.toJson(next, elementType, out);
}
out.endArray();
}
@Override
public Iterator<T> read(final JsonReader in) {
return getJsonReaderIterator(elementType, gson, in);
}
}
请注意,迭代器必须是可关闭的才能关闭底层资源。说实话,我不喜欢Java中final class JsonReaderIterator<T>
implements Iterator<T>, Closeable {
private final Type elementType;
private final Gson gson;
private final JsonReader in;
private ReadingIteratorState state = ReadingIteratorState.BEFORE_ARRAY;
private JsonReaderIterator(final Type elementType, final Gson gson, final JsonReader in) {
this.elementType = elementType;
this.gson = gson;
this.in = in;
}
static <T> Iterator<T> getJsonReaderIterator(final Type elementType, final Gson gson, final JsonReader in) {
return new JsonReaderIterator<>(elementType, gson, in);
}
@Override
public boolean hasNext() {
try {
if ( state == ReadingIteratorState.END_OF_STREAM ) {
return false;
}
final boolean hasNext;
loop:
for ( ; ; ) {
switch ( state ) {
case BEFORE_ARRAY:
if ( in.peek() == BEGIN_ARRAY ) {
in.beginArray();
state = ReadingIteratorState.WITHIN_ARRAY;
}
continue;
case WITHIN_ARRAY:
if ( in.peek() == END_ARRAY ) {
in.endArray();
state = ReadingIteratorState.END_OF_STREAM;
continue;
}
hasNext = true;
break loop;
case AFTER_ARRAY:
hasNext = false;
state = ReadingIteratorState.END_OF_STREAM;
break loop;
case END_OF_STREAM:
hasNext = false;
break loop;
default:
throw new AssertionError(state);
}
}
return hasNext;
} catch ( final IOException ex ) {
throw new RuntimeException(ex);
}
}
@Override
public T next() {
try {
if ( !in.hasNext() || state == ReadingIteratorState.END_OF_STREAM ) {
throw new NoSuchElementException();
}
final T element;
loop:
for ( ; ; ) {
switch ( state ) {
case BEFORE_ARRAY:
in.beginArray();
state = ReadingIteratorState.WITHIN_ARRAY;
if ( in.peek() == END_ARRAY ) {
state = ReadingIteratorState.END_OF_STREAM;
}
break;
case WITHIN_ARRAY:
element = gson.fromJson(in, elementType);
if ( in.peek() == END_ARRAY ) {
state = ReadingIteratorState.AFTER_ARRAY;
}
break loop;
case AFTER_ARRAY:
in.endArray();
state = ReadingIteratorState.END_OF_STREAM;
break;
case END_OF_STREAM:
throw new NoSuchElementException(String.valueOf(state));
default:
throw new AssertionError(state);
}
}
return element;
} catch ( final IOException ex ) {
throw new RuntimeException(ex);
}
}
@Override
public void close()
throws IOException {
in.close();
}
private enum ReadingIteratorState {
BEFORE_ARRAY,
WITHIN_ARRAY,
AFTER_ARRAY,
END_OF_STREAM
}
}
方法的契约,该方法需要关闭底层资源的方法,我相信关闭资源是打开资源的对象的责任。例如,这样的迭代器不需要在Spring MVC中关闭,因为框架本身监听HTTP请求,让处理程序处理请求,然后自己关闭请求。在Retrofit资源中,如果暴露迭代器,就会发生泄漏,因此这就是为什么这个实现具有close
方法实现的原因。
接下来:配置Retrofit以使用close
- 感知Gson实例。下面有一个转换器工厂可以使用惰性迭代器。请注意,默认的Iterator
实现不允许使用迭代器,因为它会在处理它们并转换为迭代器之前关闭输入流。其余两种方法可以从默认实现中重复使用,但是不必从外部传递它并且可以私下实例化。另请注意,工厂的响应转换器仅适用于最顶层对象,不需要关闭可能是其他对象字段的迭代器。
GsonConverterFactory
上面的代码使用了一些反射实用程序:
final class CustomGsonConverterFactory
extends Factory {
private final Gson gson;
private final GsonConverterFactory gsonConverterFactory;
private CustomGsonConverterFactory(final Gson gson, final GsonConverterFactory gsonConverterFactory) {
this.gson = gson;
this.gsonConverterFactory = gsonConverterFactory;
}
static Factory getCustomGsonConverterFactory(final Gson gson, final GsonConverterFactory gsonConverterFactory) {
return new CustomGsonConverterFactory(gson, gsonConverterFactory);
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(final Type type, final Annotation[] annotations, final Retrofit retrofit) {
final TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
final boolean isClosedElsewhere = isIterator(type);
return (Converter<ResponseBody, Object>) responseBody -> {
try {
return adapter.read(gson.newJsonReader(responseBody.charStream()));
} finally {
if ( !isClosedElsewhere ) {
responseBody.close();
}
}
};
}
@Override
public Converter<?, RequestBody> requestBodyConverter(final Type type, final Annotation[] parameterAnnotations, final Annotation[] methodAnnotations,
final Retrofit retrofit) {
return gsonConverterFactory.requestBodyConverter(type, parameterAnnotations, methodAnnotations, retrofit);
}
@Override
public Converter<?, String> stringConverter(final Type type, final Annotation[] annotations, final Retrofit retrofit) {
return gsonConverterFactory.stringConverter(type, annotations, retrofit);
}
}
下面的代码将使用以下可关闭的迭代器实用程序来关闭I / O资源(还记得转换器工厂创建的转换器不会为迭代器关闭资源吗?)。
final class Reflection {
private Reflection() {
}
static Type getIteratorParameterType(final Type type)
throws IllegalArgumentException {
return getTParameterType(type, Iterator.class);
}
static boolean isIterator(final Type type) {
return Iterator.class.equals(type)
|| type instanceof ParameterizedType && Iterator.class.equals(((ParameterizedType) type).getRawType());
}
private static Type getTParameterType(final Type type, final Type expectedParameterizedType)
throws IllegalArgumentException {
if ( expectedParameterizedType.equals(type) ) {
return expectedParameterizedType;
}
if ( type instanceof ParameterizedType ) {
final ParameterizedType parameterizedType = (ParameterizedType) type;
if ( expectedParameterizedType.equals(parameterizedType.getRawType()) ) {
final Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
if ( actualTypeArguments.length == 1 ) {
return actualTypeArguments[0];
}
}
}
throw new IllegalArgumentException(String.valueOf(type));
}
}
正如您所看到的,上面的所有方法都尝试关闭给定迭代器以释放I / O资源。以下是所有这些的工作原理:
final class CloseableIterators {
private CloseableIterators() {
}
static <T> void forEachAndClose(final Iterator<? extends T> iterator, final Consumer<? super T> consumer)
throws Exception {
try {
while ( iterator.hasNext() ) {
consumer.accept(iterator.next());
}
} finally {
tryClose(iterator);
}
}
static <T> List<T> collectToListAndClose(final Iterator<? extends T> iterator)
throws Exception {
final List<T> list = new ArrayList<>();
forEachAndClose(iterator, list::add);
return unmodifiableList(list);
}
static void tryClose(final Object object)
throws Exception {
if ( object instanceof AutoCloseable ) {
((AutoCloseable) object).close();
}
}
}
请注意,final Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(getIteratorTypeAdapterFactory())
.create();
final Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://localhost")
.addConverterFactory(getCustomGsonConverterFactory(gson))
.build();
final IService service = retrofit.create(IService.class);
final Call<Iterator<String>> getCall = service.get();
getCall.enqueue(new Callback<Iterator<String>>() {
@Override
public void onResponse(final Call<Iterator<String>> call, final Response<Iterator<String>> response) {
try {
final Iterator<String> iterator = response.body();
if ( ... ) {
forEachAndClose(iterator, out::println);
} else if ( ... ) {
out.println(collectToListAndClose(iterator));
} else {
tryClose(iterator);
}
} catch ( final Exception ex ) {
throw new RuntimeException(ex);
}
}
@Override
public void onFailure(final Call<Iterator<String>> call, final Throwable throwable) {
throwable.printStackTrace(err);
}
});
必须关闭最顶层的迭代器对象本身,否则特定请求资源将被泄露:即使没有预期的交互,onResponse
也必须被调用以关闭tryClose
实例。但是,根据迭代器的工作方式,您可以使用它们一次。
编辑:可关闭的迭代器增强功能
在这里,我认为至少有两种方法可以使它们更加健壮。考虑一个可以关闭的迭代器
JsonReaderIterator
此界面可用于上面的interface IAutoCloseableIterator<E>
implements Iterator<E>, AutoCloseable {
}
。然后你可以:
JsonReaderIterator
; Call<IAutoCloseableIterator<E>>
:AutoCloseable
然后(第一个选项):
static <E, I extends Iterator<E> & AutoCloseable> I asAutoCloseable(final Iterator<E> iterator) {
final Iterator<E> resultIterator;
if ( iterator instanceof AutoCloseable ) {
resultIterator = iterator;
} else {
resultIterator = new IAutoCloseableIterator<E>() {
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public E next() {
return iterator.next();
}
@Override
public void close() {
// do nothing or whatever elsewhere if there is no way to implement the method
}
};
}
@SuppressWarnings("unchecked")
final I castIterator = (I) resultIterator;
return castIterator;
}
或(对于第二种选择;可能不那么健壮......):
try ( final IAutoCloseableIterator<String> iterator = response.body() ) {
while ( iterator.hasNext() ) {
out.println(iterator.next());
}
}
try ( final IAutoCloseableIterator<String> iterator = asAutoCloseable(response.body()) ) {
while ( iterator.hasNext() ) {
out.println(iterator.next());
}
}
是你的朋友。