2017-01-11 5 views
1

Мне нужно загрузить Json-файл с 20k-элементами, и в то же время мне нужно показать процент в текстовом виде. Теперь я проверяю только поток кода, поэтому я показываю простой журнал с текущим процентом. Поэтому я создать Observable и я делаю это:Как получить JsonReader байт, чтобы сделать процент

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 (эффективное количество байт) и переменной общего. Итак, как я могу получить значение эффективного байта, загруженного из JsonReader во время цикла?

Благодаря

ответ

1

Вы можете пересмотреть свою модель прогресса для того, чтобы сделать его более простым и отделены друг от друга. Что делать, если вы инкапсулируете состояние прогресса в InputStreamdecorator и выставляете его во время чтения?

Как это работает? Прежде всего, вы должны инкапсулировать реальный поток ввода, который вы собираетесь украсить. Также необходимо некоторое промежуточное состояние для подсчета числа байтов и сравнения этого значения с ожидаемым значением длины. Как только определенное событие возникает при некоторых условиях, просто запустите значение отношения через подписчика, который уже инкапсулирован. Декоратор входного потока ниже использует отношение Float, где значения всегда находятся в диапазоне [0;1]. Зачем? Пусть ваше мнение решит , какнормализованный Отношение должно отображаться: процент в текстовом виде или индикатор выполнения или что-то еще. Процент в основном просто дружелюбный для человека ненормализованный значение, при коэффициенте дает вам быть всегда0..1 значения и не заботятся о «удобстве использования» на сайте генератора (представьте, что если когда-нибудь вы откроете промилле , hm-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.

Я тестировал его только на настольной системе, а не на реальном устройстве (без действий, потоков пользовательского интерфейса или HTTP-сети, только с файлом JSON и выходом stdout), но эту концепцию можно легко перенести в систему Android с минимальными усилия. Вот выход для файла с длиной 84047:

чтения: 0%
чтения: 9%
чтения: 19%
чтения: 29%
чтения: 38%
чтения: 48%
чтения: 58%
чтения: 68%
чтения: 77%
чтения: 87%
чтения: 97%
Прочитано: 100%
Скачано