和你一起终身学习,这里是程序员 Android

本篇文章主要介绍 Android 开发中的部分知识点,通过阅读本篇文章,您将收获以下内容:

一、input 概述
二、input in Systrace
三、关键知识点和流程
四、Input 刷新与 Vsync
五、Input 调试信息

一、input 概述

在Android 基于 Choreographer 的渲染机制详解 这篇文章中,我有讲到,Android App 的主线程运行的本质是靠 Message 驱动的,这个 Message 可以是循环动画、可以是定时任务、可以是其他线程唤醒,不过我们最常见的还是 Input Message ,这里的 Input 是以 InputReader 这里的分类,不仅包含触摸事件(Down、Up、Move) , 可包含 Key 事件(Home Key 、 Back Key) . 这里我们着重讲的是 触摸事件

由于 Android 系统在 Input 链上加了一些 Trace 点,且这些 Trace 点也比较完善,部分厂家可能会自己加一些,不过我们这里以标准的 Trace 点来讲解,这样不至于你换了个手机抓的 Trace 就不一样了

Input 在 Android 中的地位是很高的,我们在玩手机的时候,大部分应用的滑动、跳转这些都依靠 Input 事件来驱动,后续我会专门写一篇文章,来介绍 Android 中基于 Input 的运行机制。这里是从 Systrace 的角度来看 Input 。看下面的流程之前,脑子里先有个关于 Input 的大概处理流程,这样看的时候,就可以代入:

  1. 触摸屏每隔几毫秒扫描一次,如果有触摸事件,那么把事件上报到对应的驱动。
  2. InputReader 读取触摸事件交给 InputDispatcher 进行事件派发。
  3. InputDispatcher 将触摸事件发给注册了 Input 事件的 App。
  4. App 拿到事件之后,进行 Input 事件分发,如果此事件分发的过程中,App 的 UI 发生了变化,那么会请求 Vsync,则进行一帧的绘制。

另外在看 Systrace 的时候,要牢记 Systrace 中时间是从左到右流逝的,也就是说如果你在 Systrace 上画一条竖直线,那么竖直线左边的事件永远比右边的事件先发生,这也是我们分析源码流程的一个基石。我希望大家在看基于 Systrace 的源码流程分析之后,脑子里有一个图形化的、立体的流程图,你跟的代码走到哪一步了在图形你在脑中可以快速定位出来。

#二、input in Systrace

下面这张图是一个概览图,以滑动桌面为例 (滑动桌面包括一个 Input_Down 事件 + 若干个 Input_Move 事件 + 一个 Input_Up 事件,这些事件和事件流都会在 Systrace 上有所体现,这也是我们分析 Systrace 的一个重要的切入点),主要牵扯到的模块是 SystemServer 和 App 模块,其中用蓝色标识的是事件的流动信息,红色的是辅助信息。

InputReader 和 InputDispatcher 是跑在 SystemServer 里面的两个 Native 线程,负责读取和分发 Input 事件,我们分析 Systrace 的 Input 事件流,首先是找到这里。下面针对上图中标号进行简单

##1.Systrace说明

  1. InputReader 
    负责从 EventHub 里面把 Input 事件读取出来,然后交给 InputDispatcher 进行事件分发
  2. InputDispatcher
     在拿到 InputReader 获取的事件之后,对事件进行包装和分发 (也就是发给对应的)
  3. OutboundQueue 
    里面放的是即将要被派发给对应 AppConnection 的事件
  4. WaitQueue 
    里面记录的是已经派发给 AppConnection 但是 App 还在处理没有返回处理成功的事件
  5. PendingInputEventQueue 
    里面记录的是 App 需要处理的 Input 事件,这里可以看到已经到了应用进程
  6. deliverInputEvent 
    标识 App UI Thread 被 Input 事件唤醒
  7. ** InputResponse** 
    标识 Input 事件区域,这里可以看到** Input_Down 事件 + 若干个 Input_Move 事件 + 一个 Input_Up 事件这段都被算到了这里**
  8. App 响应 Input 事件 
    这里是滑动然后松手,也就是我们熟悉的桌面滑动的操作,桌面随着手指的滑动更新画面,松手后触发 Fling 继续滑动,从 Systrace 就可以看到整个事件的流程

