Question

I have a Listener class which adds values to a Queue, I have another class that contains the Queue and writes it to a file. Both of these classes are running in background thread.

Listener class

private AccelerometerQueue mLogQueue; //Queue class implements Runnable
private Thread mQueueThread; //Thread to run Runnable of Queue class
@Override
public void onSensorChanged(SensorEvent sensorEvent) {

    synchronized (this) {
        mLogQueue.addToQueue(
                new AccelerometerVector(
                        System.currentTimeMillis(),
                        sensorEvent.timestamp,
                        sensorEvent.values[0],
                        sensorEvent.values[1],
                        sensorEvent.values[2]
                )
        );


        mQueueThread = new Thread(mLogQueue);
        mQueueThread.start();
    }
}

Queue class imlpements Runnable and has this LinkedList

public void addToQueue(AccelerometerVector vector) {
    mQueue.add(vector);
}

public synchronized int getQueueSize(){
    return mQueue.size();
}

@Override
public void run() {
    try {
        mLogFile = createLogFile();
        mLogOutStream = new FileOutputStream(mLogFile, true);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }

    while (!mQueue.isEmpty()) {
        try {
            mLogOutStream.write(mQueue.poll().getDataStringLine().getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }

        if (mLogFile.length() > AppConstants.LOG_FILE_MAX_SIZE) {
            try {
                reportCurrentLogFileIsFinished(mLogFile);
                mLogOutStream.flush();
                mLogOutStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                mLogOutStream = new FileOutputStream(createLogFile(), true);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
}

private File createLogFile() {

    String datePart = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss").format(new Date());
    String namePart = "log_" + datePart + ".csv";
    File folder = new File(Environment.getExternalStorageDirectory().toString()
            + File.separator + AppConstants.APP_LOG_FOLDER_NAME);

    folder.mkdirs();

    File file = new File(folder + File.separator + namePart);

    if (!file.exists()) { //create the file if not exist
        try {
            file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
            Log.e(TAG, "Can not create file: " + file.getName());
        }
    }
    if (file.exists()) {
        return file;
    } else {
        return null;
    }
}

My goal is to save the queue content to storage and the size of the files should not exceed 256Kb, and when a file reaches 256kb a new file should be created.

What the code currently does is writes few files with sizes like 2 or 3 Kb and then it throws the exception in the createLogFile() method complaining the file can not be created (I see this in the Logcat).

The question is, what am I doing wrong and what might be the possible solution?

Was it helpful?

Solution 2

OK I figured out the problem.

The first problem was that I started the mQueueThread inside the onSensorChanged method. This caused a new thread started on receiving update from the onSensorChanged method and cause meamory leak or some other serious problems. So I moved it to the constructor.

Secondly I changed the Queue type to ConcurrentLinkedQueue but I dont have any explanation...might be another type would do better.

Lastly, I changed the implementation of Runnable to the following:

@Override
public void run() {
    while (true) {
        if (mLogOutStream != null && !mQueue.isEmpty()) {
            try {
                mLogOutStream.write(mQueue.poll().getDataStringLine().getBytes());
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (mLogFile.length() > AppConstants.LOG_FILE_MAX_SIZE) {
                synchronized (this) {
                    reportCurrentLogFileIsFinished(mLogFile);
                    try {
                        if (mLogOutStream != null) {
                            mLogOutStream.flush();
                            mLogOutStream.close();
                            mLogOutStream = null;
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                    try {
                        mLogFile = createLogFile();
                        mLogOutStream = new FileOutputStream(mLogFile, true);
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

These changes took care of the problems. Hope this can help others too.

OTHER TIPS

First, I assume you are using a synchronized collection:

http://docs.oracle.com/javase/7/docs/api/java/util/Collections.html#synchronizedList(java.util.List)

Attempting to reopen the file may be the issue. Given that there is a single thread writing to the file I would suggest that you open the file and keep it open.

Finally, what exception are you getting?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top