浩子

学无止境


  • 首页

  • 标签

  • 分类

  • 归档

adb 命令大全

发表于 2019-01-17 | 分类于 android
字数统计: 12,589 | 阅读时长 ≈ 56

adb 命令大全

转自 https://blog.csdn.net/zhonglunshun/article/details/78362439

此文章内容整合自网络,欢迎转载。

ADB是什么


Adb的全称为Android Debug Bridge:android调试桥梁,下图为Android官方对adb的介绍:
这里写图片描述
可以看出,Android的初衷是用adb这样的一个工具来协助开发人员在开发android应用的过程中更快更好的调试apk,因此adb具有安装卸载apk、拷贝推送文件、查看设备硬件信息、查看应用程序占用资源、在设备执行shell命令等功能;

我们可以在android sdk安装目录的platform-tools目录下找到adb工具;

ADB架构


为了方便理解,我们从三个指令入手,我们经常会用到adb start-server,adb devices,adb kill-server。
然后我们经常会看到这样的输出界面:

1
2
3
4
C:\Users\dell>adb devices
List of devices attached
* daemon not running. starting it now at tcp:5037 *
* daemon started successfully *

于是这里有三点疑问,为什么是server,server对应的是服务器或者服务端吧?如果手机是客户端,服务端是不是指电脑上开启的服务。还有这个呆萌(daemon )又是啥?

ADB是一个C/S架构的应用程序,由三部分组成:

  1. 运行在pc端的adb client:
    命令行程序”adb”用于从shell或脚本中运行adb命令。首先,“adb”程序尝试定位主机上的ADB服务器,如果找不到ADB服务器,“adb”程序自动启动一个ADB服务器。接下来,当设备的adbd和pc端的adb server建立连接后,adb client就可以向ADB servcer发送服务请求;
  2. 运行在pc端的adb server:
    ADB Server是运行在主机上的一个后台进程。它的作用在于检测USB端口感知设备的连接和拔除,以及模拟器实例的启动或停止,ADB Server还需要将adb client的请求通过usb或者tcp的方式发送到对应的adbd上;
  3. 运行在设备端的常驻进程adb demon (adbd):
    程序“adbd”作为一个后台进程在Android设备或模拟器系统中运行。它的作用是连接ADB服务器,并且为运行在主机上的客户端提供一些服务;
    这里写图片描述
    这里写图片描述

    基本用法


adb能做什么?答案是所有能对手机的操作都能用adb实现。也就是说如果你玩的6,你就是触摸屏完全坏了,显示屏完全坏了,就只是给你一个主板,你照样能完成你要做的动作。当然在一般场景不推荐这样做,效率优先嘛。

接下来的内容是转自github上一位大牛的博客,如有侵权请告知,立即删除;
后期

命令语法

adb 命令的基本语法如下:

adb [-d|-e|-s ]

如果只有一个设备/模拟器连接时,可以省略掉 [-d|-e|-s ] 这一部分,直接使用 adb 。

为命令指定目标设备

如果有多个设备/模拟器连接,则需要为命令指定目标设备。

参数                      | 含义                              
----------------------- | --------------------------------
-d                      | 指定当前唯一通过 USB 连接的 Android 设备为命令目标
-e                      | 指定当前唯一运行的模拟器为命令目标               
-s <serialNumber> | 指定相应 serialNumber 号的设备/模拟器为命令目标

在多个设备/模拟器连接的情况下较常用的是 -s 参数,serialNumber 可以通过 adb devices 命令获取。如:

1
2
3
4
5
6
$ adb devices

List of devices attached
cf264b8f device
emulator-5554 device
10.129.164.6:5555 device

输出里的 cf264b8f、emulator-5554 和 10.129.164.6:5555 即为 serialNumber。

比如这时想指定 cf264b8f 这个设备来运行 adb 命令获取屏幕分辨率:

1
adb -s cf264b8f shell wm size

又如想给 10.129.164.6:5555 这个设备安装应用(这种形式的 serialNumber 格式为 :,一般为无线连接的设备或 Genymotion 等第三方 Android 模拟器):

1
adb -s 10.129.164.6:5555 install test.apk

遇到多设备/模拟器的情况均使用这几个参数为命令指定目标设备,下文中为简化描述,不再重复。

启动/停止

启动 adb server 命令:

1
adb start-server

(一般无需手动执行此命令,在运行 adb 命令时若发现 adb server 没有启动会自动调起。)

停止 adb server 命令:

1
adb kill-server

查看 adb 版本

1
adb version

示例输出

1
2
Android Debug Bridge version 1.0.36
Revision 8f855a3d9b35-android

以 root 权限运行 adbd

adb 的运行原理是 PC 端的 adb server 与手机端的守护进程 adbd 建立连接,然后 PC 端的 adb client 通过 adb server 转发命令,adbd 接收命令后解析运行。

所以如果 adbd 以普通权限执行,有些需要 root 权限才能执行的命令无法直接用 adb xxx 执行。这时可以 adb shell 然后 su 后执行命令,也可以让 adbd 以 root 权限执行,这个就能随意执行高权限命令了。

命令:

1
adb root

正常输出:

1
restarting adbd as root

现在再运行 adb shell,看看命令行提示符是不是变成 # 了?

有些手机 root 后也无法通过 adb root 命令让 adbd 以 root 权限执行,比如三星的部分机型,会提示 adbd cannot run as root in production builds,此时可以先安装 adbd Insecure,然后 adb root 试试。

相应地,如果要恢复 adbd 为非 root 权限的话,可以使用 adb unroot 命令。

指定 adb server 的网络端口

1
adb -P <port> start-server

默认端口为 5037。

设备连接管理


查询已连接设备/模拟器

命令:

1
adb devices

输出示例:

1
2
3
4
List of devices attached
cf264b8f device
emulator-5554 device
10.129.164.6:5555 device

输出格式为 [serialNumber] [state],serialNumber 即我们常说的 SN,state 有如下几种:

offline —— 表示设备未连接成功或无响应。

device —— 设备已连接。注意这个状态并不能标识 Android 系统已经完全启动和可操作,在设备启动过程中设备实例就可连接到 adb,但启动完毕后系统才处于可操作状态。

no device —— 没有设备/模拟器连接。

以上输出显示当前已经连接了三台设备/模拟器,cf264b8f、emulator-5554 和 10.129.164.6:5555 分别是它们的 SN。从 emulator-5554 这个名字可以看出它是一个 Android 模拟器,而 10.129.164.6:5555 这种形为 : 的 serialNumber 一般是无线连接的设备或 Genymotion 等第三方 Android 模拟器。

常见异常输出:

没有设备/模拟器连接成功。

List of devices attached
设备/模拟器未连接到 adb 或无响应。

List of devices attached
cf264b8f offline

USB 连接

通过 USB 连接来正常使用 adb 需要保证几点:

硬件状态正常。

包括 Android 设备处于正常开机状态,USB 连接线和各种接口完好。

Android 设备的开发者选项和 USB 调试模式已开启。

可以到「设置」-「开发者选项」-「Android 调试」查看。

如果在设置里找不到开发者选项,那需要通过一个彩蛋来让它显示出来:在「设置」-「关于手机」连续点击「版本号」7 次。

设备驱动状态正常。

这一点貌似在 Linux 和 Mac OS X 下不用操心,在 Windows 下有可能遇到需要安装驱动的情况,确认这一点可以右键「计算机」-「属性」,到「设备管理器」里查看相关设备上是否有黄色感叹号或问号,如果没有就说明驱动状态已经好了。否则可以下载一个手机助手类程序来安装驱动先。

通过 USB 线连接好电脑和设备后确认状态。

1
adb devices

如果能看到
xxxxxx device
说明连接成功。

无线连接(需要借助 USB 线)

除了可以通过 USB 连接设备与电脑来使用 adb,也可以通过无线连接——虽然连接过程中也有需要使用 USB 的步骤,但是连接成功之后你的设备就可以在一定范围内摆脱 USB 连接线的限制啦!

操作步骤:
将 Android 设备与要运行 adb 的电脑连接到同一个局域网,比如连到同一个 WiFi。
将设备与电脑通过 USB 线连接。
应确保连接成功(可运行 adb devices 看是否能列出该设备)。
让设备在 5555 端口监听 TCP/IP 连接:

1
adb tcpip 5555

断开 USB 连接。
找到设备的 IP 地址。
一般能在「设置」-「关于手机」-「状态信息」-「IP地址」找到,也可以使用下文里 查看设备信息 - IP 地址 一节里的方法用 adb 命令来查看。

通过 IP 地址连接设备。

1
adb connect <device-ip-address>

这里的 就是上一步中找到的设备 IP 地址。

确认连接状态。

1
adb devices

如果能看到

1
<device-ip-address>:5555 device

说明连接成功。
如果连接不了,请确认 Android 设备与电脑是连接到了同一个 WiFi,然后再次执行 adb connect <device-ip-address> 那一步;
如果还是不行的话,通过 adb kill-server 重新启动 adb 然后从头再来一次试试。

断开无线连接
命令:

1
adb disconnect <device-ip-address>

无线连接(无需借助 USB 线)

注:需要 root 权限。

上一节「无线连接(需要借助 USB 线)」是官方文档里介绍的方法,需要借助于 USB 数据线来实现无线连接。
既然我们想要实现无线连接,那能不能所有步骤下来都是无线的呢?答案是能的。
在 Android 设备上安装一个终端模拟器。
已经安装过的设备可以跳过此步。我使用的终端模拟器下载地址是:Terminal Emulator for Android Downloads
将 Android 设备与要运行 adb 的电脑连接到同一个局域网,比如连到同一个 WiFi。
打开 Android 设备上的终端模拟器,在里面依次运行命令:

1
2
su
setprop service.adb.tcp.port 5555

找到 Android 设备的 IP 地址。

一般能在「设置」-「关于手机」-「状态信息」-「IP地址」找到,也可以使用下文里 查看设备信息 - IP 地址 一节里的方法用 adb 命令来查看。

在电脑上通过 adb 和 IP 地址连接 Android 设备。

1
adb connect <device-ip-address>

这里的 就是上一步中找到的设备 IP 地址。

如果能看到 connected to :5555 这样的输出则表示连接成功。

节注一:

有的设备,比如小米 5S + MIUI 8.0 + Android 6.0.1 MXB48T,可能在第 5 步之前需要重启 adbd 服务,在设备的终端模拟器上运行:

1
restart adbd

如果 restart 无效,尝试以下命令:

1
2
stop adbd
start adbd

应用管理

查看应用列表

查看应用列表的基本命令格式是

1
adb shell pm list packages [-f] [-d] [-e] [-s] [-3] [-i] [-u] [--user USER_ID] [FILTER]

即在 adb shell pm list packages 的基础上可以加一些参数进行过滤查看不同的列表,支持的过滤参数如下:

参数             | 显示列表                   
-------------- | -----------------------
无              | 所有应用                   
-f             | 显示应用关联的 apk 文件         
-d             | 只显示 disabled 的应用       
-e             | 只显示 enabled 的应用        
-s             | 只显示系统应用                
-3             | 只显示第三方应用               
-i             | 显示应用的 installer        
-u             | 包含已卸载应用                
&lt;FILTER&gt; | 包名包含 &lt;FILTER&gt; 字符串

所有应用

命令:

1
adb shell pm list packages

输出示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
package:com.android.smoketest
package:com.example.android.livecubes
package:com.android.providers.telephony
package:com.google.android.googlequicksearchbox
package:com.android.providers.calendar
package:com.android.providers.media
package:com.android.protips
package:com.android.documentsui
package:com.android.gallery
package:com.android.externalstorage
...
// other packages here
...

系统应用

命令:

1
adb shell pm list packages -s

第三方应用

命令:

1
adb shell pm list packages -3

包名包含某字符串的应用
比如要查看包名包含字符串 mazhuang 的应用列表,命令:

1
adb shell pm list packages mazhuang

当然也可以使用 grep 来过滤:

1
adb shell pm list packages | grep mazhuang

安装 APK

命令格式:

1
adb install [-lrtsdg] <path_to_apk>

参数:

adb install 后面可以跟一些可选参数来控制安装 APK 的行为,可用参数及含义如下:

参数 | 含义                                                                   
-- | ---------------------------------------------------------------------
-l | 将应用安装到保护目录 /mnt/asec                                                 
-r | 允许覆盖安装                                                               
-t | 允许安装 AndroidManifest.xml 里 application 指定 android:testOnly="true" 的应用
-s | 将应用安装到 sdcard                                                        
-d | 允许降级覆盖安装                                                             
-g | 授予所有运行时权限                                                            

运行命令后如果见到类似如下输出(状态为 Success)代表安装成功:

1
2
3
[100%] /data/local/tmp/1.apk
pkg: /data/local/tmp/1.apk
Success

上面是当前最新版 v1.0.36 的 adb 的输出,会显示 push apk 文件到手机的进度百分比。

使用旧版本 adb 的输出则是这样的:

1
2
3
12040 KB/s (22205609 bytes in 1.801s)
pkg: /data/local/tmp/SogouInput_android_v8.3_sweb.apk
Success

而如果状态为 Failure 则表示安装失败,比如:

1
2
3
[100%] /data/local/tmp/map-20160831.apk
pkg: /data/local/tmp/map-20160831.apk
Failure [INSTALL_FAILED_ALREADY_EXISTS]

常见安装失败输出代码、含义及可能的解决办法如下:

