java - Android 定期写入内存上的文件时如何避免 I/O 问题

标签 java android file-io io

我正在编写一个应用程序,它将各种传感器(一个传感器的位置)和设备特定的元数据记录到一个文件中,然后将该文件传输到服务器。它不是后台服务 - 应用程序只需要在应用程序处于 Activity 状态时写入和传输文件(无需设置闹钟来唤醒服务)。我想每次 onLocationChanged() 向文件写入一行被调用(尽管位置数据不是唯一被写入的数据) - 或者至少以类似的速率 onLocationChanged()叫做。 onLocationChanged()目前被调用一次/秒,但我们最终可能会以更高的速率(可能2-3x/秒)记录数据。这似乎是对内存的大量 I/O。

我目前一切正常(概念证明),但我需要改进用于写入文件的方法。我每次都向文件写入一行 onLocationChanged()被调用,这可能不明智,并且似乎导致 I/O 堆积。我读过其他类似的问题,涉及各种方法(新线程、警报、计时器任务、处理程序等),但我找不到特定于我想要做的事情的答案。我还考虑过其他方法,例如缓存/缓冲数据并仅以较低频率写入内部存储(每 10 秒?),或者可能写入 SQLite 数据库并稍后导出到文件。我该怎么做才能最好地将文件代码与传感器代码分离(假设这就是我需要做的)并确保及时更新文件?我还需要确保所有数据都写入文件。

更新: 我最终使用的解决方案涉及向 StringBuilder 追加一定数量的行(每次调用 onLocationChanged() 一行)。在附加 10 行数据并进行有效缓冲后,我将 StringBuilder 移交给 AsyncTask,数据将在其中写入文件。

最佳答案

在普及定位类(class)中遇到过类似的情况。

这是我们所做的:

文件写入器 写入文件部分对我们来说不是问题,因为我们只收集 GPS 位置,而不收集其他传感器数据。对于我们来说,像下面这样的实现就足够了。目前尚不清楚是否必须将数据写入同一个文件,如果不是,那么您应该可以直接使用它。

public class FileOutputWriter {


private static String pathString = "";
    private static String sensorString = "";

public static void setPath(Context context, String path) {
    pathString = path;
    File pathFile = new File(pathString);
    pathFile.mkdirs();
}


public static void writeData(String data, String sensor, boolean append) {

    File file = new File(pathString + "/" + sensor+ ".log");

    long timeStamp = System.currentTimeMillis();

    BufferedWriter out = null;
    try {
        out = new BufferedWriter(new FileWriter(file, append));
            out.write(timeStamp + ":" + data);

        out.newLine();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
}

上传者 为了将数据上传到服务器,我们创建了一个 LogEntries 提示(将其更改为您自己的数据持有对象或简单的字符串)。

public class DataLogger {

static Vector<LogEntry> log = new Vector<LogEntry>();


public static void addEnty(LogEntry entry) {
    Log.d("DEBUG", entry.getStrategy() + " added position to logger " + entry.getLocation());
    log.add(entry);
}

public static Vector<LogEntry> getLog() {
    return log;
}

public static void clear() {
    log.clear();
}
}

请注意,Vector 是线程安全的。

最后我们实现了一个 UploaderThread,负责定期检查 DataLogger 提示并上传添加的条目。

public class UploaderThread extends Thread {


public static LinkedList<String> serverLog = new LinkedList<String>();


Boolean stop = false;
Context c;



public UploaderThread(Context c) {
    this.c = c;
}

public void pleaseStop() {
    stop = true;
}

@Override
public void run() {
    while(!stop) {

        try {               
            if(DataLogger.log.size() > 0 && Device.isOnline(c)) {
                while(DataLogger.log.size() > 0) {

                    LogEntry logEntry = DataLogger.getLog().get(0);
                    String result = upload(logEntry);
                    serverLog.add("("+DataLogger.log.size()+")"+"ServerResponse: "+result);

                    if(result != null && result.equals("OK")) {
                        DataLogger.getLog().remove(0);
                    } else {
                        Thread.sleep(1000);
                    }
                }
            } else {
                serverLog.add("Queue size = ("+DataLogger.log.size()+") + deviceIsonline: "+Device.isOnline(c));
            }

            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

private String upload(LogEntry entry) {
    HttpClient httpclient = new DefaultHttpClient();
    HttpPost httppost = new HttpPost("http://yoururl/commit.php");      

    try {
        // Add your data
        List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
        nameValuePairs.add(new BasicNameValuePair("tracename",sessionName +"-"+ entry.getStrategy().toString()));
        nameValuePairs.add(new BasicNameValuePair("latitude", Double.toString(entry.getLocation().getLatitude())));
        nameValuePairs.add(new BasicNameValuePair("longitude", Double.toString(entry.getLocation().getLongitude())));
        nameValuePairs.add(new BasicNameValuePair("timestamp", Long.toString(entry.getTimestamp())));

        httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

        // Execute HTTP Post Request
        HttpResponse response = httpclient.execute(httppost);
        if(response != null) {
            InputStream in = response.getEntity().getContent();
            String responseContent = inputStreamToString(in);

            return responseContent;
        }
    } catch (ClientProtocolException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }       


    return null;
}


private String inputStreamToString(InputStream is) throws IOException {
    String line = "";
    StringBuilder total = new StringBuilder();

    // Wrap a BufferedReader around the InputStream
    BufferedReader rd = new BufferedReader(new InputStreamReader(is));

    // Read response until the end
    while ((line = rd.readLine()) != null) { 
        total.append(line); 
    }

    // Return full string
    return total.toString();
}

}

线程只是在应用程序的第一个 Activity 中启动:

    UploaderThread ut;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    FileOutputWriter.setPath(this, Environment.getExternalStorageDirectory()
            .getAbsolutePath());

    ut = new UploaderThread(this);
    ut.start();
}

@Override
protected void onDestroy() {
    super.onDestroy();
    ut.pleaseStop();
}

希望这能让你上路

关于java - Android 定期写入内存上的文件时如何避免 I/O 问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20130371/

相关文章:

java - 我怎样才能在java中迭代这两个循环?

android - 线程会在 Android 中的 Activity 完成之前被杀死吗?

java - Tomcat6 + Ubuntu + Servlet

java - 尝试从不同类调用函数时应用程序崩溃

android - 如何获取我的动态 IP xcode

Java读取文件的不同方法

perl - 如何解决 Perl 中的 "print() on closed filehandle"错误?

Android - 如何保存其他应用程序可读的文件?

java - 多字节字符 XML 实体

java - 按顺序组织/插入日期的算法