Framework常用工具及LOG调试方法

概述  

    Framework开发是一项非常繁琐复杂的工作,需要阅读大量的源代码,分析及其多的LOG信息来定位错误位置。这个时候如果使用一些工具或者知道如何定位重要LOG信息,就可以使一些复杂的工作变的简单很多,使我们分析问题的效率变得更快,不再为阅读大量的源代码而感到一筹莫展。本文将针对一些场景讲解如何分析系统LOG信息,如何添加LOG定位错误信息,以及常用工具以及使用方法。

常用工具

HierarchyViewer

      HierarchyViewer是随Android SDK发布的工具,位置在sdktools文件夹下,名为hierarchyviewer。它是Android自带的非常有用而且使用简单的工具,能够让我们从可视化的角度直观地获得UI布局设计结构和各种属性的信息,帮助我们优化布局设计。打开HierarchyViewer后我们可以看到当前view的布局,如下图所示。


DDMS

       DDMS 的全称是Dalvik Debug Monitor Service,是 Android 开发环境中的Dalvik虚拟机调试监控服务。它为我们提供例如:为测试设备截屏,针对特定的进程查看正在运行的线程以及堆信息、Logcat、广播状态信息、模拟电话呼叫、接收SMS、虚拟地理坐标等等。DDMS打开界面如下:


左侧的Devices一栏会列出已连接的设备,点击设备名称左侧三角符号图标会列出该设备运行中的进程的app包名。


模拟按键

使用命令adb shell input keyevent + 对应的键值,可以模拟对应的操作。手机系统中主要的键值如下:

    /** Key code constant: Home key.
     * This key is handled by the framework and is never delivered to applications. */
    public static final int KEYCODE_HOME            = 3;    //Home键
    /** Key code constant: Back key. */
    public static final int KEYCODE_BACK            = 4;    //Back键
    /** Key code constant: Call key. */
    public static final int KEYCODE_CALL            = 5;    //打电话
    /** Key code constant: End Call key. */
    public static final int KEYCODE_ENDCALL         = 6;    //挂断电话
    /** Key code constant: Volume Up key.
     * Adjusts the speaker volume up. */
    public static final int KEYCODE_VOLUME_UP       = 24;   //调大声音
    /** Key code constant: Volume Down key.
     * Adjusts the speaker volume down. */
    public static final int KEYCODE_VOLUME_DOWN     = 25;   //调小声音
    /** Key code constant: Power key. */
    public static final int KEYCODE_POWER           = 26;   //电源键
    /** Key code constant: Camera key.
     * Used to launch a camera application or take pictures. */
    public static final int KEYCODE_CAMERA          = 27;     //相机按键
    /** Key code constant: Clear key. */
    public static final int KEYCODE_CLEAR           = 28;     //清理
    /** Key code constant: Space key. */
    public static final int KEYCODE_SPACE           = 62;     //空格键
    /** Key code constant: Backspace key.
     * Deletes characters before the insertion point, unlike {@link #KEYCODE_FORWARD_DEL}. */
    public static final int KEYCODE_DEL             = 67;     //删除键
    /** Key code constant: Menu key. */
    public static final int KEYCODE_MENU            = 82;      //菜单键

当我们要模拟power键亮灭屏事件就可以执行下面命令:

adb shell input keyevent 26

模拟别的按键事件同理。

而adb shell getevent / adb shell sendevent是发送对应的事件,包括触屏,按键等事件。

am命令

      有时候我们在调试系统时可以在终端使用am命令来发送广播,打开Activity,启动Service等操作,十分方便。am的详细操作如下,我们可以通过adb shell am + 对应命令,就可操作。

usage: am [subcommand] [options]
usage: am start [-D] [-N] [-W] [-P <FILE>] [--start-profiler <FILE>]
               [--sampling INTERVAL] [-R COUNT] [-S]
               [--track-allocation] [--user <USER_ID> | current] <INTENT> //可以通过该命令启动对应Activity
       am startservice [--user <USER_ID> | current] <INTENT>   //启动服务
       am stopservice [--user <USER_ID> | current] <INTENT>    //停止服务
       am force-stop [--user <USER_ID> | all | current] <PACKAGE>  //通过包名强制停止应用
       am kill [--user <USER_ID> | all | current] <PACKAGE>  //根据进程号杀死进程
       am kill-all                                           //杀掉所有后台进程
       am broadcast [--user <USER_ID> | all | current] <INTENT>  //发送广播
       am instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]
               [--user <USER_ID> | current]
               [--no-window-animation] [--abi <ABI>] <COMPONENT>
       am profile start [--user <USER_ID> current] [--sampling INTERVAL] <PROCESS> <FILE> //抓取traceview
       am profile stop [--user <USER_ID> current] [<PROCESS>]      //停止抓取traceview
       am dumpheap [--user <USER_ID> current] [-n] <PROCESS> <FILE>
       am set-debug-app [-w] [--persistent] <PACKAGE>
       am clear-debug-app
       am set-watch-heap <PROCESS> <MEM-LIMIT>
       am clear-watch-heap
       am bug-report [--progress]
       am monitor [--gdb <port>]
       am hang [--allow-restart]
       am restart                             //重启应用
       am idle-maintenance
       am screen-compat [on|off] <PACKAGE>
       am package-importance <PACKAGE>
       am to-uri [INTENT]
       am to-intent-uri [INTENT]
       am to-app-uri [INTENT]
       am switch-user <USER_ID>
       am start-user <USER_ID>
       am unlock-user <USER_ID> [TOKEN_HEX]
       am stop-user [-w] [-f] <USER_ID>
       am stack start <DISPLAY_ID> <INTENT>         
       am stack movetask <TASK_ID> <STACK_ID> [true|false]  //stack相关
       am stack resize <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>
       am stack resize-animated <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>
       am stack resize-docked-stack <LEFT,TOP,RIGHT,BOTTOM> [<TASK_LEFT,TASK_TOP,TASK_RIGHT,TASK_BOTTOM>]
       am stack size-docked-stack-test: <STEP_SIZE> <l|t|r|b> [DELAY_MS]
       am stack move-top-activity-to-pinned-stack: <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>
       am stack positiontask <TASK_ID> <STACK_ID> <POSITION>
       am stack list                                    //列出系统中的所有stack   
       am stack info <STACK_ID>                         //stack信息
       am stack remove <STACK_ID>                       //移除stack
       am task lock <TASK_ID>
       am task lock stop                         
       am task resizeable <TASK_ID> [0 (unresizeable) | 1 (crop_windows) | 2 (resizeable) | 3 (resizeable_and_pipable)]
       am task resize <TASK_ID> <LEFT,TOP,RIGHT,BOTTOM>
       am task drag-task-test <TASK_ID> <STEP_SIZE> [DELAY_MS] 
       am task size-task-test <TASK_ID> <STEP_SIZE> [DELAY_MS] 
       am get-config
       am suppress-resize-config-changes <true|false>
       am set-inactive [--user <USER_ID>] <PACKAGE> true|false
       am get-inactive [--user <USER_ID>] <PACKAGE>
       am send-trim-memory [--user <USER_ID>] <PROCESS>
               [HIDDEN|RUNNING_MODERATE|BACKGROUND|RUNNING_LOW|MODERATE|RUNNING_CRITICAL|COMPLETE]
       am get-current-user

