概述
Framework开发是一项非常繁琐复杂的工作,需要阅读大量的源代码,分析及其多的LOG信息来定位错误位置。这个时候如果使用一些工具或者知道如何定位重要LOG信息,就可以使一些复杂的工作变的简单很多,使我们分析问题的效率变得更快,不再为阅读大量的源代码而感到一筹莫展。本文将针对一些场景讲解如何分析系统LOG信息,如何添加LOG定位错误信息,以及常用工具以及使用方法。
常用工具
HierarchyViewer
HierarchyViewer是随Android SDK发布的工具,位置在sdk的tools文件夹下,名为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 |
SCREEN_DIM_WAKE_LOCK:保持CPU 运转,允许保持屏幕显示但有可能是灰的,允许关闭键盘灯
SCREEN_BRIGHT_WAKE_LOCK:保持CPU 运转,允许保持屏幕高亮显示,允许关闭键盘灯
FULL_WAKE_LOCK:保持CPU 运转,保持屏幕高亮显示,键盘灯也保持亮度
ON_AFTER_RELEASE:设置了这个标志,当wakelock释放时用户activity计时器会被重置,导致照明持续一段时间。如果你在wacklock条件中循环,这个可以用来减少闪烁
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中的工具。具体命令如下:
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/meminfokevin@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层添加
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