Android-FrameWork原理与架构分析

  • Post author:
  • Post category:其他


Android-FrameWork原理与架构分析


Android架构主要分为分为四部分,从上往下依次为

APPLICATION(应用程序),

APPLICATION FRAMEWORK(应用框架层),

LIBRARIES(共享库,以及android运行时库),

LINUX KERNEL(内核层);

APPLICATION

主要为系统中的应用,如桌面,闹铃,设置,日历,电话,短信等系统应用。

APPLICATION FRAMEWORK

Android的应用程序框架为应用程序层的开发者提供APIs,它实际上是一个应用程序的框架。由于上层的应用程序是以JAVA构建的,因此本层次提供了以下服务:

i. 丰富而又可扩展的视图(Views),可以用来构建应用程序, 它包括列表(lists),网格(grids),文本框(text boxes),按钮(buttons), 甚至可嵌入的web浏览器;

ii. 内容提供器(Content Providers)使得应用程序可以访问另一个应用程序的数据(如联系人数据库),或者共享它们自己的数据;

iii. 资源管理器(Resource Manager)提供非代码资源的访问,如本地字符串,图形,和布局文件( layout files );

iv. 通知管理器 (Notification Manager) 使得应用程序可以在状态栏中显示自定义的提示信息;

v. 活动管理器( Activity Manager) 用来管理应用程序生命周期并提供常用的导航回退功能。

Android Runtime

Android 包括了一个核心库,该核心库提供了JAVA编程语言核心库的大多数功能。

每一个Android应用程序都在它自己的进程中运行,都拥有一个独立的Dalvik虚拟机实例。

Dalvik被设计成一个设备可以同时高效地运行多个虚拟系统。

Dalvik虚拟机执行(.dex)的Dalvik可执行文件,该格式文件针对小内存使用做了优化。

同时虚拟机是基于寄存器的,所有的类都经由JAVA编译器编译,然后通过SDK中 的 “dx” 工具转化成.dex格式由虚拟机执行。

Dalvik虚拟机依赖于linux内核的一些功能,比如线程机制和底层内存管理机制。

Libraries

Android 包含一些C/C++库,这些库能被Android系统中不同的组件使用。它们通过 Android 应用程序框架为开发者提供服务。

以下是一些核心库:

i. 系统 C 库 bionic:一个从 BSD 继承来的标准 C 系统函数库( libc ), 它是专门为基于 embedded linux 的设备定制的。

ii. 媒体库:基于 PacketVideo OpenCORE。该库支持多种常用的音频、视频格式回放和录制,同时支持静态图像文件。

编码格式包括MPEG4,H.264,MP3,AAC,AMR,JPG,PNG。

iii. Surface Manager:对显示子系统的管理,并且为多个应用程序提供了2D和3D图层的无缝融合。

iv. LibWebCore:一个最新的web浏览器引擎用,支持Android浏览器和一个可嵌入的web视图。

v. SGL:底层的2D图形引擎

vi. 3D libraries:基于OpenGL ES 1.0 APIs实现,该库可以使用硬件3D加速(如果可用)或者使用高度优化的3D软加速。

vii. FreeType:位图(bitmap)和矢量(vector)字体显示。

SQLite:一个对于所有应用程序可用,功能强劲的轻型关系型数据库引擎。

Linux Kernel

Android 的核心系统服务依赖于 Linux 内核,如安全性,内存管理,进程管理, 网络协议栈和驱动模型。

Linux 内核也同时作为硬件和软件栈之间的抽象层。


framework简介

系统服务 SystemServer 是framework中非常重要的一个进程,它是在虚拟机启动后运行的第一个java进程,

SystemServer启动其他系统服务,这些系统服务都是以一个线程的方式存在于SystemServer进程中。

a) ActivityManagerService 最核心的服务之一,管理Activity

b) PowerManagerService 电源管理服务

c) EntropyService 提供伪随机数

d) TelephonyRegistry 通过该服务注册电话模块的事件响应,比如重启、关闭、启动等

e) PackageManagerService 程序包管理服务

f) AccountManagerService 账户管理服务,是指联系人账户,而不是Linux系统的账户

g) ContentService ContentProvider服务,提供跨进程数据交换

h) BatteryService 电池管理服务

i) LightsService 自然光强度感应传感器服务

j) VibratorService 震动器服务

k) AlarmManagerService 定时器管理服务,提供定时提醒服务