am start: start an Activity.  Options are:    //启动Activity详细使用方法
    -D: enable debugging
    -N: enable native debugging
    -W: wait for launch to complete
    --start-profiler <FILE>: start profiler and send results to <FILE>
    --sampling INTERVAL: use sample profiling with INTERVAL microseconds
        between samples (use with --start-profiler)
    -P <FILE>: like above, but profiling stops when app goes idle
    -R: repeat the activity launch <COUNT> times.  Prior to each repeat,
        the top activity will be finished.
    -S: force stop the target app before starting the activity
    --track-allocation: enable tracking of object allocations
    --user <USER_ID> | current: Specify which user to run as; if not
        specified then run as the current user.
    --stack <STACK_ID>: Specify into which stack should the activity be put.
am startservice: start a Service.  Options are:
    --user <USER_ID> | current: Specify which user to run as; if not
        specified then run as the current user.

am stopservice: stop a Service.  Options are:
    --user <USER_ID> | current: Specify which user to run as; if not
        specified then run as the current user.

am force-stop: force stop everything associated with <PACKAGE>.
    --user <USER_ID> | all | current: Specify user to force stop;
        all users if not specified.

am kill: Kill all processes associated with <PACKAGE>.  Only kills.
  processes that are safe to kill -- that is, will not impact the user
  experience.
    --user <USER_ID> | all | current: Specify user whose processes to kill;
        all users if not specified.

am kill-all: Kill all background processes.

am broadcast: send a broadcast Intent.  Options are:
    --user <USER_ID> | all | current: Specify which user to send to; if not
        specified then send to all users.
    --receiver-permission <PERMISSION>: Require receiver to hold permission.

am instrument: start an Instrumentation.  Typically this target <COMPONENT>
  is the form <TEST_PACKAGE>/<RUNNER_CLASS> or only <TEST_PACKAGE> if there 
  is only one instrumentation.  Options are:
    -r: print raw results (otherwise decode REPORT_KEY_STREAMRESULT).  Use with
        [-e perf true] to generate raw output for performance measurements.
    -e <NAME> <VALUE>: set argument <NAME> to <VALUE>.  For test runners a
        common form is [-e <testrunner_flag> <value>[,<value>...]].
    -p <FILE>: write profiling data to <FILE>
    -w: wait for instrumentation to finish before returning.  Required for
        test runners.
    --user <USER_ID> | current: Specify user instrumentation runs in;
        current user if not specified.
    --no-window-animation: turn off window animations while running.
    --abi <ABI>: Launch the instrumented process with the selected ABI.
        This assumes that the process supports the selected ABI.

am trace-ipc: Trace IPC transactions.
  start: start tracing IPC transactions.
  stop: stop tracing IPC transactions and dump the results to file.
    --dump-file <FILE>: Specify the file the trace should be dumped to.

am profile: start and stop profiler on a process.  The given <PROCESS> argument
  may be either a process name or pid.  Options are:
    --user <USER_ID> | current: When supplying a process name,
        specify user of process to profile; uses current user if not specified.

am dumpheap: dump the heap of a process.  The given <PROCESS> argument may
  be either a process name or pid.  Options are:
    -n: dump native heap instead of managed heap
    --user <USER_ID> | current: When supplying a process name,
        specify user of process to dump; uses current user if not specified.

am set-debug-app: set application <PACKAGE> to debug.  Options are:
    -w: wait for debugger when application starts
    --persistent: retain this value

am clear-debug-app: clear the previously set-debug-app.

am set-watch-heap: start monitoring pss size of <PROCESS>, if it is at or
    above <HEAP-LIMIT> then a heap dump is collected for the user to report

am clear-watch-heap: clear the previously set-watch-heap.

am bug-report: request bug report generation; will launch a notification
    when done to select where it should be delivered. Options are: 
   --progress: will launch a notification right away to show its progress.

am monitor: start monitoring for crashes or ANRs.
    --gdb: start gdbserv on the given port at crash/ANR

am hang: hang the system.
    --allow-restart: allow watchdog to perform normal system restart

am restart: restart the user-space system.

am idle-maintenance: perform idle maintenance now.

am screen-compat: control screen compatibility mode of <PACKAGE>.

am package-importance: print current importance of <PACKAGE>.

am to-uri: print the given Intent specification as a URI.

am to-intent-uri: print the given Intent specification as an intent: URI.

am to-app-uri: print the given Intent specification as an android-app: URI.

am switch-user: switch to put USER_ID in the foreground, starting
  execution of that user if it is currently stopped.

am start-user: start USER_ID in background if it is currently stopped,
  use switch-user if you want to start the user in foreground.

am stop-user: stop execution of USER_ID, not allowing it to run any
  code until a later explicit start or switch to it.
  -w: wait for stop-user to complete.
  -f: force stop even if there are related users that cannot be stopped.

