写这篇文章是因为一个实际问题,这个问题其实不难,但是当我分析这个问题写日记的时候,突然觉得有必要把分析这个问题的过程记录下来,分享给大家.一些工具和方法在分析过程中很有用,我也从另一位聪明的朋友美明那里学到了一些分析技巧和工具使用技巧。本文的分析过程包含了我之前概述Android-methodology丢帧原因时提到的一些工具,包括:ReproduceVideo\EventLog\AndroidStudioSourceCodeandAppDebug\AndroidStudioProfile\Systrace\Dumpsys\PS等,开发过程中用到了大部分工具,本次分析就是利用这些工具相互配合,最终找到问题的原因。看完之后你可能会觉得这么简单的一道题还是要写的。写这篇文章的目的一是给自己记录一下,二是觉得分析过程比较通用,包括分析思路和工具的使用。如果能帮到大家,那就最好了。如果你也有好的想法或独家调试技巧,欢迎扫描关于我中的讨论群二维码加入群聊,共同进步!这个问题是测试直接报错的,bug描述是一个典型的症状描述:“从应用程序返回桌面,桌面图标加载缓慢”。测试提供了录制的视频和抓取的Log,以及相应的Systrace等,既然现象和Log都有了,那我们就开始分析吧。分析过程确定问题发生的时间点。因为测试提供的是复现视频,所以先看复现视频,判断时间发生的时间。根据视频中的大概时间(精确到分钟),查看对应的EventLog。与视频对比,确定发生的确切时间(精确到秒)查看EventLog和MainLog,还原发生时的用户操作。在这个例子中,发现Launcher在启动app后被kill掉了,我信了。EventLog//启动com.jx.cmcc.ict.ibelieve这个App09-1010:14:48.87714562269Iam_set_resumed_activity:[0,com.jx.cmcc.ict.ibelieve/.ui.MainTabActivity,resumeTopActivityInnerLocked]09-1010:14:48.8861456_2269Iactiv,80317506,54938,com.jx.cmcc.ict.ibelieve/.ui.MainTabActivity]09-1010:14:48.89114561485Isysui_count:[window_time_0,0]09-1010:14:48.89114561485Isysi_2,_action,0dui_22,779]09-1010:14:48.90214562269Iam_uid_stopped:10021//桌面在这里被杀死09-1010:14:48。90314562269Iam_kill:[0,13509,com.meizu.flyme.launcher,600,killbackground]//这里开始从App返回桌面09-1010:14:51.99014561791Iam_pause_activity:[0,80317506,com.jx.cmcc.ict.ibelieve/.ui.MainTabActivity]09-1010:14:51.99414561791Iam_task_to_front:[0,54923]09-1010:14:51.9961367413674Iam_on_paused_called:[0,com.jx.cmcc.ict.ibelieve.Activity.Main-Taband]01010:14:52.01314562270Iam_uid_running:10021//重新创建桌面进程09-1010:14:52.02514562270Iam_proc_start:[0,14013,10021,com.meizu.flyme.launcher,activity,com.meizu.flyme.launcher/.启动器]09-1010:14:52.04514562270IAM_PROC_BOUND:[0,14013,com.meizu.flyme.launcher]09-1010:14:14:52.069145622270IAM_UID_UID_UID_UID_UID_UID_EACTIVE.meizu.flyme.launcher/.Launcher]//桌面显示09-1010:14:52.07114562270Iam_set_resumed_activity:[0,com.meizu.flyme.launcher/.Launcher,minimalResumeActivityLocked]09-1010:14:52.3351401314013Iam,_onled:[0resume_on_com.meizu.flyme.launcher.Launcher,LAUNCH_ACTIVITY]09-1010:14:52.43714561504Iam_activity_launch_time:[0,238217861,com.meizu.flyme.launcher/.Launcher,413,413]然后这里就可以轻松恢复问题了,测试报告是从应用程序,桌面图标加载缓慢。从EventLog来看,桌面显示慢是因为桌面被kill掉了,所以从App返回时,需要重新加载桌面。从创建桌面进程到桌面完全显示,用了413ms(实际到桌面完全显示,至少用了2s,因为冷启动后需要重新加载Launcher)。查杀原因分析从上面的分析,我们需要找到造成Launcher被查杀的原因。从现象来看,似乎与com.jx有关。cmcc.ict.ibelieve进程是相关的,但我们无法确认。这里重点关注这个EventLogam_kill:[0,13509,com.meizu.flyme.launcher,600,killbackground]这里可以看到Launcher被杀的原因是kill背景。查看对应的源码可以看出reason=killbackground是由AMS.killBackgroundProcesses.ActivityManagerService.killBackgroundProcessespublicvoidkillBackgroundProcesses(finalStringpackageName,intuserId){...synchronized(this){killPackageProcessesLocked(packageName,appId,targetUserId,ProcessList.SERVICE_ADJ,false,true,true,false,"killbackground");}}熟悉源码的同学可以很快知道AMS.killBackgroundProcesses接口会提供给第三方应用调用,其BinderClient在Activit中yManager.killBackgroundProcesses这里ActivityManager.killBackgroundProcesses/***Havethesystemimmediatelykillallbackgroundprocessesassociated*withthegivenpackage.Thisisthesameasthekernelkillingthose*processestoreclaimmemory;thesystemwilltakecareofrestarting*theseprocessesinthefutureasneeded.**@parampackageNameThenameofthepackagewhoseprocessesareto*bekilled.*/@RequiresPermission(Manifest.permission.KILL_BACKGROUND_PROCESSES)publicvoidkillBackgroundProcesses(StringpackageName){try{getService().killBackgroundProcesses(packageName,mContext.getUserId());}catch(RemoteExceptione){throwe.rethrowFromSystemServer();}}SystemServer进程断点Debug知道了上面的代码逻辑,我们要做的就是找到Next,它应用程序调用ActivityManager.killBackgroundProcesses来终止桌面。由于我们不知道是哪个应用程序(虽然我们这里怀疑com.jx.cmcc.ict.ibelieve,但是没有证据),所以我们首先调试SystemServer进程。1、首先调试源码,首先点击Android中的debug按钮,选择system_process进程(也就是我们所说的SystemServer),然后点击OK。我们打了上面列出的ActivityManagerService.kill作为代码的断点在BackgroundProcesses方法中,2.点击启动可疑App(可以从EventLog和视频中推断出比较可疑的App,安装后本地测试重现。这里视频中出现的几个应用是selected,包括我们之前怀疑的com.jx.cmcc.ict.ibelieve-andIbelieve),点击其他应用不会进入这个断点,但是点击我相信这个App启动后,会跳到断点。3、这里我们可以看到调用栈是一个Binder调用。我们需要找到这个Binder调用的客户端。继续在AS中操作,点击计算器按钮如下图,输入getRealCallingPid()点击下面的Evaluate可以看到结果。result=297714,通过ps命令查看这个pid对应的app。您可以看到此应用程序调用了killBackgroundProcesses。App进程断点调试为了进一步调查,我们调试了该应用程序。由于没有源码,我们这里直接下了断点到android/app/ActivityManager.killBackgroundProcesses(因为这是客户端代码,所以调试App进程的时候,可以直接打断点)。在本地安装此应用程序以进行调试。登录重启后,桌面会被杀死。这绝对是这个应用程序的问题。至此,我们基本确定问题出在这个App上,但是如果我们想看更详细的调用,可以使用AndroidStudioProfile。使用AndroidStudioProfiler工具打开AndroidStudio,点击Profiler按钮,点击+号,选择com.jx.cmcc.ict.ibelieve进程,然后点击CPU一栏。在这里选择TraceJavaMethods,然后点击旁边的Record开始操作。操作完成后,点击停止,AS会自动开始解析。我们可以在这里查看分析结果:最下面是刚才操作对应的详细函数调用栈,按照实际操作的顺序展示在我们面前(我经常用这个工具查看源码逻辑以及第三方应用的代码逻辑,无论是学习还是解决问题,都是很好的方式)。我们使用ctrl+f搜索killBackgroundProcesses,如果有就会高亮显示,我们只需要用鼠标放大就可以看到详细的调用栈。可以看到这个App在loadComplete回调中执行了killBackground方法。(此时,应用开发者已经知道问题出在哪里,可以很快修复)。权限问题分析如上所示,调用killBackgroundProcesses需要权限Manifest.permission.KILL_BACKGROUND_PROCESSES。@RequiresPermission(Manifest.permission.KILL_BACKGROUND_PROCESSES)publicvoidkillBackgroundProcesses(StringpackageName){}执行adbshelldumpsyspackagecom.jx.cmcc.ict.ibelieve检查com.jx.cmcc.ict.ibelieve进程申请的权限,以及发现这个应用在安装时申请了KILL_BACKGROUND_PROCESSES权限,默认授予。installpermissions:......android.permission.ACCESS_NETWORK_STATE:granted=trueandroid.permission.KILL_BACKGROUND_PROCESSES:granted=trueandroid.permission.WRITE_USER_DICTIONARY:granted=true......对应权限级别正常。也就是说,所有的第三方应用都可以默认拥有这个权限,只要你去申请。本案例是因为app申请了这个权限,进行了错误的行为,导致杀死桌面,严重影响了用户体验。.伤心!Systrace工具能查出Kill桌面的罪魁祸首吗?既然经常用到Systrace,那Systrace能不能找到罪魁祸首呢?答案是肯定的(如果你不知道如何在这里查看Systrace的唤醒信息,你可以阅读这篇分析Systracepreliminaries的文章)。下面记录一段Systrace,按照以下顺序安装看看1.先看system_server进程对应的trace,找到killProcessGroup对应的点,查看其唤醒情况。可以看到线程19688唤醒了执行AMS的killProcessGroup。在Systrace中搜索19688,可以看到是Binder:1295_1E,1295是SystemServer。查看对应的Binder:1295_1E,可以看到是哪个线程唤醒了这个线程。搜索线程7289,可以看到这个线程就是这个App的主线程。查看7289,确定是com.jx.cmcc.ict.ibelieve进程。也就是说,我相信这个应用程序(肿瘤)。这里也可以推断出这个Kill是由我相信的App发起的,进一步证实了上面AS的MethodTrace是可以使用的。总结从以上分析来看,该问题是由于应用程序申请了不合适的权限,错误地使用了相应的功能,严重影响了用户的使用。一般来说,分析到这里,我们的工作就基本结束了。以后只需要和店家沟通,联系App开发者修改即可。但是让我意外的是,android.permission.KILL_BACKGROUND_PROCESSES这个权限被谷歌放宽了。我一直以为这个权限是专门用来防止app被滥用或者使用的(毕竟这关系到其他app的生死存亡)。5、更多关于我们小厂系统研发工程师的信息,请点击关于我。希望与您交流,共同进步。
