2017-01-25 13 views
0

У меня есть изображение «менеджер», который загружает изображения. Раньше я использовал библиотеку Пикассо для этого, как следуетokhttp3 слишком много дескрипторов файлов выпуск

class DownloadImage implements Runnable { 

    String url; 
    Context context; 

    public DownloadImage(String url, Context context) { 
     this.url = url; 
     this.context = context; 
    } 

    @Override 
    public void run() { 
     try { 
      String hash = Utilities.getSha1Hex(url); 
      FileOutputStream fos = context.openFileOutput(hash, Context.MODE_PRIVATE); 
      Bitmap bitmap = Picasso.with(context) 
        .load(url) 
        .resize(1024, 0) // Height 0 to ensure the image is scaled with respect to width - http://stackoverflow.com/a/26782046/1360853 
        .onlyScaleDown() 
        .memoryPolicy(MemoryPolicy.NO_CACHE) 
        .get(); 

      // Writing the bitmap to the output stream 
      bitmap.compress(Bitmap.CompressFormat.JPEG, 80, fos); 
      fos.close(); 
      bitmap.recycle(); 
     } catch (IOException e) { 
      Timber.e(e, "For url %s", url); 
     } catch (OutOfMemoryError e) { 
      Timber.e(e, "out of memory for url %s", url); 
     } 
    } 
} 

Но это создает объект Bitmap, который не только потребляет много памяти, также значительно медленнее и ненужные.

Я модифицировал этот Runnable использовать okhttp3 вместо:

class DownloadImage implements Runnable { 

    String url; 
    Context context; 

    public DownloadImage(String url, Context context) { 
     this.url = url; 
     this.context = context; 
    } 

    @Override 
    public void run() { 
     try { 
      String hash = Utilities.getSha1Hex(url); 
      final FileOutputStream fos = context.openFileOutput(hash, Context.MODE_PRIVATE); 
      Request request = new Request.Builder().url(url).build(); 
      okHttpClient.newCall(request).enqueue(new Callback() { 
       @Override 
       public void onFailure(Call call, IOException e) { 
        try { 
         fos.close(); 
        } catch (IOException e1) { 
         e1.printStackTrace(); 
        } 
       } 

       @Override 
       public void onResponse(Call call, Response response) throws IOException { 
        Sink sink = null; 
        BufferedSource source = null; 
        try { 
         source = response.body().source(); 
         sink = Okio.sink(fos); 
         source.readAll(sink); 
        } catch (Exception e) { 
         Timber.e(e, "Downloading an image went wrong"); 
        } finally { 
         if (source != null) { 
          source.close(); 
         } 
         if (sink != null) { 
          sink.close(); 
         } 
         fos.close(); 
         okHttpClient.connectionPool().evictAll(); // For testing 
        } 
       } 
      }); 
     } catch (IOException e) { 
      Timber.e(e, "For url %s", url); 
     } 
    } 
} 

Хотя такой подход намного быстрее, чем предыдущий, для большого количества изображений, я получаю A/libc: FORTIFY_SOURCE: FD_SET: file descriptor >= FD_SETSIZE. Calling abort(). с последующим microdump, что означает, у меня есть открыто слишком много файловых дескрипторов.

Для проверки цели я добавил линию okHttpClient.connectionPool().evictAll(); // For testing, но это не сработало. Я также попытался установить builder.connectionPool(new ConnectionPool(4, 500, TimeUnit.MILLISECONDS)); при построении okHttpClient, но это тоже ничего не сделало. Мне также известно https://github.com/square/okhttp/issues/2636

Я, кажется, закрываю все потоки/раковины/источники, так что здесь происходит?

В runnables добавляют к ThreadPoolExecutor используя свою execute функцию, которая создается следующим образом:

// Sets the amount of time an idle thread waits before terminating 
private static final int KEEP_ALIVE_TIME = 500; 
// Sets the Time Unit to milliseconds 
private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.MILLISECONDS; 
private static int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors(); 

// A queue of Runnables 
private final BlockingQueue<Runnable> mDecodeWorkQueue; 
private OkHttpClient okHttpClient; 

ThreadPoolExecutor mDecodeThreadPool; 

public ImageManager() { 
    // Instantiates the queue of Runnables as a LinkedBlockingQueue 
    mDecodeWorkQueue = new LinkedBlockingQueue<Runnable>(); 

    // Creates a thread pool manager 
    mDecodeThreadPool = new ThreadPoolExecutor(
      NUMBER_OF_CORES,  // Initial pool size 
      NUMBER_OF_CORES,  // Max pool size 
      KEEP_ALIVE_TIME, 
      KEEP_ALIVE_TIME_UNIT, 
      mDecodeWorkQueue); 

} 

ответ

0

решаемые его путем создания и использования FileOutputStream в организме OnResponse, так что это не открыто, а запрос делается.