Reading Your Android App’s Logs

综合技术 2018-02-15

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 example code is in Kotlin because I ❤ Kotlin.

You can execute a process with Android’s 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 != null && line.length > 0) {
              emitter.onNext(line)
          }
      } while (line != null)

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

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 */

责编内容by:emuneee.com (源链)。感谢您的支持!

您可能感兴趣的

Android 采用AOP方式封装6.0权限管理 【一】背景 6.0运行时申请权限已经是一个老生常谈的内容了,最近项目TargetSDKVersion升到23以上,所以我们也需要做权限管理,我想到的需求是这样的: 1、支持单个权限、多个权限...
搞定 RecycleView 侧滑菜单、添加头部底部、加载更多... 在 动手打造史上最简单的 Recycleview 侧滑菜单 中,萌生了将这种方案封装为一个开源库的想法,旨在实现调用方式最简单,且又不失可定制性。本库最大的特点的是采用了 Glide 简洁明了...
ArchitectureComponent 架构库 LiveData 数据变更观察者, 我觉得和DataBinding的Observable接口发生冲突 ​ ViewModel 解耦数据脱离组件, 防止意外销毁 ​...
Data Persistence With Room Many apps need to deal with persisting data. Perhaps you have an app that stores your fav...
Android自定义动画专题一 Android自定义动画 在目前的移动端产品中,不管是app还是网页一个好看酷炫的页面总是会第一时间吸引人的眼球,那么对于android开发人员来说,要想实现一个好看的页面必然需要掌握自定义...