l) WindowManagerService Framework最核心的服务之一,负责窗口管理

m) BluetoothService 蓝牙服务

n) DevicePolicyManagerService 提供一些系统级别的设置及属性

o) StatusBarManagerService 状态栏管理服务

p) ClipboardService 系统剪切板服务

q) InputMethodManagerService 输入法管理服务

r) NetStatService 网络状态服务

s) NetworkManagementService 网络管理服务

t) ConnectivityService 网络连接管理服务

u) AccessibilityManagerService 辅助管理程序截获所有的用户输入,并根据这些输入给用户一些额外的反馈,起到辅助的效果

v) MountService 挂载服务,可通过该服务调用Linux层面的mount程序

w) NotificationManagerService 通知栏管理服务,Android中的通知栏和状态栏在一起,只是界面上前者在左边,后者在右边

x) DeviceStorageMonitorService 磁盘空间状态检测服务

y) LocationManagerService 地理位置服务

z) SearchManagerService 搜索管理服务

aa) DropBoxManagerService 通过该服务访问Linux层面的Dropbox程序

ab) WallpaperManagerService 墙纸管理服务,墙纸不等同于桌面背景,在View系统内部,墙纸可以作为任何窗口的背景

ac) AudioService 音频管理服务

ad) BackupManagerService 系统备份服务

ae) AppWidgetService         Widget服务

af) RecognitionManagerService 身份识别服务

ag) DiskStatsService 磁盘统计服务

核心服务介绍

a) ActivityManagerService

ActivityManagerService(以下简称:AMS)是android系统的一个系统服务,是应用进程的管理服务端,直接的控制了应用程序的各个行为,

保证了系统中不同的应用程序之间能够和谐的合理的进行调度运行。

AMS是android上层系统最核心的模块之一,其主要的工作是对所有的应用进程及其进程中的四大组件进行管理。(当然这里面也涉及了一些window、电源、权限等内容)

对进程的管理包括:进程的创建与销毁、进程的优先级调整。

对组件的管理包括:Activity的调度管理、Service的管理、Broadcast的分发、以及ContentProvider管理。

b) WindowManagerService

对系统中所有窗口进行管理;动画处理 ;Input分发、处理;Display管理(多屏显示)

c) Android Graphics系统

d) Surfaceflinger

负责Layer合成(composer);创建surface;管理surface。

e) PackageManagerService

负责Package的管理,应用程序的安装 、卸载、信息查询等。

f) Input系统

android的输入系统主要完成键盘、触屏、鼠标等输入设备的事件输入及向焦点窗口和焦点视图的事件派发,插入,过滤,拦截等功能。

android支持的输入设备主要有:键盘、鼠标、触摸屏、轨迹球、游戏摇杆/手柄、绘图板。

Framework的范围

Framework负责APPLICATION FRAMEWORK、ANDROID RUNTIME和LIBRARIES三部分。

a) 系统Manager和Service相关内容

b) 系统接口和jni相关内容

c) 系统功能相关内容(watchdog、vold、binder等)

d) 虚拟机dalvik、art

e) 系统so库相关内容

f) CTS、GTS等预分析

g) Monkey预分析

h) 系统稳定性问题(系统ANR、冻屏、重启、蓝屏等)

i) 系统性能问题

framework基础

语言基础:Java, C/C++;

技术基础:进程间通信,多线程,jni,linux相关知识,binder;

Android FrameWork包含的内容

这一层为上层应用提供各种api,提供各种组件和服务来支持我们的Android开发;

通常情况下包含三个方面的内容,服务端, 客户端, Linux驱动;

服务端

(1)ActivityManagerService(Ams)负责调度整个app中的Activity,管理所有Activity的生命周期。

是android上层系统最核心的模块之一,主要对android的四大组件进行管理也包括一些诸如 电源,窗口,权限的内容

进程管理包括,进程的创建和销毁,进程的优先级调度

组件的管理,android的四大组件 Activity Broadcast Content Provider  Service

(2)WindowManagerService(Wms)管理程序的窗口的,动画,等 简单来说跟View有关的都离不开它。

(3)KeyQ类是Wms的一个内部类一但创建就会启动一个新线程,这个线程会不断的接受有关UI的一些消息,将其放到QueueEvent的消息队列中。

(4)InputDispatcherThread类该类也是一但创建线程就会不断的从上main的QueueEvent中取出消息,并进行过滤,然后发送给当前活动的客户端程序。