下面以第一个 Input_Down 事件的处理流程来进行详细的工作流说明,其他的 Move 事件和 Up 事件的处理是一样的(部分不一样,不过影响不大)

2. InputDown 事件在 SystemServer 的工作流

放大 SystemServer 的部分,可以看到其工作流(蓝色),滑动桌面包括 Input_Down + 若干个 Input_Move + Input_Up ,我们这里看的是 Input_Down 这个事件

##3.InputDown 事件在 App 的工作流

应用在收到 Input 事件后,有时候会马上去处理 (没有 Vsync 的情况下),有时候会等 Vsync 信号来了之后取处理,这里 Input_Donw 事件就是直接去唤醒主线程做处理,其 Systrace 比较简单,最上面有个 Input 事件队列,主线程则是简单的处理

4.App 的 Pending 队列

##5.主线程处理 Input 事件

主线程处理 Input 事件这个大家比较熟悉,从下面的调用栈可以看到,Input 事件传到了 ViewRootImpl,最终到了 DecorView ,然后就是大家熟悉的 Input 事件分发机制

三、关键知识点和流程

从上面的 Systrace 来看,Input 事件的基本流向如下:

  1. InputReader 读取 Input 事件
  2. InputReader 将读取的 Input 事件放到 InboundQueue 中
  3. InputDispatcher 从 InboundQueue 中取出 Input 事件派发到各个 App(连接) 的 OutBoundQueue
  4. **同时将事件记录到各个 App(连接) 的 WaitQueue **
  5. App 接收到 Input 事件,同时记录到 PaddingQueue ,然后对事件进行分发处理
  6. App 处理完成后,回调 InputManagerService 将负责监听的 WaitQueue 中对应的 Input 移除

通过上面的流程,一次 Input 事件就被消耗掉了(当然这只是正常情况,还有很多异常情况、细节处理,这里就不细说了,自己看相关流程的时候可以深挖一下) , 那么本节就从上面的关键流中取几个重要的知识点讲解(部分流程和图参考和拷贝了 Gityuan 的博客的图,链接在最下面参考那一节)

##1.InputReader
InputReader 是一个 Native 线程,跑在 SystemServer 进程里面,其核心功能是从 EventHub 读取事件、进行加工、将加工好的事件发送到 InputDispatcher

InputReader Loop 流程如下

  1. getEvents:通过 EventHub (监听目录 /dev/input )读取事件放入 mEventBuffer ,而mEventBuffer 是一个大小为256的数组, 再将事件 input_event 转换为 RawEvent
  2. processEventsLocked: 对事件进行加工, 转换 RawEvent -> NotifyKeyArgs(NotifyArgs)
  3. QueuedListener->flush:将事件发送到 InputDispatcher 线程, 转换 NotifyKeyArgs -> KeyEntry(EventEntry)

核心代码 loopOnce 处理流程如下:

InputReader 核心 Loop 函数 loopOnce 逻辑如下

