[英]Android: Too many open files error
我有以下每 3 秒運行一次的操作。
基本上,它每 3 秒從服務器下載一個文件並將其保存到本地文件中。
下面的代碼完成了一段時間的工作。
public class DownloadTask extends AsyncTask<String, Void, String>{
@Override
protected String doInBackground(String... params) {
downloadCommandFile( eventUrl);
return null;
}
}
private void downloadCommandFile(String dlUrl){
int count;
try {
URL url = new URL( dlUrl );
NetUtils.trustAllHosts();
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setDoInput(true);
con.setDoOutput(true);
con.connect();
int fileSize = con.getContentLength();
Log.d(TAG, "Download file size = " + fileSize );
InputStream is = url.openStream();
String dir = Environment.getExternalStorageDirectory() + Utils.DL_DIRECTORY;
File file = new File( dir );
if( !file.exists() ){
file.mkdir();
}
FileOutputStream fos = new FileOutputStream(file + Utils.DL_FILE);
byte data[] = new byte[1024];
long total = 0;
while( (count = is.read(data)) != -1 ){
total += count;
fos.write(data, 0, count);
}
is.close();
fos.close();
con.disconnect(); // close connection
} catch (Exception e) {
Log.e(TAG, "DOWNLOAD ERROR = " + e.toString() );
}
}
一切正常,但如果我讓它運行 5 到 10 分鍾,我會收到以下錯誤。
06-04 19:40:40.872: E/NativeCrypto(6320): AppData::create pipe(2) 失敗:打開的文件太多 06-04 19:40:40.892: E/NativeCrypto(6320): AppData::create管道(2)失敗:打開的文件太多 06-04 19:40:40.892: E/EventService(6320): DOWNLOAD ERROR = javax.net.ssl.SSLException: 無法創建應用程序數據
最近兩天我一直在做一些研究。
有人建議他們打開許多連接,比如這個https://stackoverflow.com/a/13990490/1503155但我仍然不知道是什么問題。
任何可能導致問題的想法?
提前致謝。
我認為您會收到此錯誤消息是因為您同時打開了太多文件,這意味着您同時運行了太多異步任務(每個異步任務都打開了一個文件),如果您說您運行了一個每3秒更新一次。
您應該嘗試使用線程池執行程序來限制同時運行的異步任務的數量。
您的問題不是線程過多,盡管這是導致問題浮出水面的原因。
就像注釋中提到的@stdout一樣,除非您另外指定,否則AsyncTask已經在所有AsyncTask中共享的線程池中運行。 這里的問題是,文件描述符未及時正確關閉。
問題是您的文件描述符關閉得不夠快。
我為此花費了數小時,數天,數周的時間,在做所有您想設置較小的讀取/連接超時並使用finally
塊關閉連接,輸入流,輸出流等的工作。但是我們從未找到可行的解決方案。 HttpsUrlConnection
似乎在某種程度上存在缺陷。
因此,我們嘗試使用OkHttp
替代HttpsUrlConnection
和HttpsUrlConnection
! 它開箱即用。
因此,如果您對此感到掙扎,並且很難解決它,建議您也嘗試使用OkHttp。
以下是基本知識:
添加Maven依賴項后,您可以執行以下操作來下載文件:
OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
OutputStream output = null;
try {
Request request = new Request.Builder().url( download_url ).build();
Response response = okHttpClient.newCall( request ).execute();
if ( !response.isSuccessful() ) {
throw new FileNotFoundException();
}
output = new FileOutputStream( output_path );
output.write( response.body().bytes() );
}
finally {
// Ensure streams are closed, even if there's an exception.
if ( output != null ) output.flush();
if ( output != null ) output.close();
}
切換到OkHttp會立即解決我們泄漏的文件描述符問題,因此即使您被卡住了也值得嘗試,即使是以增加另一個庫依賴項為代價。
我不得不一次下載數百個文件並遇到錯誤。
您可以使用以下命令檢查打開的描述符:
adb shell ps
在列表中找到您的應用程序 PID 並使用另一個命令:
adb shell run-as YOUR_PACKAGE_NAME ls -l /proc/YOUR_PID/fd
我在通常的發布中看到大約 150 個打開的描述符。 下載文件時有 700 多個。 它們的數量僅在幾分鍾后才會減少,看起來 Android 會在后台釋放它們,而不是在您可以close
流時。
唯一可行的解決方案是使用自定義 ThreadPool 來限制並發性。 這是 Kotlin 代碼:
private val downloadDispatcher = Executors.newFixedThreadPool(8).asCoroutineDispatcher()
private suspend fun downloadFile(sourceUrl: String, destPath: String, progressBlock: suspend (Long) -> Unit) = withContext(downloadDispatcher) {
val url = URL(sourceUrl)
url.openConnection().apply { connect() }
url.openStream().use { input ->
FileOutputStream(File(destPath)).use { output ->
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
var bytesRead = input.read(buffer)
var bytesCopied = 0L
while (bytesRead >= 0) {
if (!coroutineContext.isActive) break
output.write(buffer, 0, bytesRead)
bytesCopied += bytesRead
progressBlock(bytesCopied)
bytesRead = input.read(buffer)
}
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.