输出                                                                  | 含义                                                     | 解决办法                                                           
------------------------------------------------------------------- | ------------------------------------------------------ | ---------------------------------------------------------------
INSTALL_FAILED_ALREADY_EXISTS                                       | 应用已经存在,或卸载了但没卸载干净                                      | adb install 时使用 -r 参数,或者先 adb uninstall &lt;packagename&gt; 再安装
INSTALL_FAILED_INVALID_APK                                          | 无效的 APK 文件                                             |                                                                
INSTALL_FAILED_INVALID_URI                                          | 无效的 APK 文件名                                            | 确保 APK 文件名里无中文                                                 
INSTALL_FAILED_INSUFFICIENT_STORAGE                                 | 空间不足                                                   | 清理空间                                                           
INSTALL_FAILED_DUPLICATE_PACKAGE                                    | 已经存在同名程序                                               |                                                                
INSTALL_FAILED_NO_SHARED_USER                                       | 请求的共享用户不存在                                             |                                                                
INSTALL_FAILED_UPDATE_INCOMPATIBLE                                  | 以前安装过同名应用,但卸载时数据没有移除;或者已安装该应用,但签名不一致                   | 先 adb uninstall &lt;packagename&gt; 再安装                        
INSTALL_FAILED_SHARED_USER_INCOMPATIBLE                             | 请求的共享用户存在但签名不一致                                        |                                                                
INSTALL_FAILED_MISSING_SHARED_LIBRARY                               | 安装包使用了设备上不可用的共享库                                       |                                                                
INSTALL_FAILED_REPLACE_COULDNT_DELETE                               | 替换时无法删除                                                |                                                                
INSTALL_FAILED_DEXOPT                                               | dex 优化验证失败或空间不足                                        |                                                                
INSTALL_FAILED_OLDER_SDK                                            | 设备系统版本低于应用要求                                           |                                                                
INSTALL_FAILED_CONFLICTING_PROVIDER                                 | 设备里已经存在与应用里同名的 content provider                        |                                                                
INSTALL_FAILED_NEWER_SDK                                            | 设备系统版本高于应用要求                                           |                                                                
INSTALL_FAILED_TEST_ONLY                                            | 应用是 test-only 的,但安装时没有指定 -t 参数                         |                                                                
INSTALL_FAILED_CPU_ABI_INCOMPATIBLE                                 | 包含不兼容设备 CPU 应用程序二进制接口的 native code                     |                                                                
INSTALL_FAILED_MISSING_FEATURE                                      | 应用使用了设备不可用的功能                                          |                                                                
INSTALL_FAILED_CONTAINER_ERROR                                      | 1. sdcard 访问失败;2. 应用签名与 ROM 签名一致,被当作内置应用。              | 1. 确认 sdcard 可用,或者安装到内置存储;2. 打包时不与 ROM 使用相同签名。                 
INSTALL_FAILED_INVALID_INSTALL_LOCATION                             | 1. 不能安装到指定位置;2. 应用签名与 ROM 签名一致,被当作内置应用。                | 1. 切换安装位置,添加或删除 -s 参数;2. 打包时不与 ROM 使用相同签名。                     
INSTALL_FAILED_MEDIA_UNAVAILABLE                                    | 安装位置不可用                                                | 一般为 sdcard,确认 sdcard 可用或安装到内置存储                                
INSTALL_FAILED_VERIFICATION_TIMEOUT                                 | 验证安装包超时                                                |                                                                
INSTALL_FAILED_VERIFICATION_FAILURE                                 | 验证安装包失败                                                |                                                                
INSTALL_FAILED_PACKAGE_CHANGED                                      | 应用与调用程序期望的不一致                                          |                                                                
INSTALL_FAILED_UID_CHANGED                                          | 以前安装过该应用,与本次分配的 UID 不一致                                | 清除以前安装过的残留文件                                                   
INSTALL_FAILED_VERSION_DOWNGRADE                                    | 已经安装了该应用更高版本                                           | 使用 -d 参数                                                       
INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE                           | 已安装 target SDK 支持运行时权限的同名应用,要安装的版本不支持运行时权限             |                                                                
INSTALL_PARSE_FAILED_NOT_APK                                        | 指定路径不是文件,或不是以 .apk 结尾                                  |                                                                
INSTALL_PARSE_FAILED_BAD_MANIFEST                                   | 无法解析的 AndroidManifest.xml 文件                           |                                                                
INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION                           | 解析器遇到异常                                                |                                                                
INSTALL_PARSE_FAILED_NO_CERTIFICATES                                | 安装包没有签名                                                |                                                                
INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES                      | 已安装该应用,且签名与 APK 文件不一致                                  | 先卸载设备上的该应用,再安装                                                 
INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING                           | 解析 APK 文件时遇到 CertificateEncodingException              |                                                                
INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME                               | manifest 文件里没有或者使用了无效的包名                               |                                                                
INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID                             | manifest 文件里指定了无效的共享用户 ID                              |                                                                
INSTALL_PARSE_FAILED_MANIFEST_MALFORMED                             | 解析 manifest 文件时遇到结构性错误                                 |                                                                
INSTALL_PARSE_FAILED_MANIFEST_EMPTY                                 | 在 manifest 文件里找不到找可操作标签(instrumentation 或 application) |                                                                
INSTALL_FAILED_INTERNAL_ERROR                                       | 因系统问题安装失败                                              |                                                                
INSTALL_FAILED_USER_RESTRICTED                                      | 用户被限制安装应用                                              |                                                                
INSTALL_FAILED_DUPLICATE_PERMISSION                                 | 应用尝试定义一个已经存在的权限名称                                      |                                                                
INSTALL_FAILED_NO_MATCHING_ABIS                                     | 应用包含设备的应用程序二进制接口不支持的 native code                       |                                                                
INSTALL_CANCELED_BY_USER                                            | 应用安装需要在设备上确认,但未操作设备或点了取消                               | 在设备上同意安装                                                       
INSTALL_FAILED_ACWF_INCOMPATIBLE                                    | 应用程序与设备不兼容                                             |                                                                
does not contain AndroidManifest.xml                                | 无效的 APK 文件                                             |                                                                
is not a valid zip file                                             | 无效的 APK 文件                                             |                                                                
Offline                                                             | 设备未连接成功                                                | 先将设备与 adb 连接成功                                                 
unauthorized                                                        | 设备未授权允许调试                                              |                                                                
error: device not found                                             | 没有连接成功的设备                                              | 先将设备与 adb 连接成功                                                 
protocol failure                                                    | 设备已断开连接                                                | 先将设备与 adb 连接成功                                                 
Unknown option: -s                                                  | Android 2.2 以下不支持安装到 sdcard                            | 不使用 -s 参数                                                      
No space left on device                                             | 空间不足                                                   | 清理空间                                                           
Permission denied … sdcard …                                        | sdcard 不可用                                             |                                                                
signatures do not match the previously installed version; ignoring! | 已安装该应用且签名不一致                                           | 先卸载设备上的该应用,再安装                                                 

参考:[PackageManager.java]

adb install 内部原理简介

adb install 实际是分三步完成:

  1. push apk 文件到 /data/local/tmp。
  2. 调用 pm install 安装。
  3. 删除 /data/local/tmp 下的对应 apk 文件。 所以,必要的时候也可以根据这个步骤,手动分步执行安装过程。

卸载应用

命令:

1
adb uninstall [-k] <packagename>

<packagename> 表示应用的包名,-k 参数可选,表示卸载应用但保留数据和缓存目录。
命令示例:

1
adb uninstall com.qihoo360.mobilesafe

表示卸载 360 手机卫士。

清除应用数据与缓存

命令:

1
adb shell pm clear <packagename>

<packagename> 表示应用名包,这条命令的效果相当于在设置里的应用信息界面点击了「清除缓存」和「清除数据」。

命令示例:

1
adb shell pm clear com.qihoo360.mobilesafe

表示清除 360 手机卫士的数据和缓存。

查看前台 Activity

命令:

1
adb shell dumpsys activity activities | grep mFocusedActivity

输出示例:

1
mFocusedActivity: ActivityRecord{8079d7e u0 com.cyanogenmod.trebuchet/com.android.launcher3.Launcher t42}

其中的 com.cyanogenmod.trebuchet/com.android.launcher3.Launcher 就是当前处于前台的 Activity。

查看正在运行的 Services

命令:

1
adb shell dumpsys activity services [<packagename>]

<packagename> 参数不是必须的,指定 <packagename> 表示查看与某个包名相关的 Services,不指定表示查看所有 Services。

<packagename> 不一定要给出完整的包名,比如运行 adb shell dumpsys activity services org.mazhuang,那么包名 org.mazhuang.demo1、org.mazhuang.demo2 和 org.mazhuang123 等相关的 Services 都会列出来。

查看应用详细信息

命令:

1
adb shell dumpsys package <packagename>

输出中包含很多信息,包括 Activity Resolver Table、Registered ContentProviders、包名、userId、安装后的文件资源代码等路径、版本信息、权限信息和授予状态、签名版本信息等。

<packagename> 表示应用包名。

输出示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
Activity Resolver Table:
Non-Data Actions:
android.intent.action.MAIN:
5b4cba8 org.mazhuang.guanggoo/.SplashActivity filter 5ec9dcc
Action: "android.intent.action.MAIN"
Category: "android.intent.category.LAUNCHER"
AutoVerify=false

Registered ContentProviders:
org.mazhuang.guanggoo/com.tencent.bugly.beta.utils.BuglyFileProvider:
Provider{7a3c394 org.mazhuang.guanggoo/com.tencent.bugly.beta.utils.BuglyFileProvider}

ContentProvider Authorities:
[org.mazhuang.guanggoo.fileProvider]:
Provider{7a3c394 org.mazhuang.guanggoo/com.tencent.bugly.beta.utils.BuglyFileProvider}
applicationInfo=ApplicationInfo{7754242 org.mazhuang.guanggoo}

Key Set Manager:
[org.mazhuang.guanggoo]
Signing KeySets: 501

Packages:
Package [org.mazhuang.guanggoo] (c1d7f):
userId=10394
pkg=Package{55f714c org.mazhuang.guanggoo}
codePath=/data/app/org.mazhuang.guanggoo-2
resourcePath=/data/app/org.mazhuang.guanggoo-2
legacyNativeLibraryDir=/data/app/org.mazhuang.guanggoo-2/lib
primaryCpuAbi=null
secondaryCpuAbi=null
versionCode=74 minSdk=15 targetSdk=25
versionName=1.1.74
splits=[base]
apkSigningVersion=2
applicationInfo=ApplicationInfo{7754242 org.mazhuang.guanggoo}
flags=[ HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ]
privateFlags=[ RESIZEABLE_ACTIVITIES ]
dataDir=/data/user/0/org.mazhuang.guanggoo
supportsScreens=[small, medium, large, xlarge, resizeable, anyDensity]
timeStamp=2017-10-22 23:50:53
firstInstallTime=2017-10-22 23:50:25
lastUpdateTime=2017-10-22 23:50:55
installerPackageName=com.miui.packageinstaller
signatures=PackageSignatures{af09595 [53c7caa2]}
installPermissionsFixed=true installStatus=1
pkgFlags=[ HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ]
requested permissions:
android.permission.READ_PHONE_STATE
android.permission.INTERNET
android.permission.ACCESS_NETWORK_STATE
android.permission.ACCESS_WIFI_STATE
android.permission.READ_LOGS
android.permission.WRITE_EXTERNAL_STORAGE
android.permission.READ_EXTERNAL_STORAGE
install permissions:
android.permission.INTERNET: granted=true
android.permission.ACCESS_NETWORK_STATE: granted=true
android.permission.ACCESS_WIFI_STATE: granted=true
User 0: ceDataInode=1155675 installed=true hidden=false suspended=false stopped=true notLaunched=false enabled=0
gids=[3003]
runtime permissions:
android.permission.READ_EXTERNAL_STORAGE: granted=true
android.permission.READ_PHONE_STATE: granted=true
android.permission.WRITE_EXTERNAL_STORAGE: granted=true
User 999: ceDataInode=0 installed=false hidden=false suspended=false stopped=true notLaunched=true enabled=0
gids=[3003]
runtime permissions:


Dexopt state:
[org.mazhuang.guanggoo]
Instruction Set: arm64
path: /data/app/org.mazhuang.guanggoo-2/base.apk
status: /data/app/org.mazhuang.guanggoo-2/oat/arm64/base.odex [compilation_filter=speed-profile, status=kOatUpToDa
te]

与应用交互

主要是使用 am <command> 命令,常用的 <command> 如下:

command                               | 用途                            
------------------------------------- | ------------------------------
start [options] &lt;INTENT&gt;        | 启动 &lt;INTENT&gt; 指定的 Activity
startservice [options] &lt;INTENT&gt; | 启动 &lt;INTENT&gt; 指定的 Service
broadcast [options] &lt;INTENT&gt;    | 发送 &lt;INTENT&gt; 指定的广播       
force-stop &lt;packagename&gt;        | 停止 &lt;packagename&gt; 相关的进程  

<INTENT> 参数很灵活,和写 Android 程序时代码里的 Intent 相对应。

用于决定 intent 对象的选项如下:

参数                   | 含义                                                                     
-------------------- | -----------------------------------------------------------------------
-a &lt;ACTION&gt;    | 指定 action,比如 android.intent.action.VIEW                                
-c &lt;CATEGORY&gt;  | 指定 category,比如 android.intent.category.APP_CONTACTS                    
-n &lt;COMPONENT&gt; | 指定完整 component 名,用于明确指定启动哪个 Activity,如 com.example.app/.ExampleActivity

<INTENT> 里还能带数据,就像写代码时的 Bundle 一样:

参数                                                                         | 含义                                               
-------------------------------------------------------------------------- | -------------------------------------------------
--esn &lt;EXTRA_KEY&gt;                                                    | null 值(只有 key 名)                                 
`-e                                                                        | –es &lt;EXTRA_KEY&gt; &lt;EXTRA_STRING_VALUE&gt;`
--ez &lt;EXTRA_KEY&gt; &lt;EXTRA_BOOLEAN_VALUE&gt;                         | boolean 值                                        
--ei &lt;EXTRA_KEY&gt; &lt;EXTRA_INT_VALUE&gt;                             | integer 值                                        
--el &lt;EXTRA_KEY&gt; &lt;EXTRA_LONG_VALUE&gt;                            | long 值                                           
--ef &lt;EXTRA_KEY&gt; &lt;EXTRA_FLOAT_VALUE&gt;                           | float 值                                          
--eu &lt;EXTRA_KEY&gt; &lt;EXTRA_URI_VALUE&gt;                             | URI                                              
--ecn &lt;EXTRA_KEY&gt; &lt;EXTRA_COMPONENT_NAME_VALUE&gt;                 | component name                                   
--eia &lt;EXTRA_KEY&gt; &lt;EXTRA_INT_VALUE&gt;[,&lt;EXTRA_INT_VALUE...]   | integer 数组                                       
--ela &lt;EXTRA_KEY&gt; &lt;EXTRA_LONG_VALUE&gt;[,&lt;EXTRA_LONG_VALUE...] | long 数组                                          

调起 Activity

命令格式:

1
adb shell am start [options] <INTENT>

例如:

1
adb shell am start -n com.tencent.mm/.ui.LauncherUI

表示调起微信主界面。

1
adb shell am start -n org.mazhuang.boottimemeasure/.MainActivity --es "toast" "hello, world"

表示调起 org.mazhuang.boottimemeasure/.MainActivity 并传给它 string 数据键值对 toast - hello, world。

调起 Service

命令格式:

1
adb shell am startservice [options] <INTENT>

例如:

1
adb shell am startservice -n com.tencent.mm/.plugin.accountsync.model.AccountAuthenticatorService

表示调起微信的某 Service。

发送广播

命令格式:

1
adb shell am broadcast [options] <INTENT>

可以向所有组件广播,也可以只向指定组件广播。

例如,向所有组件广播 BOOT_COMPLETED:

1
adb shell am broadcast -a android.intent.action.BOOT_COMPLETED

又例如,只向 org.mazhuang.boottimemeasure/.BootCompletedReceiver 广播 BOOT_COMPLETED:

1
adb shell am broadcast -a android.intent.action.BOOT_COMPLETED -n org.mazhuang.boottimemeasure/.BootCompletedReceiver

这类用法在测试的时候很实用,比如某个广播的场景很难制造,可以考虑通过这种方式来发送广播。
既能发送系统预定义的广播,也能发送自定义广播。如下是部分系统预定义广播及正常触发时机:

action                                          | 触发时机                       
----------------------------------------------- | ---------------------------
android.net.conn.CONNECTIVITY_CHANGE            | 网络连接发生变化                   
android.intent.action.SCREEN_ON                 | 屏幕点亮                       
android.intent.action.SCREEN_OFF                | 屏幕熄灭                       
android.intent.action.BATTERY_LOW               | 电量低,会弹出电量低提示框              
android.intent.action.BATTERY_OKAY              | 电量恢复了                      
android.intent.action.BOOT_COMPLETED            | 设备启动完毕                     
android.intent.action.DEVICE_STORAGE_LOW        | 存储空间过低                     
android.intent.action.DEVICE_STORAGE_OK         | 存储空间恢复                     
android.intent.action.PACKAGE_ADDED             | 安装了新的应用                    
android.net.wifi.STATE_CHANGE                   | WiFi 连接状态发生变化              
android.net.wifi.WIFI_STATE_CHANGED             | WiFi 状态变为启用/关闭/正在启动/正在关闭/未知
android.intent.action.BATTERY_CHANGED           | 电池电量发生变化                   
android.intent.action.INPUT_METHOD_CHANGED      | 系统输入法发生变化                  
android.intent.action.ACTION_POWER_CONNECTED    | 外部电源连接                     
android.intent.action.ACTION_POWER_DISCONNECTED | 外部电源断开连接                   
android.intent.action.DREAMING_STARTED          | 系统开始休眠                     
android.intent.action.DREAMING_STOPPED          | 系统停止休眠                     
android.intent.action.WALLPAPER_CHANGED         | 壁纸发生变化                     
android.intent.action.HEADSET_PLUG              | 插入耳机                       
android.intent.action.MEDIA_UNMOUNTED           | 卸载外部介质                     
android.intent.action.MEDIA_MOUNTED             | 挂载外部介质                     
android.os.action.POWER_SAVE_MODE_CHANGED       | 省电模式开启                     

(以上广播均可使用 adb 触发)

强制停止应用

命令:

1
adb shell am force-stop <packagename>

命令示例:

1
adb shell am force-stop com.qihoo360.mobilesafe

表示停止 360 安全卫士的一切进程与服务。

文件管理

复制设备里的文件到电脑

命令:

1
adb pull <设备里的文件路径> [电脑上的目录]

其中 电脑上的目录 参数可以省略,默认复制到当前目录。

例:

1
adb pull /sdcard/sr.mp4 ~/tmp/

小技巧:设备上的文件路径可能需要 root 权限才能访问,如果你的设备已经 root 过,可以先使用 adb shell 和 su 命令在 adb shell 里获取 root 权限后,先 cp /path/on/device /sdcard/filename 将文件复制到 sdcard,然后 adb pull /sdcard/filename /path/on/pc。

复制电脑里的文件到设备

命令:

1
adb push <电脑上的文件路径> <设备里的目录>

例:

1
adb push ~/sr.mp4 /sdcard/

小技巧:设备上的文件路径普通权限可能无法直接写入,如果你的设备已经 root 过,可以先 adb push /path/on/pc /sdcard/filename,然后 adb shell 和 su 在 adb shell 里获取 root 权限后,cp /sdcard/filename /path/on/device。

模拟按键/输入

在 adb shell 里有个很实用的命令叫 input,通过它可以做一些有趣的事情。
input 命令的完整 help 信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Usage: input [<source>] <command> [<arg>...]

The sources are:
mouse
keyboard
joystick
touchnavigation
touchpad
trackball
stylus
dpad
gesture
touchscreen
gamepad

The commands and default sources are:
text <string> (Default: touchscreen)
keyevent [--longpress] <key code number or name> ... (Default: keyboard)
tap <x> <y> (Default: touchscreen)
swipe <x1> <y1> <x2> <y2> [duration(ms)] (Default: touchscreen)
press (Default: trackball)
roll <dx> <dy> (Default: trackball)

比如模拟点击://在屏幕上点击坐标点x=50 y=250的位置。

adb shell input tap 50 250

比如使用 adb shell input keyevent 命令,不同的 keycode 能实现不同的功能,完整的 keycode 列表详见 KeyEvent,摘引部分我觉得有意思的如下:

keycode | 含义                  
------- | --------------------
3       | HOME 键              
4       | 返回键                 
5       | 打开拨号应用              
6       | 挂断电话                
24      | 增加音量                
25      | 降低音量                
26      | 电源键                 
27      | 拍照(需要在相机应用里)        
64      | 打开浏览器               
82      | 菜单键                 
85      | 播放/暂停               
86      | 停止播放                
87      | 播放下一首               
88      | 播放上一首               
122     | 移动光标到行首或列表顶部        
123     | 移动光标到行末或列表底部        
126     | 恢复播放                
127     | 暂停播放                
164     | 静音                  
176     | 打开系统设置              
187     | 切换应用                
207     | 打开联系人               
208     | 打开日历                
209     | 打开音乐                
210     | 打开计算器               
220     | 降低屏幕亮度              
221     | 提高屏幕亮度              
223     | 系统休眠                
224     | 点亮屏幕                
231     | 打开语音助手              
276     | 如果没有 wakelock 则让系统休眠

下面是 input 命令的一些用法举例。

电源键

1
db shell input keyevent 26

执行效果相当于按电源键。

菜单键

命令:

1
adb shell input keyevent 82

HOME 键

命令:

1
adb shell input keyevent 3

返回键

命令:

1
adb shell input keyevent 4

音量控制

增加音量:

1
adb shell input keyevent 24

降低音量:

1
adb shell input keyevent 25

静音:

1
adb shell input keyevent 164

媒体控制

播放/暂停:

1
adb shell input keyevent 85

停止播放:

1
adb shell input keyevent 86

播放下一首:

1
adb shell input keyevent 87

播放上一首:

1
adb shell input keyevent 88

恢复播放:

1
adb shell input keyevent 126

暂停播放:

1
adb shell input keyevent 127

点亮/熄灭屏幕

可以通过上文讲述过的模拟电源键来切换点亮和熄灭屏幕,但如果明确地想要点亮或者熄灭屏幕,那可以使用如下方法。

点亮屏幕:

1
adb shell input keyevent 224

熄灭屏幕:

1
adb shell input keyevent 223

滑动解锁

如果锁屏没有密码,是通过滑动手势解锁,那么可以通过 input swipe 来解锁。

命令(参数以机型 Nexus 5,向上滑动手势解锁举例):

1
adb shell input swipe 300 1000 300 500

参数 300 1000 300 500 分别表示起始点x坐标 起始点y坐标 结束点x坐标 结束点y坐标。

输入文本

在焦点处于某文本框时,可以通过 input 命令来输入文本。

命令:

1
adb shell input text hello

现在 hello 出现在文本框了。

查看日志

Android 系统的日志分为两部分,底层的 Linux 内核日志输出到 /proc/kmsg,Android 的日志输出到 /dev/log。

Android 日志

命令格式:

1
[adb] logcat [<option>] ... [<filter-spec>] ...

常用用法列举如下:

按级别过滤日志

Android 的日志分为如下几个优先级(priority):

  • V —— Verbose(最低,输出得最多)
  • D —— Debug I —— Info
  • W —— Warning
  • E —— Error
  • F—— Fatal
  • S —— Silent(最高,啥也不输出) 按某级别过滤日志则会将该级别及以上的日志输出。

    比如,命令:

1
adb logcat *:W

会将 Warning、Error、Fatal 和 Silent 日志输出。

(注: 在 macOS 下需要给 _:W 这样以 _ 作为 tag 的参数加双引号,如 adb logcat “_:W”,不然会报错 no matches found: _:W。)

按 tag 和级别过滤日志

<filter-spec> 可以由多个 <tag>[:priority] 组成。

比如,命令:

1
adb logcat ActivityManager:I MyApp:D *:S

表示输出 tag ActivityManager 的 Info 以上级别日志,输出 tag MyApp 的 Debug 以上级别日志,及其它 tag 的 Silent 级别日志(即屏蔽其它 tag 日志)。

日志格式

可以用 adb logcat -v <format> 选项指定日志输出格式。

日志支持按以下几种 <format> :

  • brief

    默认格式。格式为:

1
<priority>/<tag>(<pid>): <message>
示例:
1
D/HeadsetStateMachine( 1785): Disconnected process message: 10, size: 0
  • process

    格式为:

1
<priority>(<pid>) <message>
示例:
1
D( 1785) Disconnected process message: 10, size: 0  (HeadsetStateMachine)
  • tag

    格式为:

1
<priority>/<tag>: <message>
示例:
1
D/HeadsetStateMachine: Disconnected process message: 10, size: 0
  • raw

    格式为:

1
<message>
示例:
1
Disconnected process message: 10, size: 0
  • time

    格式为:

1
<datetime> <priority>/<tag>(<pid>): <message>
示例:
1
08-28 22:39:39.974 D/HeadsetStateMachine( 1785): Disconnected process message: 10, size: 0
  • threadtime

    格式为:

1
<datetime> <pid> <tid> <priority> <tag>: <message>
示例:
1
08-28 22:39:39.974  1785  1832 D HeadsetStateMachine: Disconnected process message: 10, size: 0
  • long

    格式为:

1
[ <datetime> <pid>:<tid> <priority>/<tag> ] <message>
示例:
1
[ 08-28 22:39:39.974  1785: 1832 D/HeadsetStateMachine ] Disconnected process message: 10, size: 0
指定格式可与上面的过滤同时使用。比如:
1
adb logcat -v long ActivityManager:I *:S

清空日志

1
adb logcat -c

内核日志

命令:

1
adb shell dmesg

输出示例:

1
2
3
4
5
6
7
<6>[14201.684016] PM: noirq resume of devices complete after 0.982 msecs
<6>[14201.685525] PM: early resume of devices complete after 0.838 msecs
<6>[14201.753642] PM: resume of devices complete after 68.106 msecs
<4>[14201.755954] Restarting tasks ... done.
<6>[14201.771229] PM: suspend exit 2016-08-28 13:31:32.679217193 UTC
<6>[14201.872373] PM: suspend entry 2016-08-28 13:31:32.780363596 UTC
<6>[14201.872498] PM: Syncing filesystems ... done.

中括号里的 [14201.684016] 代表内核开始启动后的时间,单位为秒。

通过内核日志我们可以做一些事情,比如衡量内核启动时间,在系统启动完毕后的内核日志里找到 Freeing init memory 那一行前面的时间就是。

查看设备信息

型号

命令:

1
adb shell getprop ro.product.model

输出示例:

Nexus 5

电池状况

命令:

1
adb shell dumpsys battery

输入示例:

1
2
3
4
5
6
7
8
9
10
11
12
Current Battery Service state:
AC powered: false
USB powered: true
Wireless powered: false
status: 2
health: 2
present: true
level: 44
scale: 100
voltage: 3872
temperature: 280
technology: Li-poly

其中 scale 代表最大电量,level 代表当前电量。上面的输出表示还剩下 44% 的电量。

屏幕分辨率

命令:

1
adb shell wm size

输出示例:

1
Physical size: 1080x1920

该设备屏幕分辨率为 1080px * 1920px。

如果使用命令修改过,那输出可能是:

1
2
Physical size: 1080x1920
Override size: 480x1024

表明设备的屏幕分辨率原本是 1080px 1920px,当前被修改为 480px 1024px。

屏幕密度

命令:

adb shell wm density
输出示例:

Physical density: 420
该设备屏幕密度为 420dpi。

如果使用命令修改过,那输出可能是:

Physical density: 480
Override density: 160
表明设备的屏幕密度原来是 480dpi,当前被修改为 160dpi。

显示屏参数

命令:

1
adb shell dumpsys window displays

输出示例:

1
2
3
4
WINDOW MANAGER DISPLAY CONTENTS (dumpsys window displays)
Display: mDisplayId=0
init=1080x1920 420dpi cur=1080x1920 app=1080x1794 rng=1080x1017-1810x1731
deferred=false layoutNeeded=false

其中 mDisplayId 为 显示屏编号,init 是初始分辨率和屏幕密度,app 的高度比 init 里的要小,表示屏幕底部有虚拟按键,高度为 1920 - 1794 = 126px 合 42dp。

android_id

命令:

1
adb shell settings get secure android_id

输出示例:

1
51b6be48bac8c569

IMEI

在 Android 4.4 及以下版本可通过如下命令获取 IMEI:

1
adb shell dumpsys iphonesubinfo

输出示例:

1
2
3
4
Phone Subscriber Info:
Phone Type = GSM
Device ID = 860955027785041
其中的 Device ID 就是 IMEI。

而在 Android 5.0 及以上版本里这个命令输出为空,得通过其它方式获取了(需要 root 权限):

1
2
3
adb shell
su
service call iphonesubinfo 1

输出示例:

1
2
3
4
Result: Parcel(
0x00000000: 00000000 0000000f 00360038 00390030 '........8.6.0.9.'
0x00000010: 00350035 00320030 00370037 00350038 '5.5.0.2.7.7.8.5.'
0x00000020: 00340030 00000031 '0.4.1... ')

把里面的有效内容提取出来就是 IMEI 了,比如这里的是 860955027785041。

参考:adb shell dumpsys iphonesubinfo not working since Android 5.0 Lollipop

Android 系统版本

命令:

1
adb shell getprop ro.build.version.release

输出示例:

5.0.2

IP 地址

每次想知道设备的 IP 地址的时候都得「设置」-「关于手机」-「状态信息」-「IP地址」很烦对不对?通过 adb 可以方便地查看。

命令:

1
adb shell ifconfig | grep Mask

输出示例:

1
2
inet addr:10.130.245.230  Mask:255.255.255.252
inet addr:127.0.0.1 Mask:255.0.0.0

那么 10.130.245.230 就是设备 IP 地址。

在有的设备上这个命令没有输出,如果设备连着 WiFi,可以使用如下命令来查看局域网 adb shell ifconfig wlan0例:

1
wlan0: ip 10.129.160.99 mask 255.255.240.0 flags [up broadcast running multicast]

或

1
2
3
4
5
6
7
8
wlan0     Link encap:UNSPEC
inet addr:10.129.168.57 Bcast:10.129.175.255 Mask:255.255.240.0
inet6 addr: fe80::66cc:2eff:fe68:b6b6/64 Scope: Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:496520 errors:0 dropped:0 overruns:0 frame:0
TX packets:68215 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:3000
RX bytes:116266821 TX bytes:8311736

如果以上命令仍然不能得到期望的信息,那可以试试以下命令(部分系统版本里可用):

1
adb shell netcfg

输出示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
wlan0    UP                               10.129.160.99/20  0x00001043 f8:a9:d0:17:42:4d
lo UP 127.0.0.1/8 0x00000049 00:00:00:00:00:00
p2p0 UP 0.0.0.0/0 0x00001003 fa:a9:d0:17:42:4d
sit0 DOWN 0.0.0.0/0 0x00000080 00:00:00:00:00:00
rmnet0 DOWN 0.0.0.0/0 0x00000000 00:00:00:00:00:00
rmnet1 DOWN 0.0.0.0/0 0x00000000 00:00:00:00:00:00
rmnet3 DOWN 0.0.0.0/0 0x00000000 00:00:00:00:00:00
rmnet2 DOWN 0.0.0.0/0 0x00000000 00:00:00:00:00:00
rmnet4 DOWN 0.0.0.0/0 0x00000000 00:00:00:00:00:00
rmnet6 DOWN 0.0.0.0/0 0x00000000 00:00:00:00:00:00
rmnet5 DOWN 0.0.0.0/0 0x00000000 00:00:00:00:00:00
rmnet7 DOWN 0.0.0.0/0 0x00000000 00:00:00:00:00:00
rev_rmnet3 DOWN 0.0.0.0/0 0x00001002 4e:b7:e4:2e:17:58
rev_rmnet2 DOWN 0.0.0.0/0 0x00001002 4e:f0:c8:bf:7a:cf
rev_rmnet4 DOWN 0.0.0.0/0 0x00001002 a6:c0:3b:6b:c4:1f
rev_rmnet6 DOWN 0.0.0.0/0 0x00001002 66:bb:5d:64:2e:e9
rev_rmnet5 DOWN 0.0.0.0/0 0x00001002 0e:1b:eb:b9:23:a0
rev_rmnet7 DOWN 0.0.0.0/0 0x00001002 7a:d9:f6:81:40:5a
rev_rmnet8 DOWN 0.0.0.0/0 0x00001002 4e:e2:a9:bb:d0:1b
rev_rmnet0 DOWN 0.0.0.0/0 0x00001002 fe:65:d0:ca:82:a9
rev_rmnet1 DOWN 0.0.0.0/0 0x00001002 da:d8:e8:4f:2e:fe

