A Journey Towards Android App Log Subscriptions

I’ve got an Android project going that uses FFmpeg to build short video files (more on this at a later date). Normally, you run FFmpeg in a terminal window or command prompt.  It updates you on the status of a task in that terminal window.  I’ve instrumented FFmpeg on Android with some C code and a JNI interface.  When I execute FFmpeg from an Android app those status updates are written to logcat.

There are some important details that I was to surface in my app, primarily the progress of an encoding process.  In order to do this, I needed a way to read my Android apps logging statements.  This is possible on Android and without needing to request the scary READ_LOGS Android permission.

Note: All of my examples are written in Kotlin because I ❤ Kotlin.

You can execute a process with the Runtime API:

val process = Runtime.getRuntime().exec("some_command_here")

You can then get an InputStream to the Process’s output:

val process = Runtime.getRuntime().exec("some_command_here")
val inputStream = process.inputStream

Putting it all together, this is how one would get your apps logging statements from logcat, within the scope of your app:

val process = Runtime.getRuntime().exec("logcat")
reader = BufferedReader(InputStreamReader(process.inputStream))
var line: String?

do {
    line = reader.readLine()

    if (line != null) {
        // do something with this logging statement
    }
} while (line != null)

reader.close()

This code essentially runs forever because, as far as I’ve seen, reader doesn’t return a null line.

I’m close, but I still needed to add some sophistication. I needed to read and parse logs while FFmpeg was running so I can update the user on the status of their job. I needed to start parsing logs when I wanted to update the user while FFmpeg was running and end parsing when FFmpeg was complete. This problem is perfectly solved using RxJava:

fun readLogs(): Observable = 
  return Observable.create ({ emitter ->
    var reader: BufferedReader? = null

    try {
      val process = Runtime.getRuntime().exec("logcat")
      reader = BufferedReader(InputStreamReader(process.inputStream))
      var line: String?

      do {
          line = reader.readLine()

          if (line.isNotBlank()) {
              emitter.onNext(line)
          }
      } while (line != null)
      emitter.onComplete()

    } catch (e: Exception) {
      emitter.onError(e)
    } finally {
      reader?.close()
    }
  })

With RxJava, I can emit a logging statement as it’s received by logcat.

Usage:

val disposable = readLogs()
  .subscribeOn(Schedulers.io())
  .observeOn(AndroidSchedulers.mainThread())
  .subscribe({ logLine -> 
    logTextView.setText(logLine)
  }, { error -> throw error })
  
// later, when you no longer need to subscribe to your logs
disposable.dispose()

Because it’s all based on RxJava, you can do more advanced things like filtering

val disposable = readLogs()
  .filter({ line -> line.startsWith("foo") })
  .subscribeOn(Schedulers.io())
  .observeOn(AndroidSchedulers.mainThread())
  .subscribe({ logLine -> 
    logTextView.setText(logLine)
  }, { error -> throw error })

This allows you to “subscribe” to your apps logs!

/* fini */

A Factoid: The Enforcing-Inheritance-Generic-Thing-in-Kotlin

I learn a Kotlin thing everyday.  Today’s thing, “Enforcing-Inheritance-Generic-Thing-in-Kotlin” (I’m terrible with names and terms, there’s an official term for this that I cannot remember).

I have a function that starts an activity for a result.  I want to limit this function to only be able to start activities extending an abstract class, FooActivity.

In Java:

private void start(Activity activity, Class<? extends FooActivity> cls) {
   // some stuff with intents
}

In Kotlin:

fun start(activity: Activity, cls: Class<out FooActivity>) {
    // some stuff with intents
}

That’s it.  Read more about generics in Kotlin here.

OMG Kotlin

I finally got around to watching Jake Wharton’s talk on how developers can use Kotlin to build their Android apps.  It’s a must watch.  For Android developers.

My initial thoughts on Kotlin were:

  1. Hmmm, reminds my of Swift (of Apple fame).
  2. Oh my goodness, take my money.
  3. Oh, oooh, ooooooooooooh.
  4. Nice.
  5. Kotlin is compiled to Java bytecode.  You still need to have reasonably deep knowledge of the compiler to avoid creating unnecessary objects or have inner classes hold onto references to the outer class, fail…
  6. …but, you need to have reasonably deep knowledge of the Java compiler anyway, even if you are writing just Java code (kind of invalidating point 5).
  7. This is amazing, this is the future.

One thing I have begun to realize as I learn Swift and now have seen Kotlin.  Java is VERY verbose with tons of ceremony.  Swift, Kotlin, and other similar languages are doing a good job stripping that all away and making coding genuinely fun and clean.