(5)Manager机制  Manager本身是一个服务,服务端有好多服务,当客户端想要去调用这些服务的时候,每次随意去调动的话显得很是混乱,

所以建立了这个“中间人”,客户端对服务的请求都需要先通过Manager,由他来调用,类似于java的封装。

客户端

(1)ActivityThread类 主线程类 即UI线程类,根据Ams的要求对Activity BroadCast进行调度

(2)ViewRoot类   负责客户端和 Wms的交互,内部类有W类,W类继承与Binder  Wms想和客户端通信的时候会用到此类,

内部还有 ViewRootHandler 继承与Handler,所以当W类收到消息以后可以发送给UI线程,界面的绘制发起点也在此。

(3)W类 ViewRoot的内部类,主要帮助ViewRoot和Wms实现IPC

(4)Activity类,apk运行的最小单位

(5)PhoneWindow类 ,继承自Window类里面放置了一个DecorView类,提供了统一窗口操作API;

(6)DecorView ,可以看到的View的所有,继承自FrameLayout,我们写的布局View就是放在这个里面。

(7)WindowsManager 客户端和Wms 交流的中介,客户端想要创建窗口,得先提前告知WindowsManager,客户端不能直接和Wms通信。

Linux驱动

SurfaceFlingger 和 Binder

SurfaceFlingger 负责将各个画面显示到屏幕上,Binder负责进程间通信。


Android Framework原理

App启动过程

点击桌面App图标,Launcher进程采用Binder IPC向system_server进程发起startActivity请求

system_server进程接收到请求后,向zygote进程发送创建进程的请求

Zygote进程fork出新的子进程,即App进程

App进程,通过Binder IPC向sytem_server进程发起attachApplication请求

system_server进程在收到请求后,进行一系列准备工作后,再通过binder IPC向App进程发送scheduleLaunchActivity请求

App进程的binder线程(ApplicationThread)在收到请求后,通过handler向主线程发送LAUNCH_ACTIVITY消息

主线程在收到Message后,通过发射机制创建目标Activity,并回调Activity.onCreate()等方法

App便正式启动,开始进入Activity生命周期,执行完onCreate/onStart/onResume方法,UI渲染结束后便可以看到App的主界面

Android开机流程

BootLoader引导

当按开机键的时候,引导芯片开始从固化在ROM的预设代码开始执行,然后加载引导程序到RAM

BootLoader,又称为引导程序。它是在操作系统运行之前运行的一段程序

BootLoader负责初始化软件运行所需要的最小硬件环境,最后加载内核到内存.

启动Kernel

这个入口的函数是start_kernel函数

start_kernel函数执行到最后调用了reset_init函数进行后续的初始化

start_kernel最终启动用户空间的init程序.

启动Android

当初始化内核之后,init进程负责解析init.rc配置文件, 就会启动一个相当重要的祖先进程,也就是init进程,

在Linux中所有的进程都是由init进程直接或间接fork出来的.

init进程负责创建系统中最关键的几个核心daemon(守护)进程,尤其是zygote和System_Server进程

zygote进程 android启动的第一个Dalvik 虚拟机,它将负责启动Java世界的进程;

zygote通过fork系统调用创建system_server进程,同时定义了一个Socket,用于接收ActivityManagerService启动应用程序的请求;

System_Server进程是Binder通信的基础,它还提供了property service(属性服务),类似于windows系统的注册表服务;

系统里面重要的服务都是在这个进程里面开启的,例如AMS, WindowsManager, PackageManagerService等等都是由这个System_Server fork出来的;

在System_Server进程开启的时候,就会初始化ActivityManagerService 。

同时会加载本地系统的服务库,调用createSystemContext()创建系统上下文,创建ActivityThread及开启各种服务等等;

system_server中开启了核心系统服务,并将系统服务添加到ServiceManager中,然后系统进入SystemReady状态.

启动Home Activity

在systemReady状态,ActivityManagerService会与zygote的Socket通信,请求启动Home

zygote收到AMS的连接请求后,执行runSelectLoopMode处理请求

zygote处理请求会通过forkAndSpecialize启动新的应用进程,并最终启动Home.

概况

1. 系统加电,执行bootloader。Bootloader负责初始化软件运行的最小硬件环境,最后加载内核到内存

2. 内核加载到内存后,进入内核引导阶段,在内核引导的最后,调用start_kernel进入内核启动阶段。start_kernel最终启动用户空间的init程序

