widavies
widavies

Reputation: 986

Flushing RxJava buffer on application close

Using Timber, I wrote a FileTree logger that will write Android logs to files on the disk. I buffer the logs, flushing to disk every 2 minutes, or 100 log messages, whichever comes first. The buffer prevents each log message from triggering a IO write, so as to not overload the IO resources by immediately writing every single log message.

I'm using RxJava to handle this task. A short snippet (taken from here):

logBuffer.observeOn(Schedulers.computation())
    .doOnEach((log) -> {
        processed++;

        if(processed % 100 == 0) {
            flush();
        }
    })
    .buffer(flush.mergeWith(Observable.interval(2, TimeUnit.MINUTES)))
    .subscribeOn(Schedulers.io())
    .subscribe((logs) -> {
        // Flush the logs to the file
        try {
            File logFile = new File(logDir, "app.log");

            FileWriter fw = new FileWriter(logFile, true);

            for(LogMessage msg : logs) {
                fw.append(msg.toString());
            }

            fw.flush();

            flushCompleted.onNext(logFile.length());
        } catch(Exception e) {
            Timber.e(e, "Failed to flush logs");
        }
    });        

I use the "flush" subject if I need to trigger a flush manually. I plant the FileTree in Timber within the Application's onCreate():

public class App extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        Timber.plant(new Timber.DebugTree(), new FileTree(getApplicationContext()));
    }
}

This is also where the subscriptions for RxJava are setup. I have two questions about this:

Upvotes: 0

Views: 197

Answers (1)

Rick Sanchez
Rick Sanchez

Reputation: 4766

  1. Instad of copying that code on each activity finish, you could use a BaseActivity, or use Application's activity state callbacks API ( https://developer.android.com/reference/android/app/Application#registerActivityLifecycleCallbacks(android.app.Application.ActivityLifecycleCallbacks) ).

I should note that for the purposes of chosing a lifecycle method to flush, no method after onPause() is guaranteed to be called by the OS. If the operating system needs to kill your app for low-memory, it's not guaranteed that onDestroy() will be called ( rather it will just kill your process ).

To quote https://developer.android.com/reference/android/app/Activity :

Note the "Killable" column in the above table -- for those methods that are marked as being killable, after that method returns the process hosting the activity may be killed by the system at any time without another line of its code being executed. Because of this, you should use the onPause() method to write any persistent data (such as user edits) to storage



  1. If the 'lifecycle' of your code is the same as the application's, its perfectly OK to ignore the subscription result ( as you won't ever dispose of the subscription ). Just make sure that you aren't catching in that scope parts that need to have a shorter lifecycle ( for example don't keep a reference to an activity context there, or you'll be leaking it )

Upvotes: 1

Related Questions