am stack start: start a new activity on <DISPLAY_ID> using <INTENT>.

am stack movetask: move <TASK_ID> from its current stack to the top (true) or   bottom (false) of <STACK_ID>.

am stack resize: change <STACK_ID> size and position to <LEFT,TOP,RIGHT,BOTTOM>.

am stack resize-docked-stack: change docked stack to <LEFT,TOP,RIGHT,BOTTOM>
   and supplying temporary different task bounds indicated by
   <TASK_LEFT,TOP,RIGHT,BOTTOM>

am stack size-docked-stack-test: test command for sizing docked stack by
   <STEP_SIZE> increments from the side <l>eft, <t>op, <r>ight, or <b>ottom
   applying the optional [DELAY_MS] between each step.

am stack move-top-activity-to-pinned-stack: moves the top activity from
   <STACK_ID> to the pinned stack using <LEFT,TOP,RIGHT,BOTTOM> for the
   bounds of the pinned stack.

am stack positiontask: place <TASK_ID> in <STACK_ID> at <POSITION>
am stack list: list all of the activity stacks and their sizes.

am stack info: display the information about activity stack <STACK_ID>.

am stack remove: remove stack <STACK_ID>.

am task lock: bring <TASK_ID> to the front and don't allow other tasks to run.

am task lock stop: end the current task lock.

am task resizeable: change resizeable mode of <TASK_ID>.
   0 (unresizeable) | 1 (crop_windows) | 2 (resizeable) | 3 (resizeable_and_pipable)

am task resize: makes sure <TASK_ID> is in a stack with the specified bounds.
   Forces the task to be resizeable and creates a stack if no existing stack
   has the specified bounds.

am task drag-task-test: test command for dragging/moving <TASK_ID> by
   <STEP_SIZE> increments around the screen applying the optional [DELAY_MS]
   between each step.

am task size-task-test: test command for sizing <TASK_ID> by <STEP_SIZE>   increments within the screen applying the optional [DELAY_MS] between
   each step.

am get-config: retrieve the configuration and any recent configurations
  of the device.
am suppress-resize-config-changes: suppresses configuration changes due to
  user resizing an activity/task.

am set-inactive: sets the inactive state of an app.

am get-inactive: returns the inactive state of an app.

am send-trim-memory: send a memory trim event to a <PROCESS>.

am get-current-user: returns id of the current foreground user.


<INTENT> specifications include these flags and arguments:
    [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]
    [-c <CATEGORY> [-c <CATEGORY>] ...]
    [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]
    [--esn <EXTRA_KEY> ...]
    [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]
    [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]
    [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]
    [--ef <EXTRA_KEY> <EXTRA_FLOAT_VALUE> ...]
    [--eu <EXTRA_KEY> <EXTRA_URI_VALUE> ...]
    [--ecn <EXTRA_KEY> <EXTRA_COMPONENT_NAME_VALUE>]
    [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]
        (mutiple extras passed as Integer[])
    [--eial <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]
        (mutiple extras passed as List<Integer>)
    [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]
        (mutiple extras passed as Long[])
    [--elal <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]
        (mutiple extras passed as List<Long>)
    [--efa <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]]
        (mutiple extras passed as Float[])
    [--efal <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]]
        (mutiple extras passed as List<Float>)
    [--esa <EXTRA_KEY> <EXTRA_STRING_VALUE>[,<EXTRA_STRING_VALUE...]]
        (mutiple extras passed as String[]; to embed a comma into a string,
         escape it using "\,")
    [--esal <EXTRA_KEY> <EXTRA_STRING_VALUE>[,<EXTRA_STRING_VALUE...]]
        (mutiple extras passed as List<String>; to embed a comma into a string,
         escape it using "\,")
    [--f <FLAG>]
    [--grant-read-uri-permission] [--grant-write-uri-permission]
    [--grant-persistable-uri-permission] [--grant-prefix-uri-permission]
    [--debug-log-resolution] [--exclude-stopped-packages]
    [--include-stopped-packages]
    [--activity-brought-to-front] [--activity-clear-top]
    [--activity-clear-when-task-reset] [--activity-exclude-from-recents]
    [--activity-launched-from-history] [--activity-multiple-task]
    [--activity-no-animation] [--activity-no-history]
    [--activity-no-user-action] [--activity-previous-is-top]
    [--activity-reorder-to-front] [--activity-reset-task-if-needed]
    [--activity-single-top] [--activity-clear-task]
    [--activity-task-on-home]
    [--receiver-registered-only] [--receiver-replace-pending]
    [--receiver-foreground]
    [--selector]
    [<URI> | <PACKAGE> | <COMPONENT>]

pm命令

当要查询系统中某个应用是否存在,或者存在路径,就可以根据对应的包名来查找对应的apk信息。
usage: pm path [--user USER_ID] PACKAGE
       pm dump PACKAGE                       //dump某个apk
       pm install [-lrtsfd] [-i PACKAGE] [--user USER_ID] [PATH]  //安装应用
       pm install-create [-lrtsfdp] [-i PACKAGE] [-S BYTES]
               [--install-location 0/1/2]
               [--force-uuid internal|UUID]
       pm install-write [-S BYTES] SESSION_ID SPLIT_NAME [PATH]
       pm install-commit SESSION_ID
       pm install-abandon SESSION_ID
       pm uninstall [-k] [--user USER_ID] PACKAGE            //卸载应用
       pm set-installer PACKAGE INSTALLER                    //设置installer
       pm move-package PACKAGE [internal|UUID]               //移除package
       pm move-primary-storage [internal|UUID]
       pm clear [--user USER_ID] PACKAGE                     //清除应用信息
       pm enable [--user USER_ID] PACKAGE_OR_COMPONENT
       pm disable [--user USER_ID] PACKAGE_OR_COMPONENT
       pm disable-user [--user USER_ID] PACKAGE_OR_COMPONENT
       pm disable-until-used [--user USER_ID] PACKAGE_OR_COMPONENT
       pm default-state [--user USER_ID] PACKAGE_OR_COMPONENT
       pm hide [--user USER_ID] PACKAGE_OR_COMPONENT
       pm unhide [--user USER_ID] PACKAGE_OR_COMPONENT
       pm grant [--user USER_ID] PACKAGE PERMISSION
       pm revoke [--user USER_ID] PACKAGE PERMISSION
       pm reset-permissions                                //重置权限
       pm set-app-link [--user USER_ID] PACKAGE {always|ask|never|undefined}
       pm get-app-link [--user USER_ID] PACKAGE
       pm set-install-location [0/auto] [1/internal] [2/external]   //设置安装路径
       pm get-install-location                                     获取安装路径
       pm set-permission-enforced PERMISSION [true|false]
       pm trim-caches DESIRED_FREE_SPACE [internal|UUID]
       pm create-user [--profileOf USER_ID] [--managed] [--restricted] [--ephemeral] [--guest] USER_NAME
       pm remove-user USER_ID
       pm get-max-users

