读取系统日志
普通三方应用在代码中执行adb logcat -v time命令时,只能获取应用自身打印出来的log信息,而无法获取系统log。
若想读取系统log,需要在AndroidMenifest.mxl中添加android.permission.READ_LOGS权限,随着Android权限的日益收紧,android.permission.READ_LOGS也经历了如下变化:
1 android 4.1之前:直接在AndroidManifest.xml中申请权限就可使用
2 android 4.1以后:Logcat的签名变为 “signature|system|development”了,这意味着只有系统签名的app或者root权限的app才能使用该权限。
因此在Android4.1以后,三方应用如果想要在代码中读取系统日志,AndroidManifest.xml的正确填写格式如下,需要主要的是在AndroidManifest.xml中声明了android.uid.system之后需要进行签名,否则是无法安装到手机上的(如何签名?请自行百度)。
否则,你只能在cmd窗口通过adb命令查看系统日志了。
实时读取
我们知道,通过adb logcat -v time读取系统日志时,系统日志是动态返回的,即你是无法等待它执行完毕后获取结果的,那我们应该怎么办?下面介绍如果实时的读取系统动态日志。
我们知道,ProcessBuilder.start()和Runtime.exec()方法都可以用来创建一个操作系统进程,用于执行命令行操作,并返回一个Process子类的一个实例,它的所有标准 io(即 stdin、stdout 和 stderr)操作都将通过三个流 (getOutputStream()、getInputStream() 和 getErrorStream()) 重定向到父进程。父进程使用这些流来提供到子进程的输入和获得从子进程的输出。 ProcessBuilder和Runtime之间的区别与联系如下:
1 Runtime是Java1.0开始就有的,而ProcessBuilder是Java1.5之后加进来的
2 ProcessBuilder.start() 和 Runtime.exec()传递的参数有所不同,Runtime.exec()可接受一个单独的字符串,这个字符串是通过空格来分隔可执行命令程序和参数的,也可以接受字符串数组参数。而ProcessBuilder的构造函数是一个字符串列表或者数组。列表中第一个参数是可执行命令程序,其他的是命令行执行是需要的参数。 例如:
2.1 通过Runtime执行java -jar test.jar命令:
| Process p; String cmd = 'c:\\test\\test.bat'; try { p = Runtime.getRuntime().exec(cmd); InputStream fis = p.getInputStream(); //取得命令结果的输出流 ………………………………………………………………………… } catch (IOException e) { e.printStackTrace(); } |
2.2 通过ProcessBuilder执行java -jar test.jar命令:
ProcessBuilder pb = new ProcessBuilder('java', '-jar', 'test.jar'); pb.directory(new File('F:\\dist'));//test.jar文件路径 Process p1 = pb.start(); InputStream fis = p.getInputStream(); //取得命令结果的输出流 …………………………………………………………………………………… |
3 Runtime.exec最终是通过调用ProcessBuilder来真正执行操作的。
public Process exec(String[] cmdarray, String[] envp, File dir) throws IOException { return new ProcessBuilder(cmdarray) .environment(envp) .directory(dir) .start(); } |
在介绍完如何实时读取命令行数据后,这里我们采用ProcessBuilder来实时读取android系统日志,具体代码如下:
| public class RealTimeProcess { private String TAG = 'RealTimeProcess'; private ProcessBuilder mProcessBuilder; private Process recordProcess; private BufferedReader readStdout; private BufferedReader readStderr; private RealtimeProcessListener realtimeProcessListener; // 回调用到的接口 private String tmp1; private String tmp2; public RealTimeProcess(RealtimeProcessListener realtimeProcessListener){ this.realtimeProcessListener = realtimeProcessListener; } public void start(String ...commands) { try { mProcessBuilder = new ProcessBuilder(commands).redirectErrorStream(true); recordProcess = mProcessBuilder.start(); exec(recordProcess); } catch (Exception e) { } } private void exec(final Process process) { readStdout = new BufferedReader(new InputStreamReader(process.getInputStream())); readStderr = new BufferedReader(new InputStreamReader(process.getErrorStream())); Thread execThread = new Thread(){ public void run(){ try { while((tmp1 = readStdout.readLine()) != null || (tmp2 = readStderr.readLine()) != null){ if(tmp1 != null){ realtimeProcessListener.onNewStdoutListener(tmp1); } if(tmp2 != null){ realtimeProcessListener.onNewStderrListener(tmp2); } } } catch (IOException e) { realtimeProcessListener.onProcessFinish(); } } }; execThread.start(); } public void destroyRealtimeProcess() { if (recordProcess.isAlive()) { recordProcess.destroyForcibly(); } } } |
通过上面代码我们也就可以看到,实际上就是对ProcessBuilder进行了二次封装,然后再将读取到的Android系统日志通过RealtimeProcessListener接口实时传递出去。
UI交互
在处理Android系统日志时,如何需要更新UI,由于他们不属于同一个进程,因此不能直接进行更新UI操作,否则就会报如下错误:
当一个程序第一次启动的时候,Android会同时启动一个对应的主线程,这个主线程就是UI线程,也就是ActivityThread。UI线程主要负责处理与UI相关的事件,如用户的按键点击、用户触摸屏幕以及屏幕绘图等。系统不会为每个组件单独创建一个线程,在同一个进程里的UI组件都会在UI线程里实例化,系统对每一个组件的调用都从UI线程分发出去。所以,响应系统回调的方法永远都是在UI线程里运行,如响应用户动作的onKeyDown()的回调。
Android为在子线程中更新UI提供了多种异步消息处理的方式 方式,这里我们采用最经典也是最常用的一种方式:Handler。
由于Handler总是依附于创建时所在的线程,比如我们的Handler是在主线程中创建的,而在子线程中又无法直接对UI进行操作,于是我们就通过一系列的发送消息、入队、出队等环节,最后调用到了Handler的handleMessage()方法中,这时的handleMessage()方法已经是在主线程中运行的,因而我们当然可以在这里进行UI操作了。整个异步消息处理流程的示意图如下图所示:
在Handler实际使用过程中,需要注意避免由Handler造成内存泄露,这里推荐使用静态内部类继承Handler,因为静态内部类不持有外部类引用,可以在Handler中增加一个对Activity的弱引用,这样在GC回收时会忽略掉弱引用,即就算有弱引用指向某对象,该对象也会在被GC检查到时回收掉。 具体代码如下:
static class DetectorHandler extends Handler { WeakReference<DetectorService> weakReference; public DetectorHandler(DetectorService detectorService) { weakReference = new WeakReference<>(detectorService); } @Override public void handleMessage(Message msg) { DetectorService detectorService = weakReference.get(); if (detectorService == null) { return; } switch (msg.what) { case WEBVIEW_CREATED: Bundle bundle = msg.getData(); String webViewCreated = bundle.getString('webViewCreated'); detectorService.webViewDetected(webViewCreated); break; } } } |





































苏公安网备 32011402010933号