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:
Activity
's onDestroy()
function? Is there a better way to do this?RxJava
complains that I ignore the result of ".subscribe()", presumably because I won't ever call dispose()
on it. I'm not sure exactly how to handle this case. The subscriptions have the same lifecycle as the Application itself, so I'm not sure that removing it within a particular Activity
's onDestroy
would make sense. Further, my app has several entry points (the main activity, a background service, and a few others) that all make use of the logging facilities, so when to unsubscribe/dispose the RxJava
subscriptions isn't clear. My intuition is that I don't need to dispose them because they will be cleared whenever the Application is removed by the operating system. Any ideas?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