NOTE: 'pm list' commands have moved! Run 'adb shell cmd package'
  to display the new commands.   //使用adb shell pm list packages -f获取系统中所有apk的安装路径

pm path: print the path to the .apk of the given PACKAGE. //adb shell path +包名,获取apk路径

pm dump: print system state associated with the given PACKAGE.

pm install: install a single legacy package
pm install-create: create an install session
    -l: forward lock application
    -r: replace existing application
    -t: allow test packages
    -i: specify the installer package name
    -s: install application on sdcard
    -f: install application on internal flash
    -d: allow version code downgrade (debuggable packages only)
    -p: partial application install
    -g: grant all runtime permissions
    -S: size in bytes of entire session

pm install-write: write a package into existing session; path may
  be '-' to read from stdin
    -S: size in bytes of package, required for stdin

pm install-commit: perform install of fully staged session
pm install-abandon: abandon session

pm set-installer: set installer package name

pm uninstall: removes a package from the system. Options:
    -k: keep the data and cache directories around after package removal.

pm clear: deletes all data associated with a package.

pm enable, disable, disable-user, disable-until-used, default-state:
  these commands change the enabled state of a given package or
  component (written as "package/class").

pm grant, revoke: these commands either grant or revoke permissions
    to apps. The permissions must be declared as used in the app's
    manifest, be runtime permissions (protection level dangerous),
    and the app targeting SDK greater than Lollipop MR1.

pm reset-permissions: revert all runtime permissions to their default state.

pm get-install-location: returns the current install location.
    0 [auto]: Let system decide the best location
    1 [internal]: Install on internal device storage
    2 [external]: Install on external media

pm set-install-location: changes the default install location.
  NOTE: this is only intended for debugging; using this can cause
  applications to break and other undersireable behavior.
    0 [auto]: Let system decide the best location
    1 [internal]: Install on internal device storage
    2 [external]: Install on external media

pm trim-caches: trim cache files to reach the given free space.

pm create-user: create a new user with the given USER_NAME,
  printing the new user identifier of the user.

pm remove-user: remove the user with the given USER_IDENTIFIER,
  deleting all data associated with that user

dumpsys

该命令用于打印出当前系统信息,默认打印出设备中所有service的信息,可以在命令后面加指定的service name

可以通过adb shell dumpsys + services可以打印出对应系统service的dump信息。可以将某个模块详细信息输出到控制台,可以更加直观的分析。

例如要打印Activity的dump信息:adb shell dumpsys activity

Currently running services:
  AtCmdFwd
  DockObserver
  SurfaceFlinger     //重要
  accessibility
  account
  activity          //重要
  alarm             //重要
  android.security.keystore
  android.service.gatekeeper.IGateKeeperService
  appops
  appwidget
  assetatlas
  audio           //重要
  backup   
  battery         //重要
  batteryproperties
  batterystats
  bluetooth_manager
  carrier_config
  clipboard
  cneservice
  com.qualcomm.location.izat.IzatService
  com.qualcomm.qti.qseeproxy
  commontime_management
  connectivity
  connectivity_metrics_logger
  connmetrics
  consumer_ir
  content
  contexthub_service
  country_detector
  cpuinfo       
  dbinfo
  device_policy
  deviceidle
  devicestoragemonitor
  diskstats
  display       //重要
  display.qservice
  dpmservice
  dreams
  drm.drmManager
  dropbox
  eSEPowerManagerService
  ethernet
  extphone
  gfxinfo
  gpu         //重要
  graphicsstats
  hardware_properties
  imms
  ims
  input       //重要
  input_method  //重要
  iphonesubinfo
  isms
  isub
  jobscheduler
  launcherapps
  location
  lock_settings
  mdtp
  media.audio_flinger
  media.audio_policy
  media.camera
  media.camera.proxy
  media.codec
  media.drm
  media.extractor
  media.player
  media.radio
  media.resource_manager
  media.sound_trigger_hw
  media_projection
  media_router
  media_session
  meminfo       //输出内存相关,重要
  midi
  mount        
  netd
  netd_listener
  netpolicy
  netstats
  network_management
  network_score
  network_time_update_service
  notification
  okay_property
  otadexopt
  package          //重要
  permission
  persistent_data_block
  phone
  pinner
  power           //电源管理,重要
  print
  processinfo
  procstats
  qti.ims.connectionmanagerservice
  qti.ims.ext
  qtitetherservice
  recovery
  regionalization
  restrictions
  rttmanager
  samplingprofiler
  scheduling_policy
  search
  sensorservice
  serial
  servicediscovery
  shortcut
  simphonebook
  soundtrigger
  statusbar
  telecom
  telephony.registry
  textservices
  trust
  uimode
  updatelock
  usagestats
  usb
  user
  vendor.qcom.PeripheralManager
  vibrator
  voiceinteraction
  vrmanager
  wallpaper     //重要
  webviewupdate
  wifi
  wifip2p
  wifiscanner
  window         //Windows管理信息,重要

