如何获取JsonReader字节以获得百分比



我需要下载包含20k个项目的Json文件,同时我需要在文本视图中显示百分比。现在我只测试代码流,所以我显示了一个带有当前百分比的简单日志。所以我创建了一个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两个不同的值。那么,如何在while循环中获得从JsonReader下载的有效字节的值呢?

感谢

您可以重新思考您的进度模型,使其更加简单和分离。如果将进度状态封装到InputStream装饰器中,并在读取时将其公开,该怎么办?

它是如何工作的?首先,您必须封装要装饰的真实输入流。还需要一些中间状态来计数读取字节并将该值与期望的长度值进行比较。一旦某个事件在某种条件下发生,只需通过已经封装的订阅者激发比率值。下面的输入流装饰器使用Float比率,其中值总是在[0;1]的范围内。为什么?让您的视图决定应如何渲染标准化比率:文本视图中的百分比、进度条或其他任何内容。Percentage基本上只是一个对人类友好的未规范化值,而在给定ratio时,您确保始终通过0..1值,而不关心生成器站点的"用户友好性"(想象一下,如果有一天您将暴露promille,hm-m-m——这会在其他地方破坏您的代码,而不是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;
}
}

现在,将进度计算器封装在decorator中,典型的用法如下:

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/等。

我只在桌面系统上测试了它,而不是在真正的设备上测试(没有活动、UI线程或HTTP网络,只有JSON文件和stdout输出),但这个概念可以很容易地迁移到Android系统,只需最少的努力。以下是长度为84047:的文件的输出

阅读:0%
阅读:9%
读:19%
读取:29%
读:38%
1阅读:48%
2阅读:58%
3阅读:68%
5阅读:77%
0阅读:87%
Read:97%
<100%>
已下载

最新更新