okhttp3 文件描述符问题太多
okhttp3 too many file descriptors issue
我有一张图片 "manager" 可以下载图片。
以前我使用毕加索库如下
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 -
.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().
后跟一个微转储,这意味着我打开了太多文件描述符。
为了测试,我添加了 okHttpClient.connectionPool().evictAll(); // For testing
行,但是没有用。
我还尝试在构建 okHttpClient
时设置 builder.connectionPool(new ConnectionPool(4, 500, TimeUnit.MILLISECONDS));
,但这也没有任何作用。
我也知道 https://github.com/square/okhttp/issues/2636
我好像都关闭了streams/sinks/sources,请问这是怎么回事?
使用 execute
函数将可运行对象添加到 ThreadPoolExecutor
,创建方法如下:
// 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);
}
通过在 OnResponse 正文中创建和使用 FileOutputStream
解决了这个问题,因此在完成请求时它不会打开。
我有一张图片 "manager" 可以下载图片。 以前我使用毕加索库如下
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 -
.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().
后跟一个微转储,这意味着我打开了太多文件描述符。
为了测试,我添加了 okHttpClient.connectionPool().evictAll(); // For testing
行,但是没有用。
我还尝试在构建 okHttpClient
时设置 builder.connectionPool(new ConnectionPool(4, 500, TimeUnit.MILLISECONDS));
,但这也没有任何作用。
我也知道 https://github.com/square/okhttp/issues/2636
我好像都关闭了streams/sinks/sources,请问这是怎么回事?
使用 execute
函数将可运行对象添加到 ThreadPoolExecutor
,创建方法如下:
// 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);
}
通过在 OnResponse 正文中创建和使用 FileOutputStream
解决了这个问题,因此在完成请求时它不会打开。