wakelock

       wakelock主要是系统为上层提供控制屏幕亮度显示,以及cpu唤醒的一个接口。我们可以通过上面学习的dumpsys命令来查看系统此时存在那些wakelock。

adb shell dumpsys power

Wake Locks: size=1
  SCREEN_BRIGHT_WAKE_LOCK        'WindowManager' ON_AFTER_RELEASE ACQ=-1s234ms (uid=1000 pid=1495 ws=WorkSource{1000})

       此时系统只有一个wakelock,类型为SCREEN_BRIGHT_WAKE_LOCK,所属模块为WIndowManager。下面讲解一下如何使用wakelock。

    PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); //获取PowerManager
    sWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK   //使用power创建一个新的wakelock,执行类型
            | PowerManager.ACQUIRE_CAUSES_WAKEUP
            | PowerManager.ON_AFTER_RELEASE, TAG);
    sWakeLock.acquire();                                    //使wakelock起作用,当不需要时使用release释放wakelock

   任何应用程序使用WakeLock,必须要求android.permission.WAKE_LOCK许可在应用程序的清单上使用<uses-permission>元素。下面为我们可以调用的公共方法。

Public Methods
void acquire() --->当你创建这个WakeLock时,确保这个设备按照你的要求保持唤醒的状态。
void acquire(long timeout) --->当你创建这个WakeLock时,确保这个设备按照你的要求保持唤醒的状态。
void isHeld()
void release() ---> 按照你的要求释放cpu或屏幕。
void setReferenceCounted(boolean value)--->如果需要使用引用计数,则将value设置为true。默认设置为                                                               true,表示当用户申请多少次锁时,就必须释放多少次。
void setWorkSource(WorkSource ws)
String toString() --->返回一个简洁的字符串用来将这个对象描述为人们可读懂的。
void Finalize() --->当垃圾收集器检测到该实例不再运行调用。

wakelock有不同的类型,我们可以根据需要选择不同的wakelock类型。

Flag Value CPU Screen Keyboard
PARTIAL_WAKE_LOCK On Off   Off
SCREEN_DIM_WAKE_LOCK On  Dim Off
SCREEN_BRIGHT_WAKE_LOCK On Bright Off
FULL_WAKE_LOCK On Bright Bright
      如果你持有的partial wakelock,CPU将继续运行,不论何时,即使当用户按下电源按钮。对于所有其他wakelocks,但用户使用电源按钮时,系统将进入睡眠,cpu将不再继续运行。