3. init负责解析init.rc配置文件,开启系统守护进程。2个最重要的守护进程是zygote进程和serverManager进程。

zygote是android启动的第一个Dalvik虚拟机,ServiceManager服务是Binder通信的基础

4. zygote虚拟机启动子进程system_server,在system_server中启动了核心系统服务,并将系统服务添加到ServiceManager中,然后系统进入SystemReady状态

5. 在SystemReady状态,ActivityManagerService与zygote中的socket通信,通过zygote启动home应用,进入系统界面

从步骤3开始, init启动后,上层的实现

1. init启动的核心Daemon服务包括Android的第一个Dalvik虚拟机Zygote

2. zygote定义一个socket,用于接受ActivityManangerService启动应用的请求

3. zygote通过fork系统调用创建system_server进程

4. 在system_server进程中,将会启动系统核心服务以及其他服务

5. 系统服务启动后会注册到ServiceManager中,用于Binder通信

6. ActivityManagerService进入systemReady状态

7. 在systemReady状态,ActivityManangerService会与zygote的Socket通信,请求启动Home

8. zygote收到AMS的连接请求后,执行runSelectLoopMode处理请求

9. zygote处理请求会通过forkAndSpecialize启动新的应用进程,并最终启动Home


Handler机制与底层实现原理

概念

Message – Message代表一个行为what或者一串动作Runnable, 每一个消息在加入消息队列时,都有明确的目标Handler

ThreadLocal – 线程本地存储区(Thread Local Storage,简称为TLS),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域。ThreadLocal的作用是提供线程内的局部变量TLS,这种变量在线程的生命周期内起作用,每一个线程有他自己所属的值(线程隔离)

MessageQueue (C层与Java层都有实现) – 以队列的形式对外提供插入和删除的工作, 其内部结构是以双向链表的形式存储消息的

Looper (C层与Java层都有实现) – Looper是循环的意思,它负责从消息队列中循环的取出消息然后把消息交给Handler处理

Handler – 消息的真正处理者, 具备获取消息、发送消息、处理消息、移除消息等功能

普通的线程是没有looper的,如果需要looper对象,那么必须要先调用Looper.prepare方法,而且一个线程只能有一个looper

Handler是如何完成跨线程通信的

Android中采用的是Linux中的管道通信

* 关于管道,简单来说,管道就是一个文件

* 在管道的两端,分别是两个打开文件文件描述符,这两个打开文件描述符都是对应同一个文件,其中一个是用来读的,别一个是用来写的

* 消息队列创建时,会调用JNI函数,初始化NativeMessageQueue对象。NativeMessageQueue则会初始化Looper对象

* Looper的作用就是,当Java层的消息队列中没有消息时,就使Android应用程序主线程进入等待状态,

而当Java层的消息队列中来了新的消息后,就唤醒Android应用程序的主线程来处理这个消息;

整个消息机制流程

Handler通过sendMessage()发送Message到MessageQueue队列

Looper通过loop(),不断提取出达到触发条件的Message,并将Message交给target来处理

经过dispatchMessage()后,交回给Handler的handleMessage()来进行相应地处理

将Message加入MessageQueue时,往管道写入字符,会唤醒loop线程;

如果MessageQueue中没有Message,并处于Idle状态,则会执行IdelHandler接口中的方法,往往用于做一些清理性地工作

ContentProvider原理

Android绘制原理

Activity的window组成,Activity内部有个Window成员,它的实例为PhoneWindow,PhoneWindow有个内部类是DecorView,

这个DecorView就是存放布局文件的,里面有TitleActionBar和我们setContentView传入进去的layout布局文件;

Window类是一个抽象类,提供绘制窗口的API

PhoneWindow是继承Window的一个具体的类,该类内部包含了一个DecorView对象,该DectorView对象是所有应用窗口(Activity界面)的根View

DecorView继承FrameLayout,里面id=content的就是我们传入的布局视图

ContentView必须是一个ViewGroup

ViewGroup 开始递归执行以下逻辑进行绘制

+ measure, 递归测量view的大小。有3种测量模式

– MeasureSpec.EXACTLY表示确定大小

– MeasureSpec.AT_MOST表示最大大小

– MeasureSpec.UNSPECIFIED不确定

+ layout,递归布局view的位置

+ draw,递归绘制view

