2017-01-25 4 views
0

Ich habe ein Bild "Manager", der Bilder herunterlädt. Zuvor ich die Picasso-Bibliothek für diese verwendet alsokhttp3 zu viele Dateideskriptoren Problem

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); 
     } 
    } 
} 

folgt aber dies erzeugt ein Bitmap-Objekt, das nicht nur viel Speicher verbraucht, ist es auch wesentlich langsamer und unnötig.

Ich habe dieses Runnable modifizierte okhttp3 zu verwenden, anstatt:

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); 
     } 
    } 
} 

Während dieser Ansatz viel schneller als die vorherigen ist, für eine große Anzahl von Bildern I A/libc: FORTIFY_SOURCE: FD_SET: file descriptor >= FD_SETSIZE. Calling abort(). von einem microdump gefolgt bekommen, was bedeutet, dass ich Zu viele Dateideskriptoren öffnen.

Für Testzweck habe ich die okHttpClient.connectionPool().evictAll(); // For testing Zeile hinzugefügt, aber das hat nicht funktioniert. Ich habe auch versucht, builder.connectionPool(new ConnectionPool(4, 500, TimeUnit.MILLISECONDS)); beim Aufbau der okHttpClient, aber das tat auch nichts. Ich bin mir auch bewusst von https://github.com/square/okhttp/issues/2636

I scheinen alle Ströme/Senken/Quellen zu schließen, so was ist hier los?

Die Runnables werden zu einer ThreadPoolExecutor seiner execute Funktion, die wie folgt erzeugt wird:

// 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); 

} 

Antwort

0

es gelöst durch die Schaffung und die FileOutputStream im OnResponse Körper verwendet wird, so dass es nicht geöffnet ist, während die Anfrage wird bearbeitet.