PARTIAL_WAKE_LOCK:保持CPU 运转,屏幕和键盘灯有可能是关闭的。
SCREEN_DIM_WAKE_LOCK:保持CPU 运转,允许保持屏幕显示但有可能是灰的,允许关闭键盘灯
SCREEN_BRIGHT_WAKE_LOCK:保持CPU 运转,允许保持屏幕高亮显示,允许关闭键盘灯
FULL_WAKE_LOCK:保持CPU 运转,保持屏幕高亮显示,键盘灯也保持亮度
ACQUIRE_CAUSES_WAKEUP:正常唤醒锁实际上并不打开照明。相反,一旦打开他们会一直仍然保持(例如来世user的activity)。当获得wakelock,这个标志会使屏幕或/和键盘立即打开。一个典型的使用就是可以立即看到那些对用户重要的通知。
ON_AFTER_RELEASE:设置了这个标志,当wakelock释放时用户activity计时器会被重置,导致照明持续一段时间。如果你在wacklock条件中循环,这个可以用来减少闪烁

  我们可以创建多个锁,并hold它,即使对同一类型,也如此,对于某类型的wakelock只要有一个被hold,那么它所对应的电源状态(illumination),就将不会超时,将被延续(hold).在上表中我们把越往下的,称为更高一级的wakelocks.当高级和低级wakelocks相遇的时候,高级起作用。在上面的flag上还再加上如下的2个flag,但是他们和PARTIAL_WAKE_LOCK.组合没任何意义ACQUIRE_CAUSES_WAKEUP默认情况下wake locks并不是马上开启CPU或Screen或Keyboard的(对于Screen是Dim或Bright,Keyboard是Bright. wake locks只是在被开启后(比如用户的活动),让设备延续(保存)你设定开启的状态. 但是如果加上ACQUIRE_CAUSES_WAKEUP就可以让Screen或Keyboar没开启的情况,马上开启它们。 典型的应用就是在收到一个重要的notifications时,需要马上点亮屏幕。

addr2line

在分析framework稳定性问题时会遇到Native crash的发生,而Native crash与java crash的堆栈信息不同。下面是一段Native 堆栈的片段。
03-19 14:58:50.034   319  3145 D kevin_Render: #01 pc 0005f8a5  /system/lib/libmediaplayerservice.so (android::NuPlayer::Renderer::setAnchorTime(long long, long long, long long, bool)+72)
03-19 14:58:50.034   319  3145 D kevin_Render: #02 pc 000618c5  /system/lib/libmediaplayerservice.so (android::NuPlayer::Renderer::onNewAudioMediaTime(long long)+88)
03-19 14:58:50.035   319  3145 D kevin_Render: #03 pc 00062249  /system/lib/libmediaplayerservice.so (android::NuPlayer::Renderer::onDrainAudioQueue()+764)
03-19 14:58:50.035   319  3145 D kevin_Render: #04 pc 00062ec3  /system/lib/libmediaplayerservice.so (android::NuPlayer::Renderer::onMessageReceived(android::sp<android::AMessage> const&)+866)
03-19 14:58:50.035   319  3145 D kevin_Render: #05 pc 0000d12d  /system/lib/libstagefright_foundation.so (android::ALooperRoster::deliverMessage(android::sp<android::AMessage> const&)+164)
03-19 14:58:50.035   319  3145 D kevin_Render: #06 pc 0000cac1  /system/lib/libstagefright_foundation.so (android::ALooper::loop()+216)
03-19 14:58:50.035   319  3145 D kevin_Render: #07 pc 00010977  /system/lib/libutils.so (android::Thread::_threadLoop(void*)+110)
03-19 14:58:50.035   319  3145 D kevin_Render: #08 pc 00016f5b  /system/lib/libc.so (__pthread_start(void*)+30)
03-19 14:58:50.035   319  3145 D kevin_Render: #09 pc 00014f7b  /system/lib/libc.so (__start_thread+6)
根据上面的堆栈,我们并不能看到具体哪个函数,哪行代码出现了问题,这个时候我们就需要解析Native函数的地址值,如上面 00062ec3这样的符号,我们需要通过addr2line来进行解析。我们可以使用linux系统提供的addr2line命令,也可以使用源代码sdk中的工具。具体命令如下:
addr2line -e out/target/product/项目名/symbols/system/lib/libxxx.so -f -C <addr> 
如果我们想要解析03行的地址值00062249
kevin@kevin-All-Series:~/work/$ addr2line -e out/target/product/miki8163_9003/symbols/system/lib/libmediaplayerservice.so -f -C 00062249
android::NuPlayer::Renderer::onDrainAudioQueue()
/home/kevin/work/source/sm8163/sm8163/frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp:868
解析后我们看到该行代码属于函数android::NuPlayer::Renderer::onDrainAudioQueue(),在NuPlayerRenderer.cpp的868行,这样就十分方便了。

meminfo

当我们查询系统的内存信息除了使用dumpsys命令,还可以直接使用adb shell cat /proc/meminfo
kevin@kevin-All-Series:~/work$ adb shell cat /proc/meminfo
MemTotal:        1844152 kB       //内存的总大小
MemFree:           41364 kB       //空闲内存
MemAvailable:     230892 kB       //可用的内存
Buffers:            3520 kB       
Cached:           230312 kB       //缓存
SwapCached:         6484 kB
Active:           538568 kB
Inactive:         257680 kB
Active(anon):     426264 kB
Inactive(anon):   144884 kB
Active(file):     112304 kB
Inactive(file):   112796 kB
Unevictable:         256 kB
Mlocked:             256 kB
SwapTotal:        524284 kB
SwapFree:            156 kB
Dirty:                44 kB      //脏页
Writeback:             0 kB
AnonPages:        559864 kB
Mapped:           216980 kB
Shmem:              8608 kB
Slab:             354744 kB
SReclaimable:      56820 kB
SUnreclaim:       297924 kB
KernelStack:       35488 kB
PageTables:        34172 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:     1446360 kB
Committed_AS:   63267272 kB
VmallocTotal:   244318144 kB
VmallocUsed:      116780 kB
VmallocChunk:   244056036 kB

fastboot

      Android系统编译后会生成很多的image文件,我们可以通过刷机工具将image烧制到手机中,我们也可以使用fastboot命令很方便的将image文件烧制到系统中。下面为fastboot的使用方法:

1.开机状态下执行

adb reboot bootloader(执行命令后系统会关机重启,进入fastboot模式)

2.烧写system.img

fastboot flash system system.img

如果要烧制boot.img,那么就执行fastboot flash boot boot.img,其他image也一样。

3.重启

fastboot reboot

简单几步就可以快速将我们编译出来的文件烧制到手机中。

bootchart

      bootchart是一个用于linux启动过程性能分析的开源软件工具,在系统启动过程自动收集CPU占用率、进程等信息,并以图形方式显示分析结果,可用作指导优化系统启动过程。bootchart详细使用说明请参考《Android7.0 bootchart工具使用说明

LOG分析

      在调试Android系统时如果要实时的查看输出的log信息就可以使用adb logcat -vthreadtime或者adb logcat在终端输出需要的log信息,在5.1及其之前的版本需要adb logcat后需要加上-vthreadtime 意思是将输出时间以及进程线程信息,5.1之后的版本系统自动添加了时间与进程线程信息,直接用adb logcat就可以。

    而更多的时候我们需要分析log文件,也就是在系统运行时会将log信息输出在log文件中,以便在出现问题时让开发人员进行分析,下面讲几个场景的关键LOG信息。

开机LOG

     在分析开机log时通过根据开机流程进行分段分析,这里省略bootloader中log,首先从执行kernel完成分析,到启动launcher分析结束,来看其中执行了那些过程,如果开机流程出现问题,就可以定位哪个流程出现问题。

//开始启动init进程,也就是kernel启动完成,取时间戳2.172934s,为启动kernel所耗时.
[01-01 08:30:04.349] <13>[    2.172934] c5 init: init started!

//开始启动zygote,使用时间戳相减4.281027-2.172934计算出启动init进程到启动zygote耗时2.1s.
[01-01 08:30:04.367] <13>[    4.281027] c0 init: Starting service 'zygote'...

//进入zygote,由于kernel时间与android不能相减,所以无法计算启动zygote到进入zygote所花费时间.
A001-01 08:30:05.036  2810  2810 D AndroidRuntime: >>>>>> START com.android.internal.os.ZygoteInit uid 0 <<<<<<

//进入SystemServer,使用android时间08:30:08.629-08:30:05.036 获得,从进入zygote到SystemServer耗时
A101-01 08:30:08.629  3285  3285 I SystemServer: Entered the Android system server!

//开始初始化Package, 使用android时间相减,获取进入SystemServer到初始化Package的耗时.
A101-01 08:30:09.021  3285  3285 I SystemServer: Package Manager
//扫描解析应用耗时,可以直接获取1.544 seconds
A101-01 08:30:10.813  3285  3285 I PackageManager: Time to scan packages: 1.544 seconds

//开始执行network systemReady函数,使用android时间相减,获取初始化Package完成到开始执行network systemReady函数的耗时.
A101-01 08:30:14.285  3285  3285 D NetworkManagement: enabling bandwidth control

//network执行完相关命令,使用android时间相减,获取执行network执行相关命令耗时.
A101-01 08:30:15.475  3285  3285 I SystemServiceManager: Starting phase 600

//启动launcher, 使用android时间相减,获取执行network命令完成,到启动launcher耗时
A101-01 08:30:15.915  3285  3285 I ActivityManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000000 cmp=com.android.launcher3/.Launcher} from uid 0 on display 0