可以看到网络连接名称、启用状态、IP 地址和 Mac 地址等信息。

Mac 地址

命令:

1
adb shell cat /sys/class/net/wlan0/address

输出示例:

1
f8:a9:d0:17:42:4d

这查看的是局域网 Mac 地址,移动网络或其它连接的信息可以通过前面的小节「IP 地址」里提到的 adb shell netcfg 命令来查看。

CPU 信息

命令:

1
adb shell cat /proc/cpuinfo

输出示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Processor       : ARMv7 Processor rev 0 (v7l)
processor : 0
BogoMIPS : 38.40

processor : 1
BogoMIPS : 38.40

processor : 2
BogoMIPS : 38.40

processor : 3
BogoMIPS : 38.40

Features : swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt
CPU implementer : 0x51
CPU architecture: 7
CPU variant : 0x2
CPU part : 0x06f
CPU revision : 0

Hardware : Qualcomm MSM 8974 HAMMERHEAD (Flattened Device Tree)
Revision : 000b
Serial : 0000000000000000

这是 Nexus 5 的 CPU 信息,我们从输出里可以看到使用的硬件是 Qualcomm MSM 8974,processor 的编号是 0 到 3,所以它是四核的,采用的架构是 ARMv7 Processor rev 0 (v71)。

内存信息

命令:

1
adb shell cat /proc/meminfo

输出示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
MemTotal:        1027424 kB
MemFree: 486564 kB
Buffers: 15224 kB
Cached: 72464 kB
SwapCached: 24152 kB
Active: 110572 kB
Inactive: 259060 kB
Active(anon): 79176 kB
Inactive(anon): 207736 kB
Active(file): 31396 kB
Inactive(file): 51324 kB
Unevictable: 3948 kB
Mlocked: 0 kB
HighTotal: 409600 kB
HighFree: 132612 kB
LowTotal: 617824 kB
LowFree: 353952 kB
SwapTotal: 262140 kB
SwapFree: 207572 kB
Dirty: 0 kB
Writeback: 0 kB
AnonPages: 265324 kB
Mapped: 47072 kB
Shmem: 1020 kB
Slab: 57372 kB
SReclaimable: 7692 kB
SUnreclaim: 49680 kB
KernelStack: 4512 kB
PageTables: 5912 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 775852 kB
Committed_AS: 13520632 kB
VmallocTotal: 385024 kB
VmallocUsed: 61004 kB
VmallocChunk: 209668 kB

其中,MemTotal 就是设备的总内存,MemFree 是当前空闲内存。

更多硬件与系统属性

设备的更多硬件与系统属性可以通过如下命令查看:

1
adb shell cat /system/build.prop

这会输出很多信息,包括前面几个小节提到的「型号」和「Android 系统版本」等。

输出里还包括一些其它有用的信息,它们也可通过 adb shell getprop <属性名> 命令单独查看,列举一部分属性如下:

属性名                             | 含义                 
------------------------------- | -------------------
ro.build.version.sdk            | SDK 版本             
ro.build.version.release        | Android 系统版本       
ro.build.version.security_patch | Android 安全补丁程序级别   
ro.product.model                | 型号                 
ro.product.brand                | 品牌                 
ro.product.name                 | 设备名                
ro.product.board                | 处理器型号              
ro.product.cpu.abilist          | CPU 支持的 abi 列表[节注一]
persist.sys.isUsbOtgEnabled     | 是否支持 OTG           
dalvik.vm.heapsize              | 每个应用程序的内存上限        
ro.sf.lcd_density               | 屏幕密度               

节注一:

一些小厂定制的 ROM 可能修改过 CPU 支持的 abi 列表的属性名,如果用 ro.product.cpu.abilist 属性名查找不到,可以这样试试:

1
adb shell cat /system/build.prop | grep ro.product.cpu.abi

示例输出:

1
2
ro.product.cpu.abi=armeabi-v7a
ro.product.cpu.abi2=armeabi

修改设置

注: 修改设置之后,运行恢复命令有可能显示仍然不太正常,可以运行 adb reboot 重启设备,或手动重启。

修改设置的原理主要是通过 settings 命令修改 /data/data/com.android.providers.settings/databases/settings.db 里存放的设置值。

分辨率

命令:

1
adb shell wm size 480x1024

表示将分辨率修改为 480px * 1024px。

恢复原分辨率命令:

1
adb shell wm size reset

屏幕密度

命令:

1
adb shell wm density 160

表示将屏幕密度修改为 160dpi。

恢复原屏幕密度命令:

1
adb shell wm density reset

显示区域

命令:

1
adb shell wm overscan 0,0,0,200

四个数字分别表示距离左、上、右、下边缘的留白像素,以上命令表示将屏幕底部 200px 留白。

恢复原显示区域命令:

1
adb shell wm overscan reset

关闭 USB 调试模式

命令:

adb shell settings put global adb_enabled 0
恢复:

用命令恢复不了了,毕竟关闭了 USB 调试 adb 就连接不上 Android 设备了。

去设备上手动恢复吧:「设置」-「开发者选项」-「Android 调试」。

状态栏和导航栏的显示隐藏

本节所说的相关设置对应 Cyanogenmod 里的「扩展桌面」。

命令:

1
adb shell settings put global policy_control <key-values>

<key-values> 可由如下几种键及其对应的值组成,格式为 <key1>=<value1>:<key2>=<value2> 。

key                   | 含义   
--------------------- | -----
immersive.full        | 同时隐藏
immersive.status      | 隐藏状态栏
immersive.navigation  | 隐藏导航栏
immersive.preconfirms | ?    

这些键对应的值可则如下值用逗号组合:

value        | 含义    
------------ | ------
apps         | 所有应用  
*            | 所有界面  
packagename  | 指定应用  
-packagename | 排除指定应用

例如:

1
adb shell settings put global policy_control immersive.full=*

表示设置在所有界面下都同时隐藏状态栏和导航栏。

1
adb shell settings put global policy_control immersive.status=com.package1,com.package2:immersive.navigation=apps,-com.package3

表示设置在包名为 com.package1 和 com.package2 的应用里隐藏状态栏,在除了包名为 com.package3 的所有应用里隐藏导航栏。

恢复正常模式

不想全屏了咋办呢?

1
adb shell settings put global policy_control null

实用功能

屏幕截图

截图保存到电脑:

1
adb exec-out screencap -p > sc.png

如果 adb 版本较老,无法使用 exec-out 命令,这时候建议更新 adb 版本。无法更新的话可以使用以下麻烦点的办法:

先截图保存到设备里:

1
adb shell screencap -p /sdcard/sc.png

然后将 png 文件导出到电脑:

1
adb pull /sdcard/sc.png

可以使用 adb shell screencap -h 查看 screencap 命令的帮助信息,下面是两个有意义的参数及含义:

参数            | 含义                   
------------- | ---------------------
-p            | 指定保存文件为 png 格式       
-d display-id | 指定截图的显示屏编号(有多显示屏的情况下)

实测如果指定文件名以 .png 结尾时可以省略 -p 参数;否则需要使用 -p 参数。如果不指定文件名,截图文件的内容将直接输出到 stdout。

另外一种一行命令截图并保存到电脑的方法:

Linux 和 Windows

1
adb shell screencap -p | sed "s/\r$//" > sc.png

Mac OS X

1
adb shell screencap -p | gsed "s/\r$//" > sc.png

这个方法需要用到 gnu sed 命令,在 Linux 下直接就有,在 Windows 下 Git 安装目录的 bin 文件夹下也有。如果确实找不到该命令,可以下载 sed for Windows 并将 sed.exe 所在文件夹添加到 PATH 环境变量里。

而在 Mac 下使用系统自带的 sed 命令会报错:

1
sed: RE error: illegal byte sequence

需要安装 gnu-sed,然后使用 gsed 命令:

1
brew install gnu-sed

录制屏幕

录制屏幕以 mp4 格式保存到 /sdcard:

1
adb shell screenrecord /sdcard/filename.mp4

需要停止时按 Ctrl-C,默认录制时间和最长录制时间都是 180 秒。

如果需要导出到电脑:

1
adb pull /sdcard/filename.mp4

可以使用 adb shell screenrecord –help 查看 screenrecord 命令的帮助信息,下面是常见参数及含义:

参数                 | 含义                         
------------------ | ---------------------------
–size WIDTHxHEIGHT | 视频的尺寸,比如 1280x720,默认是屏幕分辨率。
–bit-rate RATE     | 视频的比特率,默认是 4Mbps。          
–time-limit TIME   | 录制时长,单位秒。                  
–verbose           | 输出更多信息。                    

重新挂载 system 分区为可写

注:需要 root 权限。

/system 分区默认挂载为只读,但有些操作比如给 Android 系统添加命令、删除自带应用等需要对 /system 进行写操作,所以需要重新挂载它为可读写。

步骤:

进入 shell 并切换到 root 用户权限。

命令:

1
2
adb shell
su

查看当前分区挂载情况。

命令:

1
mount

输出示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
rootfs / rootfs ro,relatime 0 0
tmpfs /dev tmpfs rw,seclabel,nosuid,relatime,mode=755 0 0
devpts /dev/pts devpts rw,seclabel,relatime,mode=600 0 0
proc /proc proc rw,relatime 0 0
sysfs /sys sysfs rw,seclabel,relatime 0 0
selinuxfs /sys/fs/selinux selinuxfs rw,relatime 0 0
debugfs /sys/kernel/debug debugfs rw,relatime 0 0
none /var tmpfs rw,seclabel,relatime,mode=770,gid=1000 0 0
none /acct cgroup rw,relatime,cpuacct 0 0
none /sys/fs/cgroup tmpfs rw,seclabel,relatime,mode=750,gid=1000 0 0
none /sys/fs/cgroup/memory cgroup rw,relatime,memory 0 0
tmpfs /mnt/asec tmpfs rw,seclabel,relatime,mode=755,gid=1000 0 0
tmpfs /mnt/obb tmpfs rw,seclabel,relatime,mode=755,gid=1000 0 0
none /dev/memcg cgroup rw,relatime,memory 0 0
none /dev/cpuctl cgroup rw,relatime,cpu 0 0
none /sys/fs/cgroup tmpfs rw,seclabel,relatime,mode=750,gid=1000 0 0
none /sys/fs/cgroup/memory cgroup rw,relatime,memory 0 0
none /sys/fs/cgroup/freezer cgroup rw,relatime,freezer 0 0
/dev/block/platform/msm_sdcc.1/by-name/system /system ext4 ro,seclabel,relatime,data=ordered 0 0
/dev/block/platform/msm_sdcc.1/by-name/userdata /data ext4 rw,seclabel,nosuid,nodev,relatime,noauto_da_alloc,data=ordered 0 0
/dev/block/platform/msm_sdcc.1/by-name/cache /cache ext4 rw,seclabel,nosuid,nodev,relatime,data=ordered 0 0
/dev/block/platform/msm_sdcc.1/by-name/persist /persist ext4 rw,seclabel,nosuid,nodev,relatime,data=ordered 0 0
/dev/block/platform/msm_sdcc.1/by-name/modem /firmware vfat ro,context=u:object_r:firmware_file:s0,relatime,uid=1000,gid=1000,fmask=0337,dmask=0227,codepage=cp437,iocharset=iso8859-1,shortname=lower,errors=remount-ro 0 0
/dev/fuse /mnt/shell/emulated fuse rw,nosuid,nodev,relatime,user_id=1023,group_id=1023,default_permissions,allow_other 0 0
/dev/fuse /mnt/shell/emulated/0 fuse rw,nosuid,nodev,relatime,user_id=1023,group_id=1023,default_permissions,allow_other 0 0

找到其中我们关注的带 /system 的那一行:

1
/dev/block/platform/msm_sdcc.1/by-name/system /system ext4 ro,seclabel,relatime,data=ordered 0 0

重新挂载。

命令:

1
mount -o remount,rw -t yaffs2 /dev/block/platform/msm_sdcc.1/by-name/system /system

这里的 /dev/block/platform/msm_sdcc.1/by-name/system 就是我们从上一步的输出里得到的文件路径。

如果输出没有提示错误的话,操作就成功了,可以对 /system 下的文件为所欲为了。

查看连接过的 WiFi 密码

注:需要 root 权限。

命令:

1
2
3
adb shell
su
cat /data/misc/wifi/*.conf

输出示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
network={
ssid="TP-LINK_9DFC"
scan_ssid=1
psk="123456789"
key_mgmt=WPA-PSK
group=CCMP TKIP
auth_alg=OPEN
sim_num=1
priority=13893
}

network={
ssid="TP-LINK_F11E"
psk="987654321"
key_mgmt=WPA-PSK
sim_num=1
priority=17293
}

ssid 即为我们在 WLAN 设置里看到的名称,psk 为密码,key_mgmt 为安全加密方式。

设置系统日期和时间

注:需要 root 权限。

命令:

1
2
3
adb shell
su
date -s 20160823.131500

表示将系统日期和时间更改为 2016 年 08 月 23 日 13 点 15 分 00 秒。

重启手机

命令:

1
adb reboot

检测设备是否已 root

命令:

1
2
adb shell
su

此时命令行提示符是 $ 则表示没有 root 权限,是 # 则表示已 root。

使用 Monkey 进行压力测试

Monkey 可以生成伪随机用户事件来模拟单击、触摸、手势等操作,可以对正在开发中的程序进行随机压力测试。

简单用法:

1
adb shell monkey -p <packagename> -v 500

表示向 <packagename> 指定的应用程序发送 500 个伪随机事件。

Monkey 的详细用法参考 官方文档。

开启/关闭 WiFi

注:需要 root 权限。

有时需要控制设备的 WiFi 状态,可以用以下指令完成。

开启 WiFi:

1
2
adb root
adb shell svc wifi enable

关闭 WiFi:

1
2
adb root
adb shell svc wifi disable

若执行成功,输出为空;若未取得 root 权限执行此命令,将执行失败,输出 Killed。

刷机相关命令

重启到 Recovery 模式

命令:

1
adb reboot recovery

从 Recovery 重启到 Android

命令:

1
adb reboot

重启到 Fastboot 模式

命令:

1
adb reboot bootloader

通过 sideload 更新系统

如果我们下载了 Android 设备对应的系统更新包到电脑上,那么也可以通过 adb 来完成更新。

以 Recovery 模式下更新为例:

重启到 Recovery 模式。

命令:

1
adb reboot recovery

在设备的 Recovery 界面上操作进入 Apply update-Apply from ADB。

注:不同的 Recovery 菜单可能与此有差异,有的是一级菜单就有 Apply update from ADB。

通过 adb 上传和更新系统。

命令:

1
adb sideload <path-to-update.zip>

更多 adb shell 命令

Android 系统是基于 Linux 内核的,所以 Linux 里的很多命令在 Android 里也有相同或类似的实现,在 adb shell 里可以调用。本文档前面的部分内容已经用到了 adb shell 命令。

查看进程

1
adb shell ps

例:

1
2
3
4
5
6
7
8
USER     PID   PPID  VSIZE  RSS     WCHAN    PC        NAME
root 1 0 8904 788 ffffffff 00000000 S /init
root 2 0 0 0 ffffffff 00000000 S kthreadd
...
u0_a71 7779 5926 1538748 48896 ffffffff 00000000 S com.sohu.inputmethod.sogou:classic
u0_a58 7963 5926 1561916 59568 ffffffff 00000000 S org.mazhuang.boottimemeasure
...
shell 8750 217 10640 740 00000000 b6f28340 R ps

各列含义:

列名   | 含义    
---- | ------
USER | 所属用户  
PID  | 进程 ID
PPID | 父进程 ID
NAME | 进程名   

查看实时资源占用情况

1
adb shell top

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
User 0%, System 6%, IOW 0%, IRQ 0%
User 3 + Nice 0 + Sys 21 + Idle 280 + IOW 0 + IRQ 0 + SIRQ 3 = 307

PID PR CPU% S #THR VSS RSS PCY UID Name
8763 0 3% R 1 10640K 1064K fg shell top
131 0 3% S 1 0K 0K fg root dhd_dpc
6144 0 0% S 115 1682004K 115916K fg system system_server
132 0 0% S 1 0K 0K fg root dhd_rxf
1731 0 0% S 6 20288K 788K fg root /system/bin/mpdecision
217 0 0% S 6 18008K 356K fg shell /sbin/adbd
...
7779 2 0% S 19 1538748K 48896K bg u0_a71 com.sohu.inputmethod.sogou:classic
7963 0 0% S 18 1561916K 59568K fg u0_a58 org.mazhuang.boottimemeasure
...

各列含义:

列名   | 含义                                    
---- | --------------------------------------
PID  | 进程 ID                                 
PR   | 优先级                                   
CPU% | 当前瞬间占用 CPU 百分比                        
S    | 进程状态(R=运行,S=睡眠,T=跟踪/停止,Z=僵尸进程)        
#THR | 线程数                                   
VSS  | Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)   
RSS  | Resident Set Size 实际使用物理内存(包含共享库占用的内存)
PCY  | 调度策略优先级,SP_BACKGROUND/SPFOREGROUND    
UID  | 进程所有者的用户 ID                           
NAME | 进程名                                   

top 命令还支持一些命令行参数,详细用法如下:

1
2
3
4
5
6
7
Usage: top [ -m max_procs ] [ -n iterations ] [ -d delay ] [ -s sort_column ] [ -t ] [ -h ]
-m num 最多显示多少个进程
-n num 刷新多少次后退出
-d num 刷新时间间隔(单位秒,默认值 5)
-s col 按某列排序(可用 col 值:cpu, vss, rss, thr)
-t 显示线程信息
-h 显示帮助文档

查看进程 UID

有两种方案:

1
adb shell dumpsys package <packagename> | grep userId=

如:

1
2
$ adb shell dumpsys package org.mazhuang.guanggoo | grep userId=
userId=10394

通过 ps 命令找到对应进程的 pid 之后 adb shell cat /proc/<pid>/status | grep Uid

如:

1
2
3
4
5
6
$ adb shell
gemini:/ $ ps | grep org.mazhuang.guanggoo
u0_a394 28635 770 1795812 78736 SyS_epoll_ 0000000000 S org.mazhuang.guanggoo
gemini:/ $ cat /proc/28635/status | grep Uid
Uid: 10394 10394 10394 10394
gemini:/ $

其它

如下是其它常用命令的简单描述,前文已经专门讲过的命令不再额外说明:

命令    | 功能            
----- | --------------
cat   | 显示文件内容        
cd    | 切换目录          
chmod | 改变文件的存取模式/访问权限
df    | 查看磁盘空间使用情况    
grep  | 过滤输出          
kill  | 杀死指定 PID 的进程  
ls    | 列举目录内容        
mount | 挂载目录的查看和管理    
mv    | 移动或重命名文件      
ps    | 查看正在运行的进程     
rm    | 删除文件          
top   | 查看进程的资源占用情况   

常见问题

启动 adb server 失败

出错提示

1
error: protocol fault (couldn't read status): No error

可能原因

adb server 进程想使用的 5037 端口被占用。

解决方案

找到占用 5037 端口的进程,然后终止它。以 Windows 下为例:

1
2
3
4
5
netstat -ano | findstr LISTENING

...
TCP 0.0.0.0:5037 0.0.0.0:0 LISTENING 1548
...

这里 1548 即为进程 ID,用命令结束该进程:

1
taskkill /PID 1548

然后再启动 adb 就没问题了。

dex学习和探究

发表于 2018-12-25 | 分类于 Android
字数统计: 1,769 | 阅读时长 ≈ 7

dex学习和探究

一. Dex是什么

Dex (Dalvik Executable Format)是java中存储类定义和相关联数据的一种数据格式,主要用于保存类定义及其关联的辅助数据

.dex结构分成三部分:
文件头:表明了是dex文件,已经文件的大小等等数据
索引头:如下图所示
数据区:数据区,就像是jvm中的堆保存方法+变量。

下面是dex的数据格式:
数据格式

odex简介
odex是OptimizedDEX的缩写,表示对dex的优化。系统预置应用经常会采用odex的方式加载,一般存放在/data/dalvik-cache目录下。

由于Android程序的apk文件为zip压缩包格式,Dalvik虚拟机每次加载它们时需要从apk中读取classes.dex文件,这样会耗费很多cpu时间,而采用odex方式优化的dex文件,已经包含了加载dex必须的依赖库文件列表,Dalvik虚拟机只需检测并加载所需的依赖库即可执行相应的dex文件,这大大缩短了读取dex文件所需的时间。

不过,这个优化过程会根据不同设备上Dalvik虚拟机的版本、Framework库的不同等因素而不同。在一台设备上被优化过的ODEX文件,拷贝到另一台设备上不一定能够运行。

二. Dex的加载流程

   Android提供了一个专门验证与优化dex文件的工具dexopt。其源码位于Android系统源码的dalvik/dexopt目录下,Dalvik虚拟机在加载一个dex文件时,通过指定的验证与优化选项来调用dexopt进行相应的验证与优化操作。具体加载流程参考

加载验证

   简要总结下整个的加载流程,首先是对文件名的修正,后缀名置为”.dex”作为输出文件,然后生个一个DexPathList对象函数直接返回一个DexPathList对象,

   在DexPathList的构造函数中调用makeDexElements()函数,在makeDexElement()函数中调用loadDexFile()开始对.dex或者是.jar .zip .apk文件进行处理,跟入loadDexFile()函数中,会发现里面做的工作很简单,调用optimizedPathFor()函数对optimizedDiretcory路径进行修正。

加载验证
   之后才真正通过DexFile.loadDex()开始加载文件中的数据,其中的加载也只是返回一个DexFile对象。

   在DexFile类的构造函数中,重点便放在了其调用的openDexFile()函数,在openDexFile()中调用了openDexFileNative()真正进入native层,在openDexFileNative()的真正实现中,对于后缀名为.dex的文件或者其他文件(.jar .apk .zip)分开进行处理:

   .dex文件调用dvmRawDexFileOpen();
   其他文件调用dvmJarFileOpen()。

加载dex

   在dvmRawDexFileOpen()函数中,检验dex文件的标志,检验odex文件的缓存名称,之后将dex文件拷贝到odex文件中,并对odex进行优化。

   调用dvmDexFileOpenFromFd()对优化后的odex文件进行映射,通过mprotect置为”只读”属性并将映射的内存结构保存在DvmDex*结构中。

   dvmJarFileOpen()先对文件进行映射,结构保存在ZipArchive中,然后再尝试以文件名作为dex文件名来“打开”文件,如果失败,则调用dexZipFindEntry在ZipArchive的名称hash表中找名为”class.dex”的文件,然后创建odex文件,下面就和dvmRawDexFileOpen()一样了,就是对dex文件进行优化和映射。

最关键的loadDexFile和optimizedPathFor方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private static DexFile loadDexFile(File file, File optimizedDirectory)
throws IOException {
if (optimizedDirectory == null) {
return new DexFile(file);
} else {
String optimizedPath = optimizedPathFor(file, optimizedDirectory);
return DexFile.loadDex(file.getPath(), optimizedPath, 0);
}
}


//生成odex的目录
private static String optimizedPathFor(File path,
File optimizedDirectory) {
String fileName = path.getName();
if (!fileName.endsWith(DEX_SUFFIX)) {
int lastDot = fileName.lastIndexOf(".");
if (lastDot < 0) {
fileName += DEX_SUFFIX;
} else {
StringBuilder sb = new StringBuilder(lastDot + 4);
sb.append(fileName, 0, lastDot);
sb.append(DEX_SUFFIX);
fileName = sb.toString();
}
}
File result = new File(optimizedDirectory, fileName);
return result.getPath();
}

   optimizedPathFor 方法:就是将dex 优化形成optimizedDex文件。然后
通过DexFile的静态方法将.dex文件。或者apk,.jar加载成DexFile文件。DexFile 是C语言中的结构体,里面保存的都是一些指针。经过不断的校检,最后通过dvm里面的c代码处理,得到DexFile。

三. Dex的应用场景
   结合dex的支持动态加载的特性,dex通常被用于以下场合:

  1. MultiDex分包: 拆分dex,可以解决方法数超过65535导致的编译问题。也支持分包后再Application的onCreate()方法中调用 MultiDex.install(this)来加载不同的dex包。

    可以用于解决2.3上无法安装问题
    INSTALL_FAILED_DEXOPT

    可以解决方法数过多问题:
    Conversion to Dalvik format failed:Unable to execute dex: method ID not in [0, 0xffff]: 65536

  2. 应用热更新: 参考腾讯的tinker方案。tinker的核心思想是利用DexDiff算法对比差异生成Patch补丁包,全量合成新Dex进行整体替换。这里面就涉及到dex的具体加载过程中,很重要的一个特点,在DexPathList加载类时,会遍历dexElements数组,ClassLoader在加载到正确的类之后就会停止加载此类,基于这个原理,可以用来做应用的局部热更新。tinker是在这个基础上增加了差量包的定义,引入了自定义的 dexdiff算法。

  3. 插件化:动态加载模块,参见于广点通sdk方案。将包含dex的jar放到asset目录下,动态加载函数类,而不用安装独立的应用。该方案可以用于广告sdk,应用插件等开发,采用动态加载dex,通常会将dex文件下载放置于data目录而不是sd卡目录(sd卡不安全),并且采用加密算法进行dex的内部加密,支持动态下载更新dex文件,提升了代码破解难度,增加了apk的运维可能。

    广点通动态加载

  1. 应用加固和脱壳: 这方面没有过多涉猎,有空可以学习下mergerly整理的加固脱壳方法

四. Dex方案的优劣

  可能带来的问题:

  1. MultiDex分包可能增加进入耗时,dex文件不能过大,否则容易在进入加载dex时出现anr或异常。

  2. 可能引入一些加载异常:

    ClassNotFoundException: Didn’t find class “com.squareup.picasso.Picasso” on path: DexPathList[[zip file “/data/app/com.groupon.dexlazyload-1/base.apk”]

  3. 增加了代码注入和应用劫持的风险

  优点:

  1. 支持热更新和热修复,避免整体apk更新带来的流量问题和迭代问题。

  2. 支持dex加固,一定程度上增加了apk被反编译破解带来的风险。

  3. 允许动态加载指定的dex内的函数。

五. tqy dex加载分析
   tqy sdk的初始化中会调用initSdk,

  校验文件:
tqy加载

  加载dex
tqy加载

具体的dex文件被放置到: /data/user/0/com.tqy.sdk.ad/ 目录上:
tqy加载

在通过dex文件的md5校验,之后加载类实例,覆盖原有的register方法,jar包中清晰可见在原有的基础上增加了adServiceLogic的实现。

tqy加载

也意味着原有的方法只提供了基础的方法名称,在dex中才是方法的真正实现。

UE16禁止显示切换

发表于 2018-12-14
字数统计: 94 | 阅读时长 ≈ 1

1) 按 ctrl+N ,新建一个文件
2) 输入一个空格
3)按ctrl + H,切换到十六进制查看模式,这是显示的是:
0000000h:20
4) 修改第一个 20为34,然后接着输入 44 45 44 45 44 46 34
5) 按ctrl + H,切换到普通查看模式,就可以看到字符显示
34 44 45 44 45 44 46 34 对应 4DEDEDF4

通过ttl 判断经过多少路由

发表于 2018-08-24
字数统计: 788 | 阅读时长 ≈ 3

当我们访问某个网络时,通过tracert命令,就能知道本机与目标主机之间经过多台主机,

即经过多少路由。


eg:

C:\Users\acer>tracert www.datang.com

通过最多 30 个跃点跟踪
到 www.datang.com [219.141.223.5] 的路由:

1 1 ms <1 毫秒 <1 毫秒 221.206.0.220
2 2 ms 1 ms 1 ms 221.206.1.41
3 9 ms 6 ms 8 ms 60.14.244.45
4 25 ms 27 ms 27 ms 219.158.19.41
5 29 ms 27 ms 25 ms 219.158.101.114
6 请求超时。
7
请求超时。
8 * 请求超时。
9 133 ms 130 ms 129 ms bj141-133-246.bjtelecom.net [219.141.133.246]
10 131 ms 130 ms 131 ms bj141-162-194.bjtelecom.net [219.141.162.194]
11 141 ms 145 ms 131 ms 74.23.142.219.broad.bj.bj.dynamic.163data.com.cn
[219.142.23.74]
12 140 ms 133 ms 132 ms bj141-223-5.bjtelecom.net [219.141.223.5]

跟踪完成。


我们访问www.datang.com主机(当然这里可以是IP地址),经过12次路由之后,就到达目标主机了。

我们可以用ping命令来进行验证!eg:

C:\Users\acer>ping www.datang.com -i 11

正在 Ping www.datang.com [219.141.223.5] 具有 32 字节的数据:
来自 219.142.23.74 的回复: TTL 传输中过期。
来自 219.142.23.74 的回复: TTL 传输中过期。
来自 219.142.23.74 的回复: TTL 传输中过期。
来自 219.142.23.74 的回复: TTL 传输中过期。

219.141.223.5 的 Ping 统计信息:
数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),

C:\Users\acer>ping www.datang.com -i 12

正在 Ping www.datang.com [219.141.223.5] 具有 32 字节的数据:
来自 219.141.223.5 的回复: 字节=32 时间=131ms TTL=118
来自 219.141.223.5 的回复: 字节=32 时间=132ms TTL=118
来自 219.141.223.5 的回复: 字节=32 时间=131ms TTL=118
来自 219.141.223.5 的回复: 字节=32 时间=131ms TTL=118

219.141.223.5 的 Ping 统计信息:
数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
最短 = 131ms,最长 = 132ms,平均 = 131ms

注:

ping 命令的-i参数用于设置发出数据包的“生存时间”。
若设为n,则该数据包经过n个结点即停止传送。若两个地址之间实际节点数大于n(即包未送至目的地),
则会显示【TTL expired in transit】(传送时过期)。tracert命令即此原理,分别发送i=1,2,3……,30(默认30)的包实现。

在上面,当我们设置“生存时间”为 11 时,数据包无法正常到达目标主机,

而设置为 12 时,就能正常到达,

通过 tracert 命令,可以看到到达目的主机时,最后一个IP为 219.141.223.5,

而通过ping 命令,也会看到是由219.141.223.5返回的数据,说明对应上了,即目的主机IP即为219.141.223.5,

这里也刚好验证了,数据包要经过12次路由才能到达目标主机!

那么默认主机的TTL值应该为118+12=130 (这个值可以由公司来改,不一定就是默认值)

查看应用packagename和activity的几种方法

发表于 2018-08-20
字数统计: 347 | 阅读时长 ≈ 1

下载android SDK并配好环境变量时,可以通过多种方式获取当前activity名称:

1
aapt dump badging [yourapp.apk]

如果只是想查看手机上应用的packageName,则输入命令:

1
adb shell pm list packages

列出如下信息:

package:android

package:cn.ninegame.gamemanager

package:com.alipay.android.app

如果想知道应用对应的apk文件在手机上的安装位置则可以在上面的命令后加-f参数

adb shell pm list packages -f

查看Android应用包名、Activity的几个方法

一、有源码情况

直接打开AndroidManifest.xml文件,找到包含Android.intent.action.MAIN和android.intent.category.LAUNCHER对应的activity。

如下图中第三行package为com.cola.ui,第七行主Activity为com.cola.ui.ColaBox(.ColaBox为Activity简写方式)。

[html] view plain copy
<?xml version=”1.0” encoding=”utf-8”?>


















二、只有Apk的情况

(1)aapt

使用命令行aapt dump xmltree ColaBox.apk AndroidManifest.xml

(2)使用apktool

使用反编译工具apktool,反编译后打开AndroidManifest.xml文件,查找方式同“有源码情况”

(3)aapt

感谢“天地会珠海分舵”地址:http://blog.csdn.net/zhubaitian/article/details/38926679

三、没有apk,应用已经安装到手机或虚拟机中

1.logcat

.清除logcat内容,使用命令adb logcat -c

.启动logcat,使用命令adb logcat ActivityManager:I *:s

.启动要查看的程序,

2.dumpsys

(1)启动要查看的程序;

(2)命令行输入:adb shell dumpsys window w |findstr \/ |findstr name=

TEST_ONLY 导致apk无法安装问题解决

发表于 2018-08-16
字数统计: 664 | 阅读时长 ≈ 3

升级到7.0,android studio升级到3.0后经常出下如下异常,导致apk无法正常安装,放到手机sd卡中也无法完成安装,会提示安装失败:

adb命令行

通过如下命令可以完成安装:

1
adb install -t *.APK

但是导致问题的原因在哪里呢?

网上有说是manifest中配置了testOnly标签的:

网络上问题原因

然而实际上,真正的原因是什么呢?
看看官方的说明吧:
https://developer.android.com/studio/run/index.html

Note: The Run button builds an APK with testOnly=”true”, which means the APK can only be installed via adb (which Android Studio uses). If you want a debuggable APK that people can install without adb, select your debug variant and click Build > Build APK(s).

大致意思就是说,只要在AndroidStudio3.0内,点击运行Run,跑出来的apk,一定是TEST_ONLY的,如果需要可以安装的apk,直接使用build apks或者assembleRelease即可。


以下转载自 沈文昌同学的blog

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
之前有个问题涉及到生成apk无法安装。当时搜索到的结果是和gradle版本有关,实际上不是这样的。

今天测试妹妹要一个apk,我直接run了一个给她,发现问题又出现了。

我首先怀疑是不是Instant Run的配置,但这个配置我在settings里是关掉的,而Run功能(绿三角)的Configurations里,Android App->app中,右侧最下方Before launch,配置了一个Instant App Provision。我将这个配置直接删掉,问题依旧。

看来不是Instant Run的锅。

然后,开始怀疑是否是gradle版本不可为最新,尝试下载了升级AS3.0默认配的4.0-milestone-1,以及4.1,以及4.1-milestone-1,花费了很多时间,最后发现也不是gradle版本问题。

最终,在一个曾经看过的帖子上,发现了另一个回答。

https://stackoverflow.com/questions/25274296/adb-install-fails-with-install-failed-test-only

往下翻,有个只有个位数支持率的回答,里面提到官方网站在beta7版本已经有了提示: https://developer.android.com/studio/run/index.html

这个网站我是打不开的……好吧,这位好心人直接把主要内容粘贴了:

Note: The Run button builds an APK with testOnly="true", which means the APK can only be installed via adb (which Android Studio uses). If you want a debuggable APK that people can install without adb, select your debug variant and click Build > Build APK(s).

也就是说,只要在AndroidStudio3.0内,点击绿三角运行Run,跑出来的apk,一定是TEST_ONLY的。

问题解决,以后给测试妹妹出apk,不能直接用Run出来的了,必须跑一下Build Apks。

markdown 语法

发表于 2018-07-26 | 分类于 markdown
字数统计: 5,779 | 阅读时长 ≈ 23

Markdown 文件

Markdown

NOTE: This is Traditional Chinese Edition Document of
Markdown Syntax. If you are seeking for English Edition
Document. Please refer to Markdown: Syntax.

Markdown: Syntax

  • 概述
    • 哲学
    • 行内 HTML
    • 特殊字符自动转换
  • 区块元素
    • 段落和换行
    • 标题
    • 区块引言
    • 清单
    • 程序代码区块
    • 分隔线
  • 区段元素
    • 连结
    • 强调
    • 程序代码
    • 图片
  • 其它
    • 跳脱字符
    • 自动连结
  • 感谢

注意:这份文件是用 Markdown 写的,你可以看看它的原始档 。


概述

哲学

Markdown 的目标是实现「易读易写」。

不过最需要强调的便是它的可读性。一份使用 Markdown 格式撰写的文件应该可以直接以纯文本发布,并且看起来不会像是由许多卷标或是格式指令所构成。Markdown 语法受到一些既有 text-to-HTML 格式的影响,包括 Setext、atx、Textile、reStructuredText、Grutatext 和 EtText,然而最大灵感来源其实是纯文本的电子邮件格式。

因此 Markdown 的语法全由标点符号所组成,并经过严谨慎选,是为了让它们看起来就像所要表达的意思。像是在文字两旁加上星号,看起来就像*强调*。Markdown 的列表看起来,嗯,就是清单。假如你有使用过电子邮件,区块引言看起来就真的像是引用一段文字。

行内 HTML

Markdown 的语法有个主要的目的:用来作为一种网络内容的写作用语言。

Markdown 不是要来取代 HTML,甚至也没有要和它相似,它的语法种类不多,只和 HTML 的一部分有关系,重点不是要创造一种更容易写作 HTML 文件的语法,我认为 HTML 已经很容易写了,Markdown 的重点在于,它能让文件更容易阅读、编写。HTML 是一种发布的格式,Markdown 是一种编写的格式,因此,Markdown 的格式语法只涵盖纯文本可以涵盖的范围。

不在 Markdown 涵盖范围之外的标签,都可以直接在文件里面用 HTML 撰写。不需要额外标注这是 HTML 或是 Markdown;只要直接加标签就可以了。

只有区块元素──比如 <div>、<table>、<pre>、<p> 等标签,必须在前后加上空行,以利与内容区隔。而且这些(元素)的开始与结尾标签,不可以用 tab 或是空白来缩排。Markdown 的产生器有智能型判断,可以避免在区块标签前后加上没有必要的 <p> 标签。

举例来说,在 Markdown 文件里加上一段 HTML 表格:

This is a regular paragraph.

<table>
    <tr>
        <td>Foo</td>
    </tr>
</table>

This is another regular paragraph.

请注意,Markdown 语法在 HTML 区块卷标中将不会被进行处理。例如,你无法在 HTML 区块内使用 Markdown 形式的*强调*。

HTML 的区段标签如 <span>、<cite>、<del> 则不受限制,可以在 Markdown 的段落、清单或是标题里任意使用。依照个人习惯,甚至可以不用Markdown 格式,而采用 HTML 标签来格式化。举例说明:如果比较喜欢 HTML 的 <a> 或 <img> 标签,可以直接使用这些标签,而不用 Markdown 提供的链接或是影像标示语法。

HTML 区段卷标和区块卷标不同,在区段标签的范围内, Markdown 的语法是有效的。

特殊字符自动转换

在 HTML 文件中,有两个字符需要特殊处理: < 和 & 。 < 符号用于起始卷标,& 符号则用于标记 HTML 实体,如果你只是想要使用这些符号,你必须要使用实体的形式,像是 &lt; 和 &amp;。

& 符号其实很容易让写作网络文件的人感到困扰,如果你要打「AT&T」 ,你必须要写成「AT&amp;T」 ,还得转换网址内的 & 符号,如果你要链接到:

http://images.google.com/images?num=30&q=larry+bird

你必须要把网址转成:

http://images.google.com/images?num=30&amp;q=larry+bird

才能放到链接卷标的 href 属性里。不用说也知道这很容易忘记,这也可能是 HTML 标准检查所检查到的错误中,数量最多的。

Markdown 允许你直接使用这些符号,但是你要小心跳脱字符的使用,如果你是在HTML 实体中使用 & 符号的话,它不会被转换,而在其它情形下,它则会被转换成 &amp;。所以你如果要在文件中插入一个著作权的符号,你可以这样写:

&copy;

Markdown 将不会对这段文字做修改,但是如果你这样写:

AT&T

Markdown 就会将它转为:

AT&amp;T

类似的状况也会发生在 < 符号上,因为 Markdown 支持 行内 HTML ,如果你是使用 < 符号作为 HTML 卷标使用,那 Markdown 也不会对它做任何转换,但是如果你是写:

4 < 5

Markdown 将会把它转换为:

4 &lt; 5

不过需要注意的是,code 范围内,不论是行内还是区块, < 和 & 两个符号都一定会被转换成 HTML 实体,这项特性让你可以很容易地用 Markdown 写 HTML code (和 HTML 相对而言, HTML 语法中,你要把所有的 < 和 & 都转换为 HTML 实体,才能在 HTML 文件里面写出 HTML code。)


区块元素

段落和换行

一个段落是由一个以上相连接的行句组成,而一个以上的空行则会切分出不同的段落(空行的定义是显示上看起来像是空行,便会被视为空行。比方说,若某一行只包含空白和 tab,则该行也会被视为空行),一般的段落不需要用空白或断行缩排。

「一个以上相连接的行句组成」这句话其实暗示了 Markdown 允许段落内的强迫断行,这个特性和其他大部分的 text-to-HTML 格式不一样(包括 MovableType 的「Convert Line Breaks」选项),其它的格式会把每个断行都转成 <br /> 标签。

如果你真的想要插入 <br /> 标签的话,在行尾加上两个以上的空白,然后按 enter。

是的,这确实需要花比较多功夫来插入 <br /> ,但是「每个换行都转换为 <br />」的方法在 Markdown 中并不适合, Markdown 中 email 式的 区块引言 和多段落的 清单 在使用换行来排版的时候,不但更好用,还更好阅读。

标题

Markdown 支持两种标题的语法,Setext 和 atx 形式。

Setext 形式是用底线的形式,利用 = (最高阶标题)和 - (第二阶标题),例如:

This is an H1
=============

This is an H2
-------------

任何数量的 = 和 - 都可以有效果。

Atx 形式则是在行首插入 1 到 6 个 # ,对应到标题 1 到 6 阶,例如:

# This is an H1

## This is an H2

###### This is an H6

你可以选择性地「关闭」atx 样式的标题,这纯粹只是美观用的,若是觉得这样看起来比较舒适,你就可以在行尾加上 #,而行尾的 # 数量也不用和开头一样(行首的井字数量决定标题的阶数):

# This is an H1 #

## This is an H2 ##

### This is an H3 ######

Blockquotes

Markdown 使用 email 形式的区块引言,如果你很熟悉如何在 email 信件中引言,你就知道怎么在 Markdown 文件中建立一个区块引言,那会看起来像是你强迫断行,然后在每行的最前面加上 > :

> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
> consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
> Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
>
> Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
> id sem consectetuer libero luctus adipiscing.

Markdown 也允许你只在整个段落的第一行最前面加上 > :

> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.

> Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
id sem consectetuer libero luctus adipiscing.

区块引言可以有阶层(例如:引言内的引言),只要根据层数加上不同数量的 > :

> This is the first level of quoting.
>
> > This is nested blockquote.
>
> Back to the first level.

引言的区块内也可以使用其他的 Markdown 语法,包括标题、列表、程序代码区块等:

> ## This is a header.
>
> 1.   This is the first list item.
> 2.   This is the second list item.
>
> Here's some example code:
>
>     return shell_exec("echo $input | $markdown_script");

任何标准的文本编辑器都能简单地建立 email 样式的引言,例如 BBEdit ,你可以选取文字后然后从选单中选择增加引言阶层。

清单

Markdown 支持有序列表和无序列表。

无序清单使用星号、加号或是减号作为列表标记:

*   Red
*   Green
*   Blue

等同于:

+   Red
+   Green
+   Blue

也等同于:

-   Red
-   Green
-   Blue

有序列表则使用数字接着一个英文句点:

1.  Bird
2.  McHale
3.  Parish

很重要的一点是,你在列表标记上使用的数字并不会影响输出的 HTML 结果,上面的列表所产生的 HTML 标记为:

<ol>
<li>Bird</li>
<li>McHale</li>
<li>Parish</li>
</ol>

如果你的列表标记写成:

1.  Bird
1.  McHale
1.  Parish

或甚至是:

3. Bird
1. McHale
8. Parish

你都会得到完全相同的 HTML 输出。重点在于,你可以让 Markdown 文件的列表数字和输出的结果相同,或是你懒一点,你可以完全不用在意数字的正确性。

如果你使用懒惰的写法,建议第一个项目最好还是从 1. 开始,因为 Markdown 未来可能会支持有序列表的 start 属性。

列表项目标记通常是放在最左边,但是其实也可以缩排,最多三个空白,项目标记后面则一定要接着至少一个空白或 tab。

要让清单看起来更漂亮,你可以把内容用固定的缩排整理好:

*   Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
    Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
    viverra nec, fringilla in, laoreet vitae, risus.
*   Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
    Suspendisse id sem consectetuer libero luctus adipiscing.

但是如果你很懒,那也不一定需要:

*   Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
viverra nec, fringilla in, laoreet vitae, risus.
*   Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
Suspendisse id sem consectetuer libero luctus adipiscing.

如果列表项目间用空行分开, Markdown 会把项目的内容在输出时用 <p>
标签包起来,举例来说:

*   Bird
*   Magic

会被转换为:

<ul>
<li>Bird</li>
<li>Magic</li>
</ul>

但是这个:

*   Bird

*   Magic

会被转换为:

<ul>
<li><p>Bird</p></li>
<li><p>Magic</p></li>
</ul>

列表项目可以包含多个段落,每个项目下的段落都必须缩排 4 个空白或是一个 tab :

1.  This is a list item with two paragraphs. Lorem ipsum dolor
    sit amet, consectetuer adipiscing elit. Aliquam hendrerit
    mi posuere lectus.

    Vestibulum enim wisi, viverra nec, fringilla in, laoreet
    vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
    sit amet velit.

2.  Suspendisse id sem consectetuer libero luctus adipiscing.

如果你每行都有缩排,看起来会看好很多,当然,再次地,如果你很懒惰,Markdown 也允许:

*   This is a list item with two paragraphs.

    This is the second paragraph in the list item. You're
only required to indent the first line. Lorem ipsum dolor
sit amet, consectetuer adipiscing elit.

*   Another item in the same list.

如果要在列表项目内放进引言,那 > 就需要缩排:

*   A list item with a blockquote:

    > This is a blockquote
    > inside a list item.

如果要放程序代码区块的话,该区块就需要缩排两次,也就是 8 个空白或是两个 tab:

*   A list item with a code block:

        <code goes here>

当然,项目列表很可能会不小心产生,像是下面这样的写法:

1986. What a great season.

换句话说,也就是在行首出现数字-句点-空白,要避免这样的状况,你可以在句点前面加上反斜杠。

1986\. What a great season.

程序代码区块

和程序相关的写作或是卷标语言原始码通常会有已经排版好的程序代码区块,通常这些区块我们并不希望它以一般段落文件的方式去排版,而是照原来的样子显示,Markdown 会用 <pre> 和 <code> 标签来把程序代码区块包起来。

要在 Markdown 中建立程序代码区块很简单,只要简单地缩排 4 个空白或是 1 个 tab 就可以,例如,下面的输入:

This is a normal paragraph:

    This is a code block.

Markdown 会转换成:

<p>This is a normal paragraph:</p>

<pre><code>This is a code block.
</code></pre>

这个每行一阶的缩排(4 个空白或是 1 个 tab),都会被移除,例如:

Here is an example of AppleScript:

    tell application "Foo"
        beep
    end tell

会被转换为:

<p>Here is an example of AppleScript:</p>

<pre><code>tell application "Foo"
    beep
end tell
</code></pre>

一个程序代码区块会一直持续到没有缩排的那一行(或是文件结尾)。

在程序代码区块里面, & 、 < 和 > 会自动转成 HTML 实体,这样的方式让你非常容易使用 Markdown 插入范例用的 HTML 原始码,只需要复制贴上,再加上缩排就可以了,剩下的 Markdown 都会帮你处理,例如:

<div class="footer">
    &copy; 2004 Foo Corporation
</div>

会被转换为:

<pre><code>&lt;div class="footer"&gt;
    &amp;copy; 2004 Foo Corporation
&lt;/div&gt;
</code></pre>

程序代码区块中,一般的 Markdown 语法不会被转换,像是星号便只是星号,这表示你可以很容易地以 Markdown 语法撰写 Markdown 语法相关的文件。

分隔线

你可以在一行中用三个或以上的星号、减号、底线来建立一个分隔线,行内不能有其他东西。你也可以在星号中间插入空白。下面每种写法都可以建立分隔线:

* * *

***

*****

- - -

---------------------------------------

区段元素

连结

Markdown 支持两种形式的链接语法: 行内和参考两种形式。

不管是哪一种,链接的文字都是用 [方括号] 来标记。

要建立一个行内形式的连结,只要在方块括号后面马上接着括号并插入网址连结即可,如果你还想要加上链接的 title 文字,只要在网址后面,用双引号把 title 文字包起来即可,例如:

This is [an example](http://example.com/ "Title") inline link.

[This link](http://example.net/) has no title attribute.

会产生:

<p>This is <a href="http://example.com/" title="Title">
an example</a> inline link.</p>

<p><a href="http://example.net/">This link</a> has no
title attribute.</p>

如果你是要链接到同样主机的资源,你可以使用相对路径:

See my [About](/about/) page for details.   

参考形式的连结使用另外一个方括号接在链接文字的括号后面,而在第二个方括号里面要填入用以辨识链接的标签:

This is [an example][id] reference-style link.

你也可以选择性地在两个方括号中间加上空白:

This is [an example] [id] reference-style link.

接着,在文件的任意处,你可以把这个标签的链接内容定义出来:

[id]: http://example.com/  "Optional Title Here"

连结定义的形式为:

  • 方括号,里面输入链接的辨识用标签
  • 接着一个冒号
  • 接着一个以上的空白或 tab
  • 接着连结的网址
  • 选择性地接着 title 内容,可以用单引号、双引号或是括号包着

下面这三种连结的定义都是相同:

[foo]: http://example.com/  "Optional Title Here"
[foo]: http://example.com/  'Optional Title Here'
[foo]: http://example.com/  (Optional Title Here)

请注意:有一个已知的问题是 Markdown.pl 1.0.1 会忽略单引号包起来的连结 title。

连结网址也可以用方括号包起来:

[id]: <http://example.com/>  "Optional Title Here"

你也可以把 title 属性放到下一行,也可以加一些缩排,网址太长的话,这样会比较好看:

[id]: http://example.com/longish/path/to/resource/here
    "Optional Title Here"

网址定义只有在产生连结的时候用到,并不会直接出现在文件之中。

链接辨识卷标可以有字母、数字、空白和标点符号,但是并不区分大小写,因此下面两个连结是一样的:

[link text][a]
[link text][A]

默认的链接卷标功能让你可以省略指定链接标签,这种情形下,链接卷标和链接文字会视为相同,要用默认链接卷标只要在链接文字后面加上一个空的方括号,如果你要让 “Google” 连结到 google.com,你可以简化成:

[Google][]

然后定义连结内容:

[Google]: http://google.com/

由于链接文字可能包含空白,所以这种简化的标签内也可以包含多个文字:

Visit [Daring Fireball][] for more information.

然后接着定义连结:

[Daring Fireball]: http://daringfireball.net/

连结的定义可以放在文件中的任何一个地方,我比较偏好直接放在连结出现段落的后面,你也可以把它放在文件最后面,就像是批注一样。

下面是一个参考式链接的范例:

I get 10 times more traffic from [Google] [1] than from
[Yahoo] [2] or [MSN] [3].

  [1]: http://google.com/        "Google"
  [2]: http://search.yahoo.com/  "Yahoo Search"
  [3]: http://search.msn.com/    "MSN Search"

如果改成用连结名称的方式写:

I get 10 times more traffic from [Google][] than from
[Yahoo][] or [MSN][].

  [google]: http://google.com/        "Google"
  [yahoo]:  http://search.yahoo.com/  "Yahoo Search"
  [msn]:    http://search.msn.com/    "MSN Search"

上面两种写法都会产生下面的 HTML。

<p>I get 10 times more traffic from <a href="http://google.com/"
title="Google">Google</a> than from
<a href="http://search.yahoo.com/" title="Yahoo Search">Yahoo</a>
or <a href="http://search.msn.com/" title="MSN Search">MSN</a>.</p>

下面是用行内形式写的同样一段内容的 Markdown 文件,提供作为比较之用:

I get 10 times more traffic from [Google](http://google.com/ "Google")
than from [Yahoo](http://search.yahoo.com/ "Yahoo Search") or
[MSN](http://search.msn.com/ "MSN Search").

参考式的连结其实重点不在于它比较好写,而是它比较好读,比较一下上面的范例,使用参考式的文章本身只有 81 个字符,但是用行内形式的连结却会增加到 176 个字符,如果是用纯 HTML 格式来写,会有 234 个字符,在 HTML 格式中,卷标比文字还要多。

使用 Markdown 的参考式连结,可以让文件更像是浏览器最后产生的结果,让你可以把一些标记相关的信息移到段落文字之外,你就可以增加连结而不让文章的阅读感觉被打断。

强调

Markdown 使用星号(*)和底线(_)作为标记强调字词的符号,被 * 或 _ 包围的字词会被转成用 <em> 标签包围,用两个 * 或 _ 包起来的话,则会被转成 <strong>,例如:

*single asterisks*

_single underscores_

**double asterisks**

__double underscores__

会转成:

<em>single asterisks</em>

<em>single underscores</em>

<strong>double asterisks</strong>

<strong>double underscores</strong>

你可以随便用你喜欢的样式,唯一的限制是,你用什么符号开启卷标,就要用什么符号结束。

强调也可以直接插在文字中间:

un*frigging*believable

但是如果你的 * 和 _ 两边都有空白的话,它们就只会被当成普通的符号。

如果要在文字前后直接插入普通的星号或底线,你可以用反斜杠:

\*this text is surrounded by literal asterisks\*

程序代码

如果要标记一小段行内程序代码,你可以用反引号把它包起来(`),例如:

Use the `printf()` function.

会产生:

<p>Use the <code>printf()</code> function.</p>

如果要在程序代码区段内插入反引号,你可以用多个反引号来开启和结束程序代码区段:

``There is a literal backtick (`) here.``

这段语法会产生:

<p><code>There is a literal backtick (`) here.</code></p>

程序代码区段的起始和结束端都可以放入一个空白,起始端后面一个,结束端前面一个,这样你就可以在区段的一开始就插入反引号:

A single backtick in a code span: `` ` ``

A backtick-delimited string in a code span: `` `foo` ``

会产生:

<p>A single backtick in a code span: <code>`</code></p>

<p>A backtick-delimited string in a code span: <code>`foo`</code></p>

在程序代码区段内,& 和方括号都会被转成 HTML 实体,这样会比较容易插入 HTML 原始码,Markdown 会把下面这段:

Please don't use any `<blink>` tags.

转为:

<p>Please don't use any <code>&lt;blink&gt;</code> tags.</p>

你也可以这样写:

`&#8212;` is the decimal-encoded equivalent of `&mdash;`.

以产生:

<p><code>&amp;#8212;</code> is the decimal-encoded
equivalent of <code>&amp;mdash;</code>.</p>

图片

很明显地,要在纯文本应用中设计一个 「自然」的语法来插入图片是有一定难度的。

Markdown 使用一种和链接很相似的语法来标记图片,同样也允许两种样式: 行内和参考。

行内图片的语法看起来像是:

![Alt text](/path/to/img.jpg)

![Alt text](/path/to/img.jpg "Optional title")

详细叙述如下:

  • 一个惊叹号 !
  • 接着一对方括号,里面放上图片的替换文字
  • 接着一对普通括号,里面放上图片的网址,最后还可以用引号包住并加上
    选择性的 ‘title’ 文字。

参考式的图片语法则长得像这样:

![Alt text][id]

「id」是图片参考的名称,图片参考的定义方式则和连结参考一样:

[id]: url/to/image  "Optional title attribute"

到目前为止, Markdown 还没有办法指定图片的宽高,如果你需要的话,你可以使用普通的 <img> 标签。


其它

自动连结

Markdown 支持比较简短的自动连结形式来处理网址和电子邮件信箱,只要是用方括号包起来, Markdown 就会自动把它转成链接,链接的文字就和链接位置一样,例如:

<http://example.com/>

Markdown 会转为:

<a href="http://example.com/">http://example.com/</a>

自动的邮件连结也很类似,只是 Markdown 会先做一个编码转换的过程,把文字字符转成 16 进位码的 HTML 实体,这样的格式可以混淆一些不好的信箱地址收集机器人,例如:

<address@example.com>

Markdown 会转成:

<a href="&#x6D;&#x61;i&#x6C;&#x74;&#x6F;:&#x61;&#x64;&#x64;&#x72;&#x65;
&#115;&#115;&#64;&#101;&#120;&#x61;&#109;&#x70;&#x6C;e&#x2E;&#99;&#111;
&#109;">&#x61;&#x64;&#x64;&#x72;&#x65;&#115;&#115;&#64;&#101;&#120;&#x61;
&#109;&#x70;&#x6C;e&#x2E;&#99;&#111;&#109;</a>

在浏览器里面,这段字符串会变成一个可以点击的「address@example.com」连结。

(这种作法虽然可以混淆不少的机器人,但并无法全部挡下来,不过这样也比什么都不做好些。无论如何,公开你的信箱终究会引来广告信件的。)

跳脱字符

Markdown 可以利用反斜杠来插入一些在语法中有其它意义的符号,例如:如果你想要用星号加在文字旁边的方式来做出强调效果(但不用 <em> 标签),你可以在星号的前面加上反斜杠:

\*literal asterisks\*

Markdown 支持在下面这些符号前面加上反斜杠来帮助插入普通的符号:

\   反斜杠
`   反引号
*   星号
_   底线
{}  大括号
[]  方括号
()  括号
#   井字号
+    加号
-    减号
.   英文句点
!   惊叹号

感谢

感谢 leafy7382 协助翻译,hlb、Randylien 帮忙润稿,ethantw 的汉字标准格式・CSS Reset, WM 回报文字错误。

ListView footer dividers

发表于 2018-07-26 | 分类于 Android
字数统计: 189 | 阅读时长 ≈ 1

ListView的顶部和底部怎么增加分隔线


首先,我们先来了解ListView的两个属性以及它们的API解析

android:headerDividersEnabled: When set to false, the ListView will not draw the divider after each header view. [boolean]

android:footerDividersEnabled: When set to false, the ListView will not draw the divider before each footer view. [boolean]

可见,这里两个属性都只对HeaderView和FooterView有效,如果你没有在ListView加入HeaderView或者FooterView,无论设置什么值,都是没有作用的。

footer

那么,如何为listview设置顶部和底部的间隔线呢,是不是要为listiew的每一个item在布局中设置分割线呢?

这里列举一种比较简单的办法:

在xml中:

1
2
android:headerDividersEnabled="true"
android:footerDividersEnabled="true"

在代码中:

1
2
listView.addHeaderView(new View(this));
listView.addFooterView(new View(this));

Android 进程状态整理和常驻service实践

发表于 2018-07-21
字数统计: 1,029 | 阅读时长 ≈ 4

lowmeorykiller中进程的分类以及各类进程的adj值

在Android的lowmemroykiller机制中,会对于所有进程进行分类,对于每一类别的进程会有其oom_adj值的取值范围,oom_adj值越高则代表进程越不重要,在系统执行低杀操作时,会从oom_adj值越高的开始杀。系统lowmemeorykiller机制下对于进程的级别的以变量的形式定义在framework/base/core/java/com/android/server/am/ProcessList.java类中,可总结成下表:

进程图

再补充介绍一下:

1.AMS角度对于进程的分级

上表带分级只是从lowmemroykiller角度来分的,时用于lowmemeorykiller执行杀进程操作,但是从android的系统管理角度看,即是从AMS执行相关逻辑时,又有一套自己的分级机制,当然这两套机制也有着很多互通的点。AMS角度的级别划分以变量的形式定义在framework/base/core/java/android/app/ActivityManager.java类中,以PROCESS_STATE开头的变量。

2.没有stopService其内含activity的后台进程

这类进程从lowmemorykiller角度是划分为cached,因为如果这类进程往往占有较大的内存,这类含有activity的后台进程往往占有较大内存,所以即使这类进程包含了Service,lowmemorykiller的机制也会更加倾向于优先杀死这类进程。

但是一般启动了服务的进程往往是希望服务在后台能够执行某些任务,这样看是不希望这些服务因为进程被杀而过早的被终止的,那如何调和这种矛盾呢?正确的做法是,对于期望较长时间留在后台的服务,应该将服务运行在单独的进程里,即是UI进程与Servie进程分离,这样期望长时间留在后台的Serivce会存在与一个被lmk分类为Service 进程的服务而获得较小的Adj值,而占有大量内存的UI进程则会分类为Cached进程,能够在需要的时候更快地被回收。

还有一点,这类进程虽然被lmk划分为cached进程,但是从ams角度是被划分为PROCESS_STATE_SERVICE这个类别的,即视为服务进程,在ams相关流程中也是以服务进程来执行相关逻辑的,此外在使用dumpsys meminfo查看所有进程时,这类进程也是被列在B service这个类别的。

3.A-Service与B-Service的划分

所有启动了服务的进程,且该服务所在的进程没有显示过UI,且该服务未执行startForeground(执行后会变为perveptible服务)动作,那该进程则为A-Service与B-Service中的一种。然后根据这类服务进程所处于Lru进程表中的位置,前1/3点服务为A-Service,其余的则为B-Service。

4.perceptible的标准

perceptible名为可感知的进程,但并不是说能够感知到进程就一定表示该进程属于perveptible进程,比如播放音乐的进程活着状态栏上有通知的进程,虽然能够感知到进程的存在,但是不代表进程一定时perceptible类别的进程。决定该进程是否属于perceptible进程并未进程的可感知性,而是该进程的服务是否执行了startForeground动作。

使用命令可以打出进程所属service的状态信息:

以今日头条为例:

1
dumpsys activity services com.ss.android.article.news

服务图

1
2
3
adb shell
ps | grep com.ss.android.article.news
cat proc/9839/oom_adj

进程adj图

对于常驻Service,通常使用绑定托盘的方式使之成为前台service,这样的adj值会从5或者8变为2,一般是不会清理掉的。

常驻Service方案除了基本的sticky之外,可以参考github上优雅的常驻service.原理是通过设置相同的notification ID 设置前台service,stopInnerService后,原service会被认定为前台service,并且不会弹出通知栏。

注意: 该方案在Android 7.1 及以上版本会弹出通知栏,这个其实是goolge里的漏洞,在Android 7.1上已经修复。

各平台离线日志开关

发表于 2018-07-21
字数统计: 3,851 | 阅读时长 ≈ 14

以下是部分手机抓取日志方法

360手机


1) 在桌面的【其他应用】文件夹找到【用户反馈】,或通过搜索找到【用户反馈】
2 ) 点击系统问题反馈
3 ) 等复现问题,让问题出现2~3次
4 ) 问题复现之后,直接到文件管理、本地存储,找到根目录下的“log或者mtklog”文件,将文件压缩后发送给工作人员

MIUI:


1) 在拨号键输入 *#*#284#*#* 来抓日志(不包括前后空格),过程可能要 1 分钟左右,成功后通知栏会提示;
2)等复现问题
3) 日志存放在 MIUI/debug_log 目录里,一个类似 bugreport-0227-14564.zip 的压缩包,将其发给我们

魅族


1) 去魅族应用市场下载一个叫“Log Report\ 的软件
2) 然后用它抓取LOG
3) 等问题复现后将log发给我们

OPPO


1)在拨号键盘输入*#800# ,在弹出的界面选择第一项“Oppo Logkit”,再选取第一项“常规log日志信息抓取”,点击最下面一排按钮最左右的“保存log”,返回到上一界面
2)选择QXDM log抓取,选择打开Device_Log,运行一段时间后选择关闭Device_Log
3)问题出现后在进入“常规log日志信息抓取”点击暂停log,再点击转存log,
4)进入文件管理把文件夹OPPOlog里面最新的log和文件夹diag_logs里面的最新文件压缩后发送给相关的工作人员

vivo


在拨号键盘输入 *#*#112#*#* 进入bbklog
我们机型抓log的方法根据处理器的平台不同方法也不同,分高通处理器平台以及MTK处理器平台
mtk开启LOG方法:拔号界面输入*#*#112#*#* —>MTKLogger,点最下方红色小键头,然后点返回键到主菜单,问题出现后,输入:*#*#112#*#*—>MTKLogger,点红色的方框停止,log路径:文件管理根目录下的mtklog文件夹,将整个文件夹压缩后发给相关的工作人员
高通平台抓log的步骤:在拨号盘输入*#*#112#*#*即可进入Bbklog界面,ADBLog开关是总开关,开启后退出该界面。然后运行问题出现的软件或者打开问题出现的路径,log路径:文件管理根目录下的bbk_log文件夹,将整个文件夹压缩后发给相关的工作人员

Nubia UI:


拨号输入 *#62564#(旧版 ROM 为 *#983564#)进入自带的抓日志工具 Woodpecker

  1. 软件报错、闪退相关问题:
    a) 开启 MobileLog、NetworkLog 两项,并点击最下方开始箭头按钮
    b) 退出工具界面,操作手机,使问题复现后,记下时间点,再次进入工具点击停止按钮
    c) 在 内置存储/nubialog 文件夹里,将刚生成的 log 文件夹压缩,提交给工作人员并告知时间点
  2. 功耗问题:
    a) 保证手机没有连接电脑、关闭 ModemLog 的情况下,重启手机,然后再开启 MobileLog 、NetworkLog、功耗Log 三项,记下当前电量,并点击最下方开始箭头按钮
    b) 正常使用手机,观察耗电情况(至少30分钟);若发现异常,记录时间点、当前电量、使用了什么软件,做了什么操作,并进入 Log 工具点击停止按钮
    c) 在 内置存储/nubialog 文件夹里,将刚生成的 log 文件夹压缩,提交给工作人员并告知时间点

三星


  1. 在系统拨号盘输入 *#9900# 进入sys Dump菜单
  2. Delete dumpstate/logcat
  3. Set Silent log : On to default
  4. 测试复现问题
  5. *#9900#进入sys Dump菜单
  6. Press Run dumpstate/logcat/modem log
  7. press copy to SDcard(include CP Ramdump)
  8. 然后将内存根目录下的log文件夹压缩后发给工作人员

华为


  1. 拨号键输入*#*#2846579#*#*,进入工程菜单
  2. 选择后台设置–Log设置->Log开关
  3. 点击页面最下方中间的播放按钮开始抓log,之后按钮会变成暂停图标,
    点击后可以停止抓log,一旦选择了start,每次开机,log都会自动打开
  4. 复现问题后,需要先选择stop,这样log能完全保存下来;
  5. 打开LOG,正常使用手机,观察问题是否出现;
  6. 如果问题出现,请记录一下时间点,最好能截个问题现象的截图;在问题出现后过10分钟再停止LOG,确保整个过程的LOG都抓到;
    (如果长时间不复现,考虑到LOG可能占满手机空间,造成LOG覆盖的情况,请定时清除一下LOG)
  7. 清除LOG方法为:首先将LOG停止;然后点击又下角的*桶图标,进入后选择“clear all”,然后选择确定,则手机存储中的LOG将会被全部清除;之后再打开LOG继续进行抓取;
  8. 停止LOG后将LOG导出压缩发送;
    Log的保存路径是:手机内部存储或者SD卡存储根目录下会有一个“mtklog”的文件夹;请将此文件夹从手机中拖出后压缩发送给相关工作人员

联想(含Moto z/z play/M 国行)


####3333#模式(MTK平台)

  1. 打开拨号界面,输入####3333#,打开工程模式
  2. 点击SystemLog后点击右上角设置按钮,进入设置界面,将三个log模式均点上,其他的暂时不要点
    3.点击返回键,后点击屏幕下方中间的三角按钮,显示等待开始录取log,你会看到时间在动,这个时候退出,再通知栏也可以看到录取模式再运行
    4.按照之前出现问题的操作方式再次进行操作,直至问题再次出现(即问题复现)
    5.点击通知栏的log录取按钮,再次进入log录取界面,点击下方中间的方块按钮停止log录制
    6.找到mtklog这个文件夹,将里面的内容拷出后压缩打包发给工程师
    7.反馈完毕后为了避免log占用大量内存,按照步骤1进入工程模式后点击右下角的清除按钮(垃圾桶),然后选择需要清除的log,一般选择全部清除即可,然后返回退出即可。

####8888#模式(高通平台)

  1. 在拨号界面输入####8888#,进入工程模式
  2. 点选所有日志(或者根据问题类型选择)
    3.点home键,然后按照之前的操作步骤复现问题
    4.问题复现后再次按照步骤1进入工程模式点击保存日志
    5.找到log这个文件夹,把里面的log拷出后打包发给工程师就ok了

金立


高通平台

  1. 开机后,进入拨号界面,在拨号界面输入指令*#446633#
  2. 进入金立的log日志选项界面了,看到英文很头疼?放心,很简单的!下面我来介绍。
  3. 默认情况下,最上面两项Mobile Log和Kernel Log是打开的。遇到报错或者为了验证特殊问题时,如果是日常使用场景,不涉及到使用网络(WiFi、数据网络),打开它们就够了。
  4. 如果你反馈的问题与网络连接有关,那么一定要打开后面两项Modem Log和Net Log。(否则你做了无用功,工程师收到无效log也会感到心好累的= =!)modem log开启后会占用一定内存,这一点请注意,传完log记得及时清理,腾出存储空间。
  5. 接着看下面的Log Path选项,这个是log文件存储的位置选项,默认的就是Internal Storage(内置存储),所以这一项我们不用管。
  6. 大家注意到了吗,有一行小字,Start Automatically when Reboot,勾选这个选项后便于开机直接启动log记录器,而不需要每次都进拨号盘输入命令启动,尤其是验证死机重启的问题时它很有用。
  7. 最后也是最重要的一点,打开上面的所有选项后,一定记得点击绿色的“START”按钮。这时就会变成下面的界面了,log记录就开始了,我们就可以按返回键退出这个界面,执行各种操作,从而复现问题了。
  8. 最后的最后,复现了问题步骤以后,记得连接电脑,复制存储目录下的GNLogs文件夹(因机型与平台的不同,有些为diag_logs文件夹),最好压缩后发给工作人员

MTK平台

1、到手机的拨号界面,输入*#446633#,会进入工程模式的界面。如下图所示,然后手指向左侧滑动,进入Log and Debugging子项下的MTKLogger选项,点击进入
2、 默认界面如下,正常情况下直接开启即可。开启方法就是点击正下方的红色小三角,类似于播放器的图标一样。开启后会有一个等待动画,几秒钟后开始计时,就表明你已经开始记录log了。这时候就可以退出此界面去复现你遇到的问题了。(也就是操作一遍你遇到问题的过程)
PS:如果你对于log占用的空间比较在意,并且所反馈的问题确认与网络无关,可以在开启log之前,点击右上角的设置按钮,进入下方右侧图片中的界面,关闭ModemLog。这样记录下来的log体积会大大减少。
但如果你的问题跟网络有关(无论数据流量还是WiFi相关),请务必保持ModemLog在开启的状态,否则记录的log是没用的!
3、复现问题后,下拉状态栏,进入log抓取界面,然后点击按钮关闭log开关。进入文件管理器,找到mtklog文件夹,压缩后发给工作人员
4、 log抓取部分的教程到此就结束了。Log传完以后,手机里的log文件仍然占用着空间,你可以直接在文件管理器删除整个mtklog文件夹进行清理,也可以进入抓log界面,点击右下角的垃圾桶,进入后选择右下角的Clear All,即可完成清理

一加


氢OS系统日志抓取步骤:

  1. 打开拨号键盘输入*#800#——点击oneplus logkit;
  2. 勾选“保存log”;
  3. 点击“QXDM log抓取”;
  4. 点击“打开device log”;
  5. 返回操作使BUG复现;
  6. 问题复现后记录问题出现时间,等待几分钟;
  7. 将文件管理中“oem_log”文件夹拷出;
  8. 为了不占用更多手机容量,在LOG已经提取后,可以再次打开“常规log日志信息抓取”与“QXDM log抓取”,关闭log抓取以后点击“删除历史log”。
    然后把提取的文件压缩后发我就可以了

乐视


  1. 打开拨号页面,输入*#*#8888#*#* (或者打开问题反馈app,连续按5次左上角在线反馈后,会自动打开Log抓取设置页面)
  2. 进入抓取页面后,把所有log选项都打开
  3. 点击开始记录离线日志,点击后软件会在回后台运行记录日志
  4. 复现你所发现的bug或问题后,按照上面的方法再次进入Log抓取页,点击停止记录,选择复现了,保存日志。
  5. 保存日志后会自动打开在线反馈页面,请直接点返回键,放弃在线提交。(切记不要点提交!)
  6. 返回桌面,找到文件管理app打开。依次点击:手机储存→logs→ArchivedLogs。这时可以看到抓取的log日志就保存在ArchivedLogs文件夹下
  7. 把抓取的log日志压缩后可以发送给工程师们咯

酷派


MTK平台:

一、 如何抓取日志?

  1. 进入拨号盘,输入*20121220#进入工程模式,选择【MTK_Engineermode】。
  2. 左划到【Log and Debugging】下并点选【MTKLogger】。
  3. 点击图标抓取日志。
    日志抓取是可home键挂后台并会在托盘显示日志正在运行提示,下拉托盘点击即可进入日志抓取界面。
    注意:日志开启后,操作出现异常日志方有效,提供日志时同时提供问题发生时间点,以便定位问题。

二、如何调整日志输出等级?
1、进入工程模式中后,进入日志输出等级,就是下面的截图
2、分别将Java log level和Cand C++ log level的日志等级调整为LOGV:
3、将Kernel log level的输出等级调整为最后的KERN_DEBUG:
三、如何保存日志?

  1. 在日志抓取界面,点击结束图标,停止日志抓取。日志会自动保存到/storage/emulated/0/mtklog目录下。
  2. 进入文件管理器,在/storage/emulated/0/mtklog目录下,将【mdlog】、【mobilelog】、【netlog】三个文件夹一起压缩打包即可。
    四、如何清除日志文件?
  3. 在日志抓取界面,点击删除图标,选择【CLEAR ALL】清除日志文件。
    注意:长时间开启日志抓取会占用存储空间,建议及时清除日志文件。

高通平台:

方法如下:
1 、从拨号盘输入*20121220#进入到工程模式
2 、进入 日志输出等级
3、 按照如下的说明设置一下日志级别
java log level 选择 LOGV (即第一项)
c and c++ log level 选择 LOGV (即第一项)
kernel log level 选择 KERN_DEBUG (即最后一项)
按照上面的设置之后,返回上一级
4、 进入 离线日志设置 进行如下的操作:
(1)取消 离线日志开关 的钩钩, 然后依次点击菜单最后两行是 删除以前所有的日志 和 清除日志缓冲区。
(2)进入 日志缓冲区类型,请勾选除最后一项“Elog日志转储”之外的所有选项,即勾选Main缓冲区,Radio缓 冲区,Events缓冲区,system缓冲区,网络日志转储,内核日志转储,QXDM日志转储。然后按 确定 ,请务必保证 QXDM日志转储 勾选上。
(3)日志转储设为NO
(4)完成上面的操作之后 ,重新将 离线日志开关 勾选上。
(5)重启机器,按照你的操作步骤操作。
(6)从/sdcard/log中把日志压缩发给我们,谢谢!

使用ADB抓取系统log
一、配置环境变量
电脑桌面上右击 计算机–>属性–>高级系统配置–>环境变量,在 系统变量 中找到path(没有可以新建一个)。点击path后再变量值的开头加上你的路径“d:adb;”,切记路径最后还要带个分号。点击确定–>确定,如此就完成了环境变量的配置。
二、抓log
将手机与电脑用数据线相连。
打开命令行(开始–>附件–>命令提示符),然后进行一下步骤:

  1. 显示现在连接的手机设备:输入 adb devices + 回车
  2. 抓log:输入 adb logcat + 回车
  3. 停止抓log:使用ctrl + c
  4. 将log导出:adb logcat > d:1.txt(将log导出到d:/1.txt文件中
    ADB工具下载地址:https://yunpan.cn/cWkyshMeDeKDf 访问密码 8a91
123
Horace Zhao

Horace Zhao

宁静以致远

21 日志
6 分类
8 标签
RSS
E-Mail Github CSDN 知乎
Links
  • Github
  • StackOverflow
  • Segmentfault
© 2018 — 2019 Horace Zhao
本站访客数:
|
由 Hexo 强力驱动
|
博客全站共47.5k字