– ViewRootImpl中的代码会创建一个Canvas对象,然后调用View的draw()方法来执行具体的绘制

Binder机制及底层实现

进程空间分配

进程间,用户空间的数据不可共享,所以用户空间 = 不可共享空间

进程间,内核空间的数据可共享,所以内核空间 = 可共享空间

进程内用户与内核进行交互称为系统调用

Binder跨进程通信(IPC)的原理

先通过进程间的内核空间进行数据交互

再通过进程内的用户空间&内核空间进行数据交互,从而实现进程间的用户空间的数据交互

而Binder,就是充当连接两个进程(内核空间)的通道

使用步骤:

注册服务

* Server进程向Binder驱动发起服务注册请求

* Binder驱动将注册请求转发给ServiceManager进程

* ServiceManager进程添加该服务

* 此时ServiceManager进程拥有该服务信息

获取服务

* Client向Binder驱动发起获取服务的请求,传递要获取的服务名称(service name)

* Binder驱动将该请求转发给ServiceManager进程

* ServiceManager查找到Client需要的Server对应的服务信息

* 通过Binder驱动将上述服务信息(服务代理对象)返回给Client进程

* 此时client进程与server进程已经建立了连接

使用服务

* Client进程将参数数据发到Server进程

1. client 进程将需要的传送的数据放到client进程的共享内存;(当前线程被挂起)

2. Binder驱动从client的共享内存中读取数据,并根据ServiceManager进程里面的Server信息找到对应的Server进程

3. Binder驱动将数据copy到Server进程的共享内存里,并通知Server进程解包

* Server进程根据Client进程要求,调用目标方法

1. 接到Binder驱动通知后,Server进程从线程池中取出线程,进行数据解包和调用目标方法

2. 将最终方法结果写到自己的共享内存

* Server进程将目标方法的结果,返回给Client进程

1. Binder驱动程序将Server进程的共享内存里面的数据(方法执行结果) copy 到client进程的共享内存

2. 通知client进程获得返回结果(此时client进程之前被挂起的线程被重新唤醒)

从客户端来看进程被挂起,binder通信是通过的;但是在服务端却是收到消息后从线程池中取出线程进行处理并返回结果,在服务端是异步处理的(???待确认);

Client进程、Server进程 & Service Manager 进程之间的交互 都必须通过Binder驱动(使用 open 和 ioctl文件操作函数),而非直接交互

Client进程、Server进程 & Service Manager 进程属于进程空间的用户空间,不可进行进程间交互

Binder驱动 属于 进程空间的 内核空间,可进行进程间 & 进程内交互

Binder驱动 & Service Manager进程 属于 Android基础架构(即系统已经实现好了);而Client 进程 和 Server 进程 属于Android应用层(需要开发者自己实现)

ActivityThread工作原理

Window 、WMS的工作原理

ThreadLocal原理,实现及如何保证Local属性

每个Thread维护一个ThreadLocalMap映射表,这个映射表的key是ThreadLocal实例本身,value是真正需要存储的Object

Android线程有没有上限

android本身就是linux系统 所以查看命令和linux一样

Android内存限制

dalvik.vm.heapstartsize 表示 初始内存大小是8m

dalvik.vm.heapgrowthlimit 表示标准内存大小是96m 一般应用都是这么大

dalvik.vm.heapsize 表示 在manifest配置文件中application标签下配置 android:largeHeap=”true”时的内存大小

Android apk大小限制

apk安装包大小理论上没有限制。但是各个应用商店为了有大小限制google play 要求小于50M , 扩展包可以扩展到2g

压测: 800M的apk是没问题的只是安装的时间比较长。1.6G的apk包则把手机弄死机

线程池有没有上限

ThreadPoolExecutor构造函数的maximumPoolSize决定

AndroidToast原理分析

Art和Dalvik对比

ART 的机制与 Dalvik 不同。在Dalvik下,应用每次运行的时候,字节码都需要通过即时编译器(just in time ,JIT)转换为机器码,这会拖慢应用的运行效率,而在ART 环境中,应用在第一次安装的时候,字节码就会预先编译成机器码,使其成为真正的本地应用。这个过程叫做预编译(AOT,Ahead-Of-Time)。这样的话,应用的启动(首次)和执行都会变得更加快速。

BlockCanaryEx原理

即整个应用的主线程,只有这一个looper,不管有多少handler,最后都会回到这里

