Retrofit2和流式序列化对象

时间:2017-02-05 13:00:28

标签: gson retrofit2

我正在使用Retrofit2。到目前为止,我尝试发送和接收java.util.List<MyCustomClass>并且它工作得非常好。然而,我想知道真正的大名单会发生什么。有没有办法做更多&#34;流媒体&#34;接口

例如,我可以解析为api Iterator<MyCustomClass>而不是List并在飞行时创建每个下一个实例,以便在向API发送数据时节省内存。相同的Iterator<MyCustomClass>用于相反的方向。

有没有办法实现这个目标?

我可以看到Gson能够进行流序列化和反序列化。 Can Retrofit可以利用它吗?

编辑:澄清一点问题。

1 个答案:

答案 0 :(得分:2)

一个有趣的问题。首先,您必须考虑到ListIterable在语义上有所不同,尤其是在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()); } } 是你的朋友。