关机LOG

      在分析关机log时,是从长按power键,点击确认关机,到灭屏震动关机。关机主要经过的流程为发送关机广播,关闭AMS,PMS,关闭移动数据,卸载sd卡等流程,最后调用power往底层系统配置中写入对应的信息,底层读取信息,进行关机。

//按确认键,开始关机
01-01 12:49:10.410  1147  1226 I ShutdownThread: shutdown goto shutdownInner

//发送关机广播,使用android时间相减,获得关机到发送关机广播之间耗时
01-01 12:49:10.498  1147  2504 I ShutdownThread: Sending shutdown broadcast...

//开始shutdown activity manager, 使用android时间相减,获得关机广播处理耗时
01-01 12:49:10.637  1147  2504 I ShutdownThread: Shutting down activity manager...

 //开始关闭蜂窝网,使用android时间相减,获得开机shutdown activity manager到关闭蜂窝网耗时
01-01 12:49:10.746  1147  2522 W ShutdownThread: Turning off cellular radios...     

 //开始关闭Radio,获得关闭蜂窝网到开始关闭Radio耗时
01-01 12:49:10.753  1147  2522 I ShutdownThread: Waiting for NFC, Bluetooth and Radio...

//关闭Radio完成,使用android时间相减,获得关闭Radio耗时,该流程可能耗时较长.重点
01-01 12:49:11.255  1147  2522 I ShutdownThread: NFC, Radio and Bluetooth shutdown complete.

//开始关闭MountService,使用android时间相减,获得关闭Radio完成到关闭MountService耗时.
01-01 12:49:11.257  1147  2504 I ShutdownThread: Shutting down MountService

//关闭MountService完成,使用android时间相减,获得关闭MountService耗时.该流程可能耗时较长.重点
01-01 12:49:11.268  1147  1304 W ShutdownThread: Result code 0 from MountService.shutdown

//调用底层接口,关机完成.
01-01 12:49:11.776  1147  2504 I ShutdownThread: Performing low-level shutdown...

首启动应用LOG

      在分析分析应用启动问题时,我们通常分析event log信息,使用命令adb logcat -b events,其中的log信息主要为应用的生命周期信息。通过查看生命周期信息来分析,应用是否行为异常。

首次启动

//开始启动activity
07-29 10:26:57.033  1173  1995 I am_create_activity: [0,102422011,29,com.android.dialer/.DialtactsActivity,android.intent.action.MAIN,NULL,NULL,270532608]

//launcher pause完成。
07-29 10:26:57.109  2009  2009 I am_on_paused_called: [0,com.android.launcher3.Launcher]

//进程开始创建,与开始启动activity时间相减,获得准备工作耗时.         
07-29 10:26:57.142  1173  1996 I am_proc_start: [0,5164,10038,com.android.dialer,activity,com.android.dialer/.DialtactsActivity]

//进程bound完成,与进程创建之间这个过程可以理解为进程创建的时间。为创建进程耗时.
07-29 10:26:57.195  1173  1404 I am_proc_bound: [0,5164,com.android.dialer]

//ams向应用发消息进行启动activity操作
07-29 10:26:57.199  1173  1404 I am_restart_activity: [0,102422011,29,com.android.dialer/.DialtactsActivity]

//activity resume执行完成,与restart_activity之间这个可以理解为Activity的生命周期执行时间。为生命周期耗时
07-29 10:26:57.394  5164  5164 I am_on_resume_called: [0,com.android.dialer.DialtactsActivity]

//这里activity显示完成,当前Activity显示过程644ms,整个显示过程644ms。
//从activity resume完成到这个log之间可以理解为广义上的绘制过程(measure,layout,draw,render)。
07-29 10:26:57.757  1173  1356 I am_activity_launch_time: [0,102422011,com.android.dialer/.DialtactsActivity,644,644]

非首次启动

//开始启动activity,到launcher pause完成。
01-01 13:26:59.645  1162  1733 I am_create_activity: [0,233918075,10,com.android.messaging/.ui.conversationlist.ConversationListActivity,android.intent.action.MAIN,NULL,NULL,270532608]

//launcher pause完成。
 07-29 10:26:57.109  2009  2009 I am_on_paused_called: [0,com.android.launcher3.Launcher]

//ams向应用发消息进行启动activity操作,准备工作
01-01 13:26:59.710  1162  1439 I am_restart_activity: [0,233918075,10,com.android.messaging/.ui.conversationlist.ConversationListActivity]

 //activity resume执行完成,与restart_activity之间这个可以理解为Activity的生命周期执行时间。
01-01 13:26:59.782  2189  2189 I am_on_resume_called: [0,com.android.messaging.ui.conversationlist.ConversationListActivity]

//这里activity显示完成,当前Activity显示过程168ms,整个显示过程168ms。
//从activity resume完成到这个log之间可以理解为广义上的绘制过程(measure,layout,draw,render)。
01-01 13:26:59.871  1162  1283 I am_activity_launch_time: [0,233918075,com.android.messaging/.ui.conversationlist.ConversationListActivity,168,168]

亮屏关键LOG

//kernel中断key事件,ScanCode:116为Power键,value:1为down事件
<6>[41051.961488] c1 [SPRD_KEY_INFO]Key:Power Key ScanCode:116 value:1

//InputReader获取power键事件,code=116为power键
01-02 08:04:44.016   600   643 D InputReader: processEventsLocked: type=1 Count=2 code=116 value=1 deviceId=2

//Power收到亮屏调用,开始亮屏
01-02 08:04:44.037   600   643 I PowerManagerService: Waking up from sleep (uid 1000)...

//开始block亮屏,直到keyguard与windows绘制完成
01-02 08:04:44.052   600   622 I DisplayPowerController: Blocking screen on until initial contents have been drawn.

//开始绘制keyguard
01-02 08:04:44.052   600   622 I WindowManager: Screen turning on...

//tosuspend_disable()耗时283s,有时为setPowerMode耗时长
01-02 08:04:44.347   600   676 D PowerManagerService-JNI: Excessive delay in autosuspend_disable() while 

