2017-02-14 39 views
1

У меня есть приложение, которое может записывать и загружать видеофайл. У меня есть механизм, который регистрирует onFailure Throwable при каждой загрузке файла.Что не так с скоростью загрузки некоторых устройств с помощью Multipart OkHttp3/Retrofit 2

Я заметил, что из тысячи устройств, использующих приложение, есть некоторые, которые имеют постоянные тайм-ауты и имеют плохой коэффициент загрузки.

Проблематика устройство: Asus ZenPad C 7,0 Z170C

Я купил прибор и начал испытывать его, но первый позволяет дать вам вкус того, что код я использую.

В деятельности:

FileUploadService service = ServiceGenerator.createService(FileUploadService.class); 

    File file = new File(getRealPathFromURI(mediaForUpload.mediaUri)); 
    ProgressRequestBody fileBody = new ProgressRequestBody(file, 
      new ProgressRequestBody.UploadCallbacks() { 
       @Override 
       public void onProgressUpdate(int uploadPercentage) { 
        // set current progress 
       } 
       @Override 
       public void onError() {} 
       @Override 
       public void onFinish() {} 
      }); 

    MultipartBody.Part body = MultipartBody.Part.createFormData(mediaForUpload.mediaNameWithExtension, mediaForUpload.mediaNameWithExtension, fileBody); 

    Call<ResponseBody> call = service.upload(body); 
    call.enqueue(new Callback<ResponseBody>() { 
     @Override 
     public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) { 
      if (response.isSuccessful()) { 
      } else { 
       //Log 
      } 
     } 

     @Override 
     public void onFailure(Call<ResponseBody> call, Throwable t) { 
      //Log 
     } 
    }); 

В ServiceGenerator:

public static <S> S createService(Class<S> serviceClass) { 
     OkHttpClient client = new OkHttpClient.Builder() 
       .connectTimeout(120, TimeUnit.SECONDS) 
       .writeTimeout(120, TimeUnit.SECONDS) 
       .readTimeout(120, TimeUnit.SECONDS) 
       .build(); 

     Retrofit.Builder builder = 
       new Retrofit.Builder() 
         .baseUrl("https://some.nice.api/") 
         .addConverterFactory(GsonConverterFactory.create()); 

     Retrofit retrofit = builder.client(client).build(); 
     return retrofit.create(serviceClass); 
    } 

В FileUploadService:

@Multipart 
@POST("/somepath") 
Call<ResponseBody> upload(@Part MultipartBody.Part file); 

Так как вы можете видеть, что мой тайм-аут составляет 120 секунд, это интересно, но этого недостаточно, чтобы успешно загрузить 15MB видеофайл без тайм-аута.

Я сделал пару тест из Asus ZenPad C 7.0 и Samsung Tab 3 7" просто чтобы показать вам разницу (Тест на 2,4 сети, в 5 метрах от маршрутизатора;. Tab 3 средняя скорость загрузки: 1 Мб/с, средняя скорость загрузки ZenPad C7: ~ 30KB/s, если происходит чудо)

Скриншоты из Android Monitor:

  • Samsung Tab 3 7"

Samsung Tab 3

  • Asus ZenPad C 7,0

неудачу с 120 тайм-аут после этого: failed after 120 seconds

успех после этих двух, но в ожидании 1:35 минут: enter image description here enter image description here

Я протестировал загрузку OkHttp3 Multipart, чтобы быть уверенным, что это не проблема Retrofit 2, и результаты те же.

+0

Вы пробовали использовать профайлер выборки? Можно пролить свет на то, что он тратит все свое время. –

+0

Это может помочь: http://www.android-ide.com/blog/2014/01/26/using-androids-hidden-sampling-profiler/ –

ответ

1

После двух дней поиска дополнительной информации я нашел проблему.

Сначала вы должны знать, что фабрика сокетов по умолчанию имеет getSendBufferSize() из 524288. В нашем случае это много для Asus Zenpad C 7.0.

Мои шаги нахождения решения являются:

https://github.com/square/okhttp/issues/1078

и реализации RestrictedSocketFactoryhttps://gist.github.com/slightfoot/00a26683ea68856ceb50e26c7d8a47d0

После этого я сделал некоторые изменения в мой код:

OkHttpClient client = new OkHttpClient.Builder() 
      .connectTimeout(120, TimeUnit.SECONDS) 
      .writeTimeout(120, TimeUnit.SECONDS) 
      .readTimeout(120, TimeUnit.SECONDS) 
      .socketFactory(new RestrictedSocketFactory(bufferSize)) 
      .build(); 

Мой bufferSize что это сокет sendBufferSize - тот же размер, что и мой DEFAULT_BUFFER_SIZE от ProgressRequestBody. Я тестировал с 256 * 1024 и 128 * 1024, а оптимальный для C 7.0 - 128 * 1024. К сожалению, скорость загрузки для всех других устройств была уменьшена на ~ 30%, поэтому я создал механизм, который применяет эти изменения только для нескольких устройств. (Из моих тестов оптимальным для всех других устройств является 256 * 1024 как bufferSize и DEFAULT_BUFFER_SIZE)

Вот изображение загрузки видеофайла 17 МБ с Asus Zenpad C 7.0 вы можете видеть отличие от первого сообщения.

enter image description here