public static void loop() {


for (;;) {


// This must be in a local variable, in case a UI event sets the logger

Printer logging = me.mLogging;

if (logging != null) {


logging.println(“>>>>> Dispatching to ” + msg.target + ” ” +

msg.callback + “: ” + msg.what);

}

msg.target.dispatchMessage(msg);

if (logging != null) {


logging.println(“<<<<< Finished to ” + msg.target + ” ” + msg.callback);

}



}

}

设置Printer对象,判断是否超过预期时间,notifyLog

代码结构

framework开发工作方式与应用开发的工作方式是完全不同的,framework开发是基于Android源代码进行的开发,版本管理主要使用:git,repo两种工具。

如果想要学习framework开发,必须需要下载一套Android源码。

官方下载地址: https://source.android.com/source/downloading.html,

具体的下载编译调试代码的方式可以参考老罗的博客视频,讲解的非常清楚,附上地址(http://0xcc0xcd.com/p/index.php),感谢老罗。

也可参考国内的mirror镜像,如清华AOSP镜像 https://mirrors.tuna.tsinghua.edu.cn/help/AOSP/

而framework开发主要要了解的就是frameworks下面的内容。

android源码的结构图

├── bionic    android上实现的libc库

├── bootable  存放可启动项,如recovery、bootloader等

├── build     android编译系统所用到的make文件及其它工具

├── cts       android兼容性测试

├── dalvik    dalvik虚拟机

├── development  与开发相关的一些东西

├── device    存放需要适配的设备信息

├── external  第三方库

├── frameworks  framework部分

├── hardware  硬件相关代码

├── kernel    kernel相关代码

├── libcore   android上实现的Java基础库

├── Makefile

├── ndk

├── out       编译输出目录

├── packages  包含系统应用、壁纸应用、内容提供者、输入法等

├── prebuilt  预编译好的工具

├── sdk       sdk相关内容

├── system    操作系统层次的一些可执行程序和配置文件

├── u-boot    用于引导linux启动的u-boot

└── vendor    存放与厂商相关的信息,也可粗放需要适配的设备信息


当把代码下载完毕后需要对源代码整体进行编译,具体方法如下:

1.source build/envsetup.sh

读取各个board的vendorsetup.sh

2.lunch

选择要编译的对象

3.完整系统编译,输出到out/target/product/<device>/…

make -j16(j16为使用16个线程同时编译)

4.如果要单编某个模块,就是用如下命令来编译需要的模块。

编译bootloader,

make bootloader

编译kernel生成boot.img,

make bootimage

编译system生成system.img,

make systemimage

编译userdata生成userdata.img,

make userdataimage

编译recovery生成recovery.img,

make recoveryimage

如何进入fastboot:

开机状态下执行adb reboot bootloader

1.检查连接设备

fastboot devices

2.烧写system.img

fastboot flash system system.img

3.烧写boot.img(recovery)

fastboot flash boot boot.img(recovery.img)

4.烧写bootloader

fastboot flash 2ndbl u-boot.bin

常用工具以及调试方法

常用工具

HierarchyViewer

HierarchyViewer是随Android SDK发布的工具,位置在sdk的tools文件夹下,名为hierarchyviewer。

它是Android自带的非常有用而且使用简单的工具,能够让我们从可视化的角度直观地获得UI布局设计结构和各种属性的信息,帮助我们优化布局设计。

DDMS

DDMS 的全称是Dalvik Debug Monitor Service,是 Android 开发环境中的Dalvik虚拟机调试监控服务。

它为我们提供例如:为测试设备截屏,针对特定的进程查看正在运行的线程以及堆信息、Logcat、广播状态信息、模拟电话呼叫、接收SMS、虚拟地理坐标等等

模拟按键

使用命令adb shell input keyevent + 对应的键值,可以模拟对应的操作。键值定义可参考 frameworks\base\core\java\android\view\KeyEvent.java

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

am命令

有时候我们在调试系统时可以在终端使用am命令来发送广播,打开Activity,启动Service等操作,十分方便。

am的详细操作如下,我们可以通过adb shell am + 对应命令,就可操作。参考 http://gityuan.com/2016/02/27/am-command/

对于am的子命令,startservice, stopservice, broadcast, kill, profile start, profile stop, dumpheap的可选参数都允许设置–user <USER_ID>。

目前市面上的绝大多数手机还是单用户模式,故可以忽略该参数,默认为当前用户。

dumpsys

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

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

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

addr2line

在分析framework稳定性问题时会遇到Native crash的发生,而Native crash与java crash的堆栈信息不同。下面是一段Native 堆栈的片段。

03-19 14:58:50.034   319  3145 D kevin_Render: #01 pc 0005f8a5  /system/lib/libmediaplayerservice.so (android::NuPlayer::Renderer::setAnchorTime(long long, long long, long long, bool)+72)

03-19 14:58:50.034   319  3145 D kevin_Render: #02 pc 000618c5  /system/lib/libmediaplayerservice.so (android::NuPlayer::Renderer::onNewAudioMediaTime(long long)+88)

03-19 14:58:50.035   319  3145 D kevin_Render: #03 pc 00062249  /system/lib/libmediaplayerservice.so (android::NuPlayer::Renderer::onDrainAudioQueue()+764)

03-19 14:58:50.035   319  3145 D kevin_Render: #04 pc 00062ec3  /system/lib/libmediaplayerservice.so (android::NuPlayer::Renderer::onMessageReceived(android::sp<android::AMessage> const&)+866)

03-19 14:58:50.035   319  3145 D kevin_Render: #05 pc 0000d12d  /system/lib/libstagefright_foundation.so (android::ALooperRoster::deliverMessage(android::sp<android::AMessage> const&)+164)

03-19 14:58:50.035   319  3145 D kevin_Render: #06 pc 0000cac1  /system/lib/libstagefright_foundation.so (android::ALooper::loop()+216)

03-19 14:58:50.035   319  3145 D kevin_Render: #07 pc 00010977  /system/lib/libutils.so (android::Thread::_threadLoop(void*)+110)

03-19 14:58:50.035   319  3145 D kevin_Render: #08 pc 00016f5b  /system/lib/libc.so (__pthread_start(void*)+30)

03-19 14:58:50.035   319  3145 D kevin_Render: #09 pc 00014f7b  /system/lib/libc.so (__start_thread+6)

根据上面的堆栈,我们并不能看到具体哪个函数,哪行代码出现了问题,这个时候我们就需要解析Native函数的地址值,

如上面 00062ec3 这样的符号,我们需要通过addr2line来进行解析。我们可以使用linux系统提供的addr2line命令,也可以使用源代码sdk中的工具。具体命令如下:

addr2line -e out/target/product/项目名/symbols/system/lib/libxxx.so -f -C <addr>

如果我们想要解析03行的地址值 00062249

$ addr2line -e out/target/product/miki8163_9003/symbols/system/lib/libmediaplayerservice.so -f -C 00062249

android::NuPlayer::Renderer::onDrainAudioQueue()

/home/kevin/work/source/sm8163/sm8163/frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp:868

解析后我们看到该行代码属于函数android::NuPlayer::Renderer::onDrainAudioQueue(),在NuPlayerRenderer.cpp的868行,这样就十分方便了。

meminfo

当我们查询系统的内存信息除了使用dumpsys命令,还可以直接使用adb shell cat /proc/meminfo

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工具使用说明》 https://blog.csdn.net/fu_kevin0606/article/details/53928748

LOG分析

在调试Android系统时如果要实时的查看输出的log信息就可以使用adb logcat -vthreadtime或者adb logcat在终端输出需要的log信息,

在5.1及其之前的版本需要adb logcat后需要加上-vthreadtime 意思是将输出时间以及进程线程信息,

5.1之后的版本系统自动添加了时间与进程线程信息,直接用adb logcat就可以。


添加LOG信息

java层添加

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

如 Log.d(TAG, “getActiveSubscriberId=” + retVal + ” subId=” + subId); //添加log信息

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

try {


throw new Exception(“Call Stack Trace”);

} catch (Exception e) {


Log.i(“debug”, “xxx”, e);

}

运行时就会打印调用栈信息

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(“debug”);

示例代码如下:

void NuPlayer::start() {


CallStack stack;

stack.update();

stack.log(“debug”);

(new AMessage(kWhatStart, this))->post();

}

可以使用前面学习的 addr2line 解析调用栈地址得到具体代码调用;


参考资料:

https://www.jianshu.com/p/2f95ab717078

https://blog.csdn.net/fu_kevin0606/article/details/79532710

https://blog.csdn.net/fu_kevin0606/article/details/79616216

https://blog.csdn.net/haibowen/article/details/82456747



版权声明:本文为halazi100原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。