我有一個Android應用程序,它以大約100Hz的頻率記錄幾個Android傳感器。因此,如果我記錄10個傳感器,我每秒鐘向文件寫入約3000個數據點(每個傳感器通常有3個條目)。現在的問題是,我想盡量減少這種寫作對應用程序其餘部分的影響。具體來說,我不希望日誌記錄會減慢事件發送速度......我想確保我在事件發生後立即獲取事件,而不是延遲(我知道總會有一些延遲,因爲Android不是實時以及由於傳感器事件框架的「拉」性質)。記錄大量的Android傳感器數據
接下來,我將概述我的方法,該方法看起來不太好。我想提出如何改進的建議。
我現在的程序是這樣的......
對於每一個傳感器,我創建一個事件日誌的BlockingQueue的一個單獨的線程。在線程內部,我有一個從隊列中拉出來的while循環,並使用緩衝寫入器寫文件。當傳感器管理器傳遞一個新的SensorEvent時,該事件將放入適當的隊列中(從而觸發另一個線程上的文件IO),以便不延遲SensorEvents傳遞的主線程。
我希望在事件發生後立即獲取事件,所以重要的是我不會在傳感器框架中引入任何延遲。例如,如果我在onEvent回調中直接執行了文件IO,那麼我擔心事件可能開始堆積在流水線中,並且他們在最終交付時會過期。上述方法緩解了這些擔憂。
但是還有另外一個問題...
儘管IO時關閉傳感器事件傳遞線程的文件,有時應用程序仍然感覺呆滯。也就是說,有時我會看到事件快速連續發生(例如5個事件在彼此的1ms內傳遞)。這表明雖然傳感器傳送線程上沒有發生IO,但傳送線程仍然延遲。有幾個原因給我建議:
我創建了太多的IO線程。也許如果我把所有的寫入都推到一個單獨的線程中,我會在新的事件進入時增加傳感器傳送線程處於活動狀態的可能性。在當前的設置中,可能是所有活動線程都被用於文件IO當事件進入時,導致事件備份,直到其中一個寫事件結束。
目前,我使用的是平面文件輸出,而不是數據庫。使用數據庫進行檢索的好處對我來說很明顯。我不清楚的是,如果我只將數據附加到文件中,我是否也應該期望數據庫速度更快......也就是說,我從不需要從文件讀取數據或將數據插入隨機地點,我只是從字面上追加到文件的末尾。在我看來,在這種情況下,數據庫不能比標準文件IO更快。或者我錯了?
其他人建議垃圾收集器可能會干擾我的線程,並且問題的可能來源是由於正在創建的大量事件導致的內存抖動。
從哪個角度我應該接近這個?
編輯:
以下是我使用的字符串寫入到文件中的類。其中一個是根據SensorEvent類型創建的。
package io.pcess.utils;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/**
* This type of File Writer internally creates a low-priority {@link Thread}
* which queues data to be written to a specified file. Note that the
* constructor starts the {@link Thread}. Furthermore, you can then append a
* {@link String} to the end of the specified file by calling
*
* <pre>
* fileWritingThread.append(stringToAppend);
* </pre>
*
* Finally, you should tidy up by calling
*
* <pre>
* fileWritingThread.close();
* </pre>
*
* which will force the writer to finish what it is doing and close. Note that
* some {@link String}s might be left in the queue when closing, and hence will
* never be written.
*/
public class NonblockingFileWriter {
/**
* ---------------------------------------------
*
* Private Fields
*
* ---------------------------------------------
*/
/** The {@link Thread} on which the file writing will occur. */
private Thread thread = null;
/** The writer which does the actual file writing. **/
private BufferedWriter writer = null;
/** A Lock for the {@link #writer} to ensure thread-safeness */
private final Object writerLock = new Object();
/** {@link BlockingQueue} of data to write **/
private final BlockingQueue<String> data = new LinkedBlockingQueue<String>();
/** Flag indicating whether the {@link Runnable} is running. **/
private volatile boolean running = false;
/**
* The {@link Runnable} which will do the actual file writing. This method
* will keep writing until there is no more data in the list to write. Then
* it will wait until more data is supplied, and continue.
*/
private class FileWritingRunnable implements Runnable {
@Override
public void run() {
try {
while (running) {
String string = data.take();
synchronized (writerLock) {
if (writer != null) {
writer.write(string);
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
close();
}
}
};
/**
* ---------------------------------------------
*
* Constructors
*
* ---------------------------------------------
*/
public NonblockingFileWriter(String filename) {
this(new File(filename));
}
public NonblockingFileWriter(File file) {
writer = createWriter(file);
if (writer != null) {
running = true;
}
thread = new Thread(new FileWritingRunnable());
thread.setPriority(Thread.MIN_PRIORITY);
thread.start();
}
/**
* ---------------------------------------------
*
* Public Methods
*
* ---------------------------------------------
*/
/** Append the specified string to the file. */
public void append(String string) {
try {
data.put(string);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
/**
* Close the {@link BufferedWriter} and force the {@link Thread} to stop.
*/
public void close() {
running = false;
try {
synchronized (writerLock) {
if (writer != null) {
writer.close();
writer = null;
}
}
/**
* This string will not be written, but ensures that this Runnable
* will run to the end
*/
data.put("Exit");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Create a {@link BufferedWriter} for the specified file.
*
* @param file
* @return
*/
private BufferedWriter createWriter(File file) {
BufferedWriter writer = null;
if (!file.exists()) {
try {
file.getParentFile().mkdirs();
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return writer;
}
}
if (file.canWrite()) {
boolean append = true;
try {
synchronized (writerLock) {
writer = new BufferedWriter(new FileWriter(file, append));
}
} catch (IOException e) {
e.printStackTrace();
}
}
return writer;
}
}
你正在寫入單個文件還是多個? – Pavlus
我正在寫入多個文件。我的想法是,如果我試圖將所有內容轉儲到同一個文件,我會創建一個瓶頸。或者那是錯的? –
使用更少的文件和更少的線程通常更高效,請嘗試使用輪詢線程寫入來自'ArrayBlockingQueue'的單個緩衝流並使用數據持有者的池,onEvent - 將數據從池中複製到持有者並將其放入隊列在輪詢線程中寫入持有者的數據並將其返回給池。採用這種方法,我已經設法在雙核設備上每秒接收4k 1442字節的數據包,並且具有足夠的性能 – Pavlus