void InputReader::loopOnce() {int32_t oldGeneration;int32_t timeoutMillis;bool inputDevicesChanged = false;std::vector<InputDeviceInfo> inputDevices;{ // acquire lock......//获取输入事件、设备增删事件,count 为事件数量size_t count = mEventHub ->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);{......if (count) {//处理事件processEventsLocked(mEventBuffer, count);}}......mQueuedListener->flush();//将事件传到 InputDispatcher,这里getListener 得到的就是 InputDispatcher
}

##2. InputDispatcher

上面的 InputReader 调用 mQueuedListener->flush 之后 ,将 Input 事件加入到InputDispatcher 的 mInboundQueue ,然后唤醒 InputDispatcher , 从 Systrace 的唤醒信息那里也可以看到 InputDispatch 线程是被 InputReader 唤醒的

InputDispatcher 的核心逻辑如下:

  1. dispatchOnceInnerLocked(): 从 InputDispatcher 的 mInboundQueue 队列,取出事件 EventEntry。另外该方法开始执行的时间点 (currentTime) 便是后续事件 dispatchEntry 的分发时间 (deliveryTime)
  2. dispatchKeyLocked():满足一定条件时会添加命令 doInterceptKeyBeforeDispatchingLockedInterruptible;
  3. enqueueDispatchEntryLocked():生成事件 DispatchEntry 并加入 connection 的 outbound 队列
  4. startDispatchCycleLocked():从 outboundQueue 中取出事件 DispatchEntry, 重新放入 connection 的 waitQueue 队列;
  5. InputChannel.sendMessage 通过 socket 方式将消息发送给远程进程;
  6. runCommandsLockedInterruptible():通过循环遍历地方式,依次处理 mCommandQueue 队列中的所有命令。而 mCommandQueue 队列中的命令是通过 postCommandLocked() 方式向该队列添加的。

其核心处理逻辑在 dispatchOnceInnerLocked 这里

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {// Ready to start a new event.// If we don't already have a pending event, go grab one.if (! mPendingEvent) {if (mInboundQueue.isEmpty()) {} else {// Inbound queue has at least one entry.mPendingEvent = mInboundQueue.dequeueAtHead();traceInboundQueueLengthLocked();}// Poke user activity for this event.if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {pokeUserActivityLocked(mPendingEvent);}// Get ready to dispatch the event.resetANRTimeoutsLocked();}case EventEntry::TYPE_MOTION: {done = dispatchMotionLocked(currentTime, typedEntry,&dropReason, nextWakeupTime);break;}if (done) {if (dropReason != DROP_REASON_NOT_DROPPED) {dropInboundEventLocked(mPendingEvent, dropReason);}mLastDropReason = dropReason;releasePendingEventLocked();*nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately}
}

##3.InboundQueue

InputDispatcher 执行 notifyKey 的时候,会将 Input 事件封装后放到 InboundQueue 中,后续 InputDispatcher 循环处理 Input 事件的时候,就是从 InboundQueue 取出事件然后做处理

4.OutboundQueue

Outbound 意思是出站,这里的 OutboundQueue 指的是要被 App 拿去处理的事件队列,每一个 App(Connection) 都对应有一个 OutboundQueue ,从 InboundQueue 那一节的图来看,事件会先进入 InboundQueue ,然后被 InputDIspatcher 派发到各个 App 的 OutboundQueue

5.WaitQueue

当 InputDispatcher 将 Input 事件分发出去之后,将 DispatchEntry 从 outboundQueue 中取出来放到 WaitQueue 中,当 publish 出去的事件被处理完成(finished),InputManagerService 就会从应用中得到一个回复,此时就会取出 WaitQueue 中的事件,从 Systrace 中看就是对应 App 的 WaitQueue 减少

如果主线程发生卡顿,那么 Input 事件没有及时被消耗,也会在 WaitQueue 这里体现出来,如下图:

##6.整体逻辑

图来自 Gityuan 博客

四、Input 刷新与 Vsync

Input 的刷新取决于触摸屏的采样,目前比较多的屏幕采样率是 120Hz 和 160Hz ,对应就是 8ms 采样一次或者 6.25ms 采样一次,我们来看一下其在 Systrace 上的展示

可以看到上图中, InputReader 每隔 6.25ms 就可以读上来一个数据,交给 InputDispatcher 去分发给 App ,那么是不是屏幕采样率越高越好呢?也不一定,比如上面那张图,虽然 InputReader 每隔 6.25ms 就可以读上来一个数据给 InputDispatcher 去分发给 App ,但是从 WaitQueue 的表现来看,应用并没有消耗这个 Input 事件,这是为什么呢?

原因在于应用消耗 Input 事件的时机是 Vsync 信号来了之后,刷新率为 60Hz 的屏幕,一般系统也是 60 fps ,也就是说两个 Vsync 的间隔在 16.6ms ,这期间如果有两个或者三个 Input 事件,那么必然有一个或者两个要被抛弃掉,只拿最新的那个。也就是说:

  1. 在屏幕刷新率和系统 FPS 都是 60 的时候,盲目提高触摸屏的采样率,是没有太大的效果的,反而有可能出现上面图中那样,有的 Vsync 周期中有两个 Input 事件,而有的 Vsync 周期中有三个 Input 事件,这样造成事件不均匀,可能会使 UI 产生抖动
  2. 在屏幕刷新率和系统 FPS 都是 60 的时候,使用 120Hz 采样率的触摸屏就可以了
  3. 如果在屏幕刷新率和系统 FPS 都是 90 的时候 ,那么 120Hz 采样率的触摸屏显然不够用了,这时候应该采用 180Hz 采样率的屏幕

五、Input 调试信息

Dumpsys Input 主要是 Debug 用,我们也可以来看一下其中的一些关键信息,到时候遇到了问题也可以从这里面找 , 其命令如下:

adb shell dumpsys input

其中的输出比较多,我们终点截取 Device 信息、InputReader、InputDispatcher 三段来看就可以了

1.Device 信息

主要是目前连接上的 Device 信息,下面摘取的是 touch 相关的

  3: main_touchClasses: 0x00000015Path: /dev/input/event6Enabled: trueDescriptor: 4055b8a032ccf50ef66dbe2ff99f3b2474e9eab5Location: main_touch/input0ControllerNumber: 0UniqueId: Identifier: bus=0x0000, vendor=0xbeef, product=0xdead, version=0x28bbKeyLayoutFile: /system/usr/keylayout/main_touch.klKeyCharacterMapFile: /system/usr/keychars/Generic.kcmConfigurationFile: HaveKeyboardLayoutOverlay: false

##2.Input Reader 状态

InputReader 这里就是当前 Input 事件的一些展示

 Device 3: main_touchGeneration: 24IsExternal: falseHasMic:     falseSources: 0x00005103KeyboardType: 1Motion Ranges:X: source=0x00005002, min=0.000, max=1079.000, flat=0.000, fuzz=0.000, resolution=0.000Y: source=0x00005002, min=0.000, max=2231.000, flat=0.000, fuzz=0.000, resolution=0.000PRESSURE: source=0x00005002, min=0.000, max=1.000, flat=0.000, fuzz=0.000, resolution=0.000SIZE: source=0x00005002, min=0.000, max=1.000, flat=0.000, fuzz=0.000, resolution=0.000TOUCH_MAJOR: source=0x00005002, min=0.000, max=2479.561, flat=0.000, fuzz=0.000, resolution=0.000TOUCH_MINOR: source=0x00005002, min=0.000, max=2479.561, flat=0.000, fuzz=0.000, resolution=0.000TOOL_MAJOR: source=0x00005002, min=0.000, max=2479.561, flat=0.000, fuzz=0.000, resolution=0.000TOOL_MINOR: source=0x00005002, min=0.000, max=2479.561, flat=0.000, fuzz=0.000, resolution=0.000Keyboard Input Mapper:Parameters:HasAssociatedDisplay: falseOrientationAware: falseHandlesKeyRepeat: falseKeyboardType: 1Orientation: 0KeyDowns: 0 keys currently downMetaState: 0x0DownTime: 521271703875000Touch Input Mapper (mode - direct):Parameters:GestureMode: multi-touchDeviceType: touchScreenAssociatedDisplay: hasAssociatedDisplay=true, isExternal=false, displayId=''OrientationAware: trueRaw Touch Axes:X: min=0, max=1080, flat=0, fuzz=0, resolution=0Y: min=0, max=2232, flat=0, fuzz=0, resolution=0Pressure: min=0, max=127, flat=0, fuzz=0, resolution=0TouchMajor: min=0, max=512, flat=0, fuzz=0, resolution=0TouchMinor: unknown rangeToolMajor: unknown rangeToolMinor: unknown rangeOrientation: unknown rangeDistance: unknown rangeTiltX: unknown rangeTiltY: unknown rangeTrackingId: min=0, max=65535, flat=0, fuzz=0, resolution=0Slot: min=0, max=20, flat=0, fuzz=0, resolution=0Calibration:touch.size.calibration: geometrictouch.pressure.calibration: physicaltouch.orientation.calibration: nonetouch.distance.calibration: nonetouch.coverage.calibration: noneAffine Transformation:X scale: 1.000X ymix: 0.000X offset: 0.000Y xmix: 0.000Y scale: 1.000Y offset: 0.000Viewport: displayId=0, orientation=0, logicalFrame=[0, 0, 1080, 2232], physicalFrame=[0, 0, 1080, 2232], deviceSize=[1080, 2232]SurfaceWidth: 1080pxSurfaceHeight: 2232pxSurfaceLeft: 0SurfaceTop: 0PhysicalWidth: 1080pxPhysicalHeight: 2232pxPhysicalLeft: 0PhysicalTop: 0SurfaceOrientation: 0Translation and Scaling Factors:XTranslate: 0.000YTranslate: 0.000XScale: 0.999YScale: 1.000XPrecision: 1.001YPrecision: 1.000GeometricScale: 0.999PressureScale: 0.008SizeScale: 0.002OrientationScale: 0.000DistanceScale: 0.000HaveTilt: falseTiltXCenter: 0.000TiltXScale: 0.000TiltYCenter: 0.000TiltYScale: 0.000Last Raw Button State: 0x00000000Last Raw Touch: pointerCount=1[0]: id=0, x=660, y=1338, pressure=44, touchMajor=44, touchMinor=44, toolMajor=0, toolMinor=0, orientation=0, tiltX=0, tiltY=0, distance=0, toolType=1, isHovering=falseLast Cooked Button State: 0x00000000Last Cooked Touch: pointerCount=1[0]: id=0, x=659.389, y=1337.401, pressure=0.346, touchMajor=43.970, touchMinor=43.970, toolMajor=43.970, toolMinor=43.970, orientation=0.000, tilt=0.000, distance=0.000, toolType=1, isHovering=falseStylus Fusion:ExternalStylusConnected: falseExternal Stylus ID: -1External Stylus Data Timeout: 9223372036854775807External Stylus State:When: 9223372036854775807Pressure: 0.000000Button State: 0x00000000Tool Type: 0

##3.InputDispatcher 状态

InputDispatch 这里的重要信息主要包括

  1. FocusedApplication :当前获取焦点的应用
  2. FocusedWindow : 当前获取焦点的窗口
  3. TouchStatesByDisplay
  4. Windows :所有的 Window
  5. MonitoringChannels :Window 对应的 Channel
  6. Connections :所有的连接
  7. AppSwitch: not pending
  8. Configuration
Input Dispatcher State:DispatchEnabled: 1DispatchFrozen: 0FocusedApplication: name='AppWindowToken{ac6ec28 token=Token{a38a4b ActivityRecord{7230f1a u0 com.meizu.flyme.launcher/.Launcher t13}}}', dispatchingTimeout=5000.000msFocusedWindow: name='Window{3c007ad u0 com.meizu.flyme.launcher/com.meizu.flyme.launcher.Launcher}'TouchStatesByDisplay:0: down=true, split=true, deviceId=3, source=0x00005002Windows:0: name='Window{3c007ad u0 com.meizu.flyme.launcher/com.meizu.flyme.launcher.Launcher}', pointerIds=0x80000000, targetFlags=0x1051: name='Window{8cb8f7 u0 com.android.systemui.ImageWallpaper}', pointerIds=0x0, targetFlags=0x4102Windows:2: name='Window{ba2fc6b u0 NavigationBar}', displayId=0, paused=false, hasFocus=false, hasWallpaper=false, visible=true, canReceiveKeys=false, flags=0x21840068, type=0x000007e3, layer=0, frame=[0,2136][1080,2232], scale=1.000000, touchableRegion=[0,2136][1080,2232], inputFeatures=0x00000000, ownerPid=26514, ownerUid=10033, dispatchingTimeout=5000.000ms3: name='Window{72b7776 u0 StatusBar}', displayId=0, paused=false, hasFocus=false, hasWallpaper=false, visible=true, canReceiveKeys=false, flags=0x81840048, type=0x000007d0, layer=0, frame=[0,0][1080,84], scale=1.000000, touchableRegion=[0,0][1080,84], inputFeatures=0x00000000, ownerPid=26514, ownerUid=10033, dispatchingTimeout=5000.000ms9: name='Window{3c007ad u0 com.meizu.flyme.launcher/com.meizu.flyme.launcher.Launcher}', displayId=0, paused=false, hasFocus=true, hasWallpaper=true, visible=true, canReceiveKeys=true, flags=0x81910120, type=0x00000001, layer=0, frame=[0,0][1080,2232], scale=1.000000, touchableRegion=[0,0][1080,2232], inputFeatures=0x00000000, ownerPid=27619, ownerUid=10021, dispatchingTimeout=5000.000msMonitoringChannels:0: 'WindowManager (server)'RecentQueue: length=10MotionEvent(deviceId=3, source=0x00005002, action=MOVE, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, edgeFlags=0x00000000, xPrecision=1.0, yPrecision=1.0, displayId=0, pointers=[0: (524.5, 1306.4)]), policyFlags=0x62000000, age=61.2msMotionEvent(deviceId=3, source=0x00005002, action=MOVE, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, edgeFlags=0x00000000, xPrecision=1.0, yPrecision=1.0, displayId=0, pointers=[0: (543.5, 1309.4)]), policyFlags=0x62000000, age=54.7msPendingEvent: <none>InboundQueue: <empty>ReplacedKeys: <empty>Connections:0: channelName='WindowManager (server)', windowName='monitor', status=NORMAL, monitor=true, inputPublisherBlocked=falseOutboundQueue: <empty>WaitQueue: <empty>5: channelName='72b7776 StatusBar (server)', windowName='Window{72b7776 u0 StatusBar}', status=NORMAL, monitor=false, inputPublisherBlocked=falseOutboundQueue: <empty>WaitQueue: <empty>6: channelName='ba2fc6b NavigationBar (server)', windowName='Window{ba2fc6b u0 NavigationBar}', status=NORMAL, monitor=false, inputPublisherBlocked=falseOutboundQueue: <empty>WaitQueue: <empty>12: channelName='3c007ad com.meizu.flyme.launcher/com.meizu.flyme.launcher.Launcher (server)', windowName='Window{3c007ad u0 com.meizu.flyme.launcher/com.meizu.flyme.launcher.Launcher}', status=NORMAL, monitor=false, inputPublisherBlocked=falseOutboundQueue: <empty>WaitQueue: length=3MotionEvent(deviceId=3, source=0x00005002, action=MOVE, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, edgeFlags=0x00000000, xPrecision=1.0, yPrecision=1.0, displayId=0, pointers=[0: (634.4, 1329.4)]), policyFlags=0x62000000, targetFlags=0x00000105, resolvedAction=2, age=17.4ms, wait=16.8msMotionEvent(deviceId=3, source=0x00005002, action=MOVE, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, edgeFlags=0x00000000, xPrecision=1.0, yPrecision=1.0, displayId=0, pointers=[0: (647.4, 1333.4)]), policyFlags=0x62000000, targetFlags=0x00000105, resolvedAction=2, age=11.1ms, wait=10.4msMotionEvent(deviceId=3, source=0x00005002, action=MOVE, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, edgeFlags=0x00000000, xPrecision=1.0, yPrecision=1.0, displayId=0, pointers=[0: (659.4, 1337.4)]), policyFlags=0x62000000, targetFlags=0x00000105, resolvedAction=2, age=5.2ms, wait=4.6msAppSwitch: not pendingConfiguration:KeyRepeatDelay: 50.0msKeyRepeatTimeout: 500.0ms

原文链接:https://www.androidperformance.com/2019/11/04/Android-Systrace-Input/

至此,本篇已结束。转载网络的文章,小编觉得很优秀,欢迎点击阅读原文,支持原创作者,如有侵权,恳请联系小编删除,欢迎您的建议与指正。同时期待您的关注,感谢您的阅读,谢谢!

Systrace之 input 解读相关推荐

  1. Systrace6 Input 解读

    一 概述 本文是 Systrace 系列文章的第六篇,主要是对 Systrace 中的 Input 进行简单介绍,介绍其 Input 的流程: Systrace 中 Input 信息的体现 ,以及如何 ...

  2. Systrace 之 Vsync 解读

    和你一起终身学习,这里是程序员 Android 本篇文章主要介绍 Android 开发中的部分知识点,通过阅读本篇文章,您将收获以下内容: 一.Vsync 概述 二.Android 图形数据流向 三. ...

  3. Android Systrace 基础知识(10) - Binder 和锁竞争解读

    本文是 Systrace 系列文章的第十篇,主要是对 Systrace 中的 Binder 和锁信息进行简单介绍,简单介绍了 Binder 的情况,介绍了 Systrace 中 Binder 通信的表 ...

  4. Android Systrace 基础知识(9)-MainThread 和 RenderThread 解读

    本文是 Systrace 系列文章的第九篇,主要是是介绍 Android App 中的 MainThread 和 RenderThread,也就是大家熟悉的「主线程」和「渲染线程」.文章会从 Syst ...

  5. Systrace 流畅性实战 2 :案例分析: MIUI 桌面滑动卡顿分析

    当我们说 流畅度 的时候,我们说的是什么?不同的人对流畅性(卡顿掉帧)有不同的理解,对卡顿阈值也有不同的感知,所以有必要在开始这个系列文章之前,先把涉及到的内容说清楚,防止出现不同的理解,也方便大家带 ...

  6. 记录一个android性能优化宝藏级总结

    发现一个android性能优化文章宝藏级总结,太赞了,感谢大佬的无私奉献总结,防止丢失,在此记录一下 传送门 复制一些目录,增加一些篇幅{嘻嘻} 优化心得和经验 抖音 Android 性能优化系列:启 ...

  7. Android 性能优化必知必会

    做了这么久性能相关的工作,也接触了不少模块,说实话要做好性能这一块,真心不容易.为什么这么说? 是因为需要接触的知识实在是太多了, Android 是一个整体,牵一发而动全身,不是说只懂一个模块就可以 ...

  8. Android 性能优化必知必会(2020-5-16)

    做了这么久性能相关的工作,也接触了不少模块,说实话要做好性能这一块,真心不容易.为什么这么说? 是因为需要接触的知识实在是太多了, Android 是一个整体,牵一发而动全身,不是说只懂一个模块就可以 ...

  9. andoird TV 优化学习笔记

    文章目录 1. 崩溃优化 2. 内存优化 2.x 内存优化工具 2.x 查看内存的相关命令 参考资料 3. 卡顿优化 3.1 基础知识 3.2 Andorid 卡顿排查工具 3.3 可视化方法 3.4 ...

最新文章

  1. linux进程间通讯-信号
  2. 2018年首单!基石资本成功发行了双创债 募集3.1亿元
  3. oracle 增加一个新分区,oracle 11g 新增分区
  4. Struts2文件上传超出配置大小的解决办法
  5. 如何快速调整图片亮度和对比度?
  6. 发电厂与变电站高低压工程设计
  7. RS232串口接线图
  8. Zoj 3527 Shinryaku! Kero Musume (DP_章鱼图上的树形DP)
  9. debian8文件服务器,debian 8 下部署开发环境
  10. 视频截帧:javacv实现视频截帧功能
  11. C语言【微项目01】—电话号码管理系统(文件操作实现)【2021-06-29】
  12. 创建DAO模式的步骤
  13. GitLab用户在组中有五种角色权限说明
  14. Go Flag包使用及解析
  15. 【实例】使用 PHPExcel 读取excel 文件
  16. IMAX B6充电器使用说明
  17. 格式化输出(占位符%)
  18. Microsoft修复工具
  19. 设计分享|单片机花样流水灯
  20. UVA 1589 Xiangqi——模拟

热门文章

  1. 大学生日常生活安排与规划
  2. Xcode7与Xcode8及Xcode8.1之间兼容的那些事(持续更新)
  3. 游戏开发中的人工智能(五):以势函数实现移动
  4. echarts柱状图数据显示
  5. 高斯消元 卡我常数(shit)
  6. 计算机技术在本专业的应用与探索3000字,【计算机专业论文】移动学习在计算机专业教学中的应用(共3000字)...
  7. 新一代光学工程仿真软件RED MPC支持的功能
  8. UG NX 8.5有限元分析入门与实例精讲(PPT、视频、模型)
  9. Java游戏开发组件LGame简易测试版发布(版本号 0 1 5)
  10. HTML5+CSS3+JS制作动态背景登录界面