//yguard绘制完成
01-02 08:04:44.504   600   700 D WindowManager: mKeyguardDelegate.ShowListener.onDrawn.
01-02 08:04:44.504   600   614 W WindowManager: Setting mKeyguardDrawComplete

//windows绘制完成
01-02 08:04:44.551   600   620 I WindowManager: All windows ready for display!
01-02 08:04:44.551   600   614 W WindowManager: Setting mWindowManagerDrawComplete

//block亮屏,总共blocked亮屏505ms
01-02 08:04:44.557   600   622 I DisplayPowerController: Unblocked screen on after 505 ms

//屏幕亮度,往节点中写亮度值
01-02 08:04:44.558   600   676 D LIGHTS  : file:vendor/sprd/modules/lights/lights.c, func:set_light_backlight, brightness=25
01-02 08:04:44.559   600   676 E LIGHTS  : file:vendor/sprd/modules/lights/lights.c, func:write_int, path=/sys/class/backlight/sprd_backlight/brightness, value=25

来电亮屏LOG

//创建dialer进程
12-22 15:46:23.704   595   595 I ActivityManager: Start proc 5067:com.android.dialer/u0a1 for service com.android.dialer/com.android.incallui.InCallServiceImpl

//来电响铃
12-22 15:46:23.736   595  5083 I Telecom : AsyncRingtonePlayer: Play ringtone.

//启动InCallActivity 
12-22 15:46:24.410   595  1016 I ActivityManager: START u0 {act=android.intent.action.MAIN flg=0x10040000 cmp=com.android.dialer/com.android.incallui.InCallActivity (has extras)} from uid 10001 on display 0

//显示InCallActivity
12-22 15:46:25.715   595   615 I am_activity_launch_time: [0,134650581,com.android.dialer/com.android.incallui.InCallActivity,1293,1299]

//power收到亮屏调用
12-22 15:46:25.730   595   615 I PowerManagerService: Waking up from sleep due toandroid android.server.wm:TURN_ON (uid 1000)...

//点亮屏幕
A012-22 15:46:26.220   595   679 D LIGHTS  : file:vendor/sprd/modules/lights/lights.c, func:set_light_backlight, brightness=25
A012-22 15:46:26.221   595   679 E LIGHTS  : file:vendor/sprd/modules/lights/lights.c, func:write_int, path=/sys/class/backlight/sprd_backlight/brightness, value=25

灭屏LOG

//kernel中断key事件,ScanCode:116为Power键,value:1为down事件
<6>[42586.507314] c1 [SPRD_KEY_INFO]Key:Power Key ScanCode:116 value:1

//value:0为up事件
<6>[42586.660288] c1 [SPRD_KEY_INFO]Key:Power Key ScanCode:116 value:0

//Input收到power键down事件
01-06 13:25:48.460  3339  3447 D InputReader: processEventsLocked: type=1 Count=2 code=116 value=1 deviceId=2

//Input收到power键up事件
01-06 13:25:48.643  3339  3447 D InputReader: processEventsLocked: type=1 Count=2 code=116 value=0 deviceId=2

//短按power键,power开始灭屏
01-06 13:25:48.643  3339  3447 I PowerManagerService: Going to sleep due to power button (uid 1000)...

//设置全局变量,通知keyguard灭屏了
01-06 13:25:48.645  3339  3361 I WindowManager: Screen turned off...

//设置屏幕亮度为0
01-06 13:25:48.645  3339  3493 D LIGHTS  : file:vendor/sprd/modules/lights/lights.c, func:set_light_backlight, brightness=0
01-06 13:25:48.645  3339  3493 E LIGHTS  : file:vendor/sprd/modules/lights/lights.c, func:write_int, path=/sys/class/backlight/sprd_backlight/brightness, value=0

添加LOG信息

java层添加

    当在调试系统代码时,如果要确认代码是否走了某个流程,就可以添加log信息,如果执行某项操作可以将该行log打印出来就说明走了该流程。
import android.util.Log;    //导入LOG包

    private static String getActiveSubscriberId(Context context, int subId) {
        final TelephonyManager tele = TelephonyManager.from(context);
        String retVal = tele.getSubscriberId(subId);
        Log.d(TAG, "getActiveSubscriberId=" + retVal + " subId=" + subId); //添加log信息
        return retVal;
    }

如果想知道某行代码是怎么调用过来的,他的调用关系是什么,就可以通过打印堆栈的方式来确认。具体方式如下;

try {
    throw new Exception("Call Stack Trace");
} catch (Exception e) {
    Log.i("kevin", "xxx", e);
}

例如在唤醒屏幕流程,如果想要分析wakeUpInternal是怎么调用过来的,就可以打印堆栈的方式来确认调用关系。

    private void wakeUpInternal(long eventTime, String reason, int uid, String opPackageName,
            int opUid) {
        synchronized (mLock) {

            try {
                throw new Exception("Call Stack Trace");
            } catch (Exception e) {
                Log.i("kevin", "xxx", e);
            }

            if (wakeUpNoUpdateLocked(eventTime, reason, uid, opPackageName, opUid)) {
                updatePowerStateLocked();
            }
        }
    }

当我们修改好代码后编译services.jar,将services.jar push到手机中,重启,按power键亮灭屏就可以看到如下信息。


C++层添加

如果想要在C++层添加log信息,需要在对应的Android.mk文件中添加下面代码,添加共享库。

LOCAL_SHARED_LIBRARIES := libutils

用如下log可以打印C++层的信息。

ALOGD("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2); 

如果要在C++层中打印对应堆栈,使用如下代码:

#include <utils/CallStack.h>

CallStack stack;
stack.update();
stack.log("kevin");

示例代码如下:

void NuPlayer::start() {
    CallStack stack;
    stack.update();
    stack.log("kevin");
    (new AMessage(kWhatStart, this))->post();
}

输出堆栈如下:


可以使用前面学习的addr2line


可以看到具体的代码调用。

猜你喜欢

转载自blog.csdn.net/fu_kevin0606/article/details/79616216