如何让JsonReader字节产生一个百分比

时间:2017-01-11 09:33:56

标签: android json gson rx-java

我需要下载带有20k项目的Json文件,同时我需要在textview中显示一个百分比。现在我只测试代码的流程,所以我用当前百分比显示简单的日志。所以我创建了一个Observable并且我这样做了:

private void downloadAirports()
{
    final OkHttpClient mOkHttpClient = new OkHttpClient();

    final Request mRequest = new Request.Builder().url(SERVICE_ENDPOINT).build();

    Observable.create(new Observable.OnSubscribe<String>()
    {
        @Override
        public void call(Subscriber<? super String> subscriber)
        {
            try {
                InputStream inputStream;
                okhttp3.Response response = mOkHttpClient.newCall(mRequest).execute();
                if (response.isSuccessful())
                {
                    inputStream = response.body().byteStream();
                   long len = response.body().contentLength();

                    Log.d("str",String.valueOf(len));

                    String progress = "0";
                    subscriber.onNext(progress);

                    final int bufferSize = 1024;
                    boolean flag = false;
                    final char[] buffer = new char[bufferSize];
                    final StringBuilder out = new StringBuilder();
                    Reader in = new InputStreamReader(inputStream, "UTF-8");

                    long total = 0;
                    airp = new ArrayList<AirportObject>();
                    int count =0;

                    Gson gson = new Gson();
                    JsonReader reader = new JsonReader(new InputStreamReader(inputStream, "UTF-8"));
                    airp = new ArrayList<>();
                    long i = 0;
                    reader.beginArray();

                    while (reader.hasNext())
                    {

                        AirportObject message = gson.fromJson(reader, AirportObject.class);
                        airp.add(message);
                        i++;
                        byte [] arr = message.toString().getBytes();
                        total = total + arr.length;

                        Log.d("%",String.valueOf(total));

                        double p = total/len * 100;


                        subscriber.onNext(String.valueOf(p));
                    }


                    reader.endArray();
                    reader.close();






                    //airp = Arrays.asList(airportArray);


                    subscriber.onCompleted();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }).subscribeOn(Schedulers.newThread())
            .subscribe(new Subscriber<String>() {

                long size, perc;
                public void onCompleted()
                {
                    Log.wtf("on complete","On complete");
                }

                @Override
                public void onError(Throwable e)
                {
                    e.printStackTrace();
                }

                @Override
                public void onNext(final String progress) {
                    getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run()
                        {
                     //       Log.d("%",progress);
                           // textView.setText("Download aeroporti in corso:"+progress+"%");
                        }
                    });
                }
            });
}

但我给变量len(有效字节数)和变量total赋予两个不同的值。那么如何在循环中获取从JsonReader下载的有效字节的值?

由于

1 个答案:

答案 0 :(得分:1)

您可以重新考虑您的进度模型,以使其更简单和分离。如果您将进度状态封装到InputStream decorator并在阅读时公开它会怎样?

它是如何工作的?首先,您必须封装要装饰的实际输入流。还需要一些中间状态来计算读取字节并将该值与预期长度值进行比较。一旦某个条件发生某个事件,只需通过已经封装的用户触发比率值。下面的输入流装饰器使用Float比率,其中值始终在[0;1]的范围内。为什么?让您的视图决定应如何 规范化比率:文本视图中的百分比,或进度条,或其他任何内容。百分比基本上只是一个人性化的非标准化值,同时给出比率,确保始终传递0..1值并且不关心“用户友好性“在发电机现场(想象一下,如果有一天你会暴露promille,嗯-mm - 那会破坏你的代码,而这些代码会产生百分比而不是promilles)。

public final class ProgressInputStream
        extends InputStream {

    private final Subscriber<? super Float> subscriber;
    private final InputStream inputStream;
    private final long expectedLength;
    private final long lengthPerPercent;

    private long actualLength;
    private long currentChunkLength;

    private ProgressInputStream(final Subscriber<? super Float> subscriber, final InputStream inputStream, final long expectedLength) {
        this.subscriber = subscriber;
        this.inputStream = inputStream;
        this.expectedLength = expectedLength;
        lengthPerPercent = (long) ceil((double) expectedLength / 100);
    }

    public static InputStream progressInputStream(final Subscriber<? super Float> subscriber, final InputStream inputStream, final long expectedLength) {
        return new ProgressInputStream(subscriber, inputStream, expectedLength);
    }

    @Override
    public int read()
            throws IOException {
        return (int) count(inputStream.read());
    }

    @Override
    public int read(final byte[] bytes)
            throws IOException {
        return (int) count(inputStream.read(bytes));
    }

    @Override
    public int read(final byte[] bytes, final int offset, final int length)
            throws IOException {
        return (int) count(inputStream.read(bytes, offset, length));
    }

    @Override
    public long skip(final long n)
            throws IOException {
        return count(inputStream.skip(n));
    }

    @Override
    public void close()
            throws IOException {
        inputStream.close();
    }

    private long count(final long read) {
        if ( read != -1 ) {
            if ( actualLength == 0 ) {
                subscriber.onNext(0F);
            }
            currentChunkLength += read;
            actualLength += read;
            if ( currentChunkLength >= lengthPerPercent ) {
                currentChunkLength = 0;
                if ( actualLength < expectedLength ) {
                    subscriber.onNext((float) actualLength / expectedLength);
                } else if ( actualLength == expectedLength ) {
                    subscriber.onNext(1F);
                    subscriber.onCompleted();
                } else {
                    throw new AssertionError("Must never happen. A bug in the code around?");
                }
            } else if ( actualLength == expectedLength ) {
                subscriber.onNext(1F);
                subscriber.onCompleted();
            }
        }
        return read;
    }

}

现在,将进度计算器封装在装饰器中,典型用法可能如下所示:

Observable
        .<Float>create(subscriber -> {
            final File file = new File("/tmp/some.json");
            try ( final InputStream inputStream = progressInputStream(subscriber, new BufferedInputStream(new FileInputStream(file)), file.length());
                  final JsonReader reader = new JsonReader(new InputStreamReader(inputStream, "UTF-8")) ) {
                reader.beginArray();
                while ( reader.hasNext() ) {
                    gson.<AirportObject>fromJson(reader, AirportObject.class);
                }
                reader.endArray();
            } catch ( final IOException ex ) {
                throw new RuntimeException(ex);
            }
        })
        .subscribe(new Subscriber<Float>() {
            @Override
            public void onNext(final Float ratio) {
                out.printf("Read: %s%%\n", (long) (ratio * 100));
            }

            @Override
            public void onCompleted() {
                out.println("Downloaded");
            }

            @Override
            public void onError(final Throwable ex) {
                throw new RuntimeException(ex);
            }
        });

检查现在您在解析JSON时不必计算进度,从而使代码更清晰。此外,您可以在其他地方重复使用此类流,而不仅仅是Gson / etc。

我仅在桌面系统上测试它,而不是在真实设备上测试(没有活动,UI线程或HTTP网络,只有JSON文件和标准输出),但这个概念可以轻松地迁移到Android系统。以下是长度为84047的文件的输出:

  

阅读:0%
  阅读:9%
  阅读:19%
  阅读:29%
  阅读:38%
  阅读:48%
  阅读:58%
  阅读:68%
  阅读:77%
  阅读:87%
  阅读:97%
  阅读:100%
  下载