文档开发者中心
我的

通过App实时读取Android流水日志


读取系统日志

普通三方应用在代码中执行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;
               }
           }
       }


点赞
收藏
回复
1
分享
举报
浏览5318 编辑于2020-07-31 08:20未知归属地
全部评论
最多点赞
最新发布
最早发布

是否可以写一个android.ui.system的应用读取系统日志, 然后将读取到日志信息通过socket发送给普通签名的应用

1楼回复于2019-07-29 10:35 未知归属地
写回答
新增插入模板功能
一键使用模板,快速填写内容,轻松发帖~
知道了
  • 为了保障您的信息安全,请勿上传您的敏感个人信息(如您的密码等信息)和您的敏感资产信息(如关键源代码、签名私钥、调试安装包、业务日志等信息),且您需自行承担由此产生的信息泄露等安全风险。
  • 如您发布的内容为转载内容,请注明内容来源。

我要发帖子

目录 收起
读取系统日志
实时读取
UI交互
了解社区公约,与您携手共创和谐专业的开发者社区。