Android宝典|View必考知识点总结

Android宝典|View必考知识点总结,第1张

我们知道,Activity 是在 ActivityThread 的 performLaunchActivity 中进行创建的,在创建完成之后就会调用其 attach 方法,它是先于 onCreate、onStart、onResume 等生命周期函数的,因此将 attach 方法作为这篇文章主线的开头:

attach() 方法就是 new 一个 PhoneWindow 并且关联 WindowManager。

接下来就到了 onCreate 方法:

这一步就是把我们的布局文件解析成 View 塞到 DecorView 的一个 id 为 Ridcontent 的 ContentView 中,DecorView 本身是一个 FrameLayout,它还承载了 StatusBar、NavigationBar 。

然后在 handleResumeActivity 中,通过 WindowManager 的 addView 方法把 DecorView 添加进去,实际实现是 WindowManagerImpl 的 addView 方法,它里面再通过 WindowManagerGlobal 的实例去 addView 的,在它里面就会 new 一个 ViewRootImpl,也就是说最后是把 DecorView 传给了 ViewRootImpl 的 setView 方法。ViewRootImpl 是 DecorView 的管理者,它负责 View 树的测量、布局、绘制,以及通过 Choreographer 来控制 View 的刷新。

WMS 是所有 Window 窗口的管理员,负责 Window 的添加和删除、Surface 的管理和事件派发等等,因此每一个 Activity 中的 PhoneWindow 对象如果需要显示等 *** 作,就必须要与 WMS 交互才能进行。

在 ViewRootImpl 的 setView 方法中,会调用 requestLayout,并且通过 WindowSession 的 addToDisplay 与 WMS 进行交互。WMS 会为每一个 Window 关联一个 WindowStatus。

SurfaceFlinger 主要是进行 Layer 的合成和渲染。

在 WindowStatus 中,会创建 SurfaceSession,SurfaceSession 会在 Native 层构造一个 SurfaceComposerClient 对象,它是应用程序与 SurfaceFlinger 沟通的桥梁。

经过步骤四和步骤五之后,ViewRootImpl 与 WMS、SurfaceFlinger 都已经建立起连接,但此时 View 还没显示出来,我们知道,所有的 UI 最终都要通过 Surface 来显示,那么 Surface 是什么时候创建的呢?

这就要回到前面所说的 ViewRootImpl 的 requestLayout 方法了,首先会 checkThread 检查是否是主线程,然后调用 scheduleTraversals 方法,scheduleTraversals 方法会先设置同步屏障,然后通过 Choreographer 类在下一帧到来时去执行 doTraversal 方法。简单来说,Choreographer 内部会接受来自 SurfaceFlinger 发出的 Vsync 垂直同步信号,这个信号周期一般是 16ms 左右。doTraversal 方法首先会先移除同步屏障,然后 performTraversals 真正进行 View 的绘制流程,即调用 performMeasure、performLayout、performDraw。不过在它们之前,会先调用 relayoutWindow 通过 WindowSession 与 WMS 进行交互,即把 Java 层创建的 Surface 与 Native 层的 Surface 关联起来。

接下来就是正式绘制 View 了,从 performTraversals 开始,Measure、Layout、Draw 三步走。

第一步是获取 DecorView 的宽高的 MeasureSpec 然后执行 performMeasure 流程。MeasureSpec 简单来说就是一个 int 值,高 2 位表示测量模式,低 30 位用来表示大小。策略模式有三种,EXACTLY、AT_MOST、UNSPECIFIED。EXACTLY 对应为 match_parent 和具体数值的情况,表示父容器已经确定 View 的大小;AT_MOST 对应 wrap_content,表示父容器规定 View 最大只能是 SpecSize;UNSPECIFIED 表示不限定测量模式,父容器不对 View 做任何限制,这种适用于系统内部。接着说,performMeasure 中会去调用 DecorView 的 measure 方法,这个是 View 里面的方法并且是 final 的,它里面会把参数透传给 onMeasure 方法,这个方法是可以重写的,也就是我们可以干预 View 的测量过程。在 onMeasure 中,会通过 getDefaultSize 获取到宽高的默认值,然后调用 setMeasureDimension 将获取的值进行设置。在 getDefaultSize 中,无论是 EXACTLY 还是 AT_MOST,都会返回 MeasureSpec 中的大小,这个 SpecSize 就是测量后的最终结果。至于 UNSPECIFIED 的情况,则会返回一个建议的最小值,这个值和子元素设置的最小值以及它的背景大小有关。从这个默认实现来看,如果我们自定义一个 View 不重写它的 onMeasure 方法,那么 warp_content 和 match_parent 一样。所以 DecorView 重写了 onMeasure 函数,它本身是一个 FrameLayout,所以最后也会调用到 FrameLayout 的 onMeasure 函数,作为一个 ViewGroup,都会遍历子 View 并调用子 View 的 measure 方法。这样便实现了层层递归调用到了每个子 View 的 onMeasure 方法进行测量。

第二步是执行 performLayout 的流程,也就是调用到 DecorView 的 layout 方法,也就是 View 里面的方法,如果 View 大小发生变化,则会回调 onSizeChanged 方法,如果 View 状态发生变化,则会回调 onLayout 方法,这个方法在 View 中是空实现,因此需要看 DecorView 的父容器 FrameLayout 的 onLayout 方法,这个方法就是遍历子 View 调用其 layout 方法进行布局,子 View 的 layout 方法被调用的时候,它的 onLayout 方法又会被调用,这样就布局完了所有的 View。

第三步就是 performDraw 方法了,里面会调用 drawSoftware 方法,这个方法需要先通过 mSurface lockCanvas 获取一个 Canvas 对象,作为参数传给 DecorView 的 draw 方法。这个方法调用的是 View 的 draw 方法,先绘制 View 背景,然后绘制 View 的内容,如果有子 View 则会调用子 View 的 draw 方法,层层递归调用,最终完成绘制。

完成这三步之后,会在 ActivityThread 的 handleResumeActivity 最后调用 Activity 的 makeVisible,这个方法就是将 DecorView 设置为可见状态。

>

可以的,左右滑动的控件就是viewPager,viewPager可以设置当前界面的。

具体设置代码:

viewPagersetCurrentItem(2); 表示转跳第三页,注意viewPager是从0开始的。

viewPagersetOffscreenPageLimit(4); 表示四个界面来回切换不会重新创建。

viewPagergetCurrentItem();表示获取当前是第几页。

serveice是一般后台执行的,所以理论上无法显示view,只能在activity显示这个view的时候,调用这个service,或者service执行的时候显示某个activity

       PowerManagerService是负责管理、协调设备电源管理的系统服务之一,设备常见功能如亮灭屏、亮度调节、低电量模式、保持CPU唤醒等,都会通过PMS的协调和处理。其继承自SystemService,因此具有SystemService子类的共性:具有生命周期方法,由SystemServer启动、注册到系统服务中,通过Binder和其他组件进行交互等。

       和SystemService的其他子类一样,PMS由SystemServer通过反射的方式启动,看一下PMS的构造方法:

       通过上面可以看到,在构造方法中首先创建了一个HandlerThread;其次获取了两个Suspend锁,SuspendBlocker是一种锁机制,只用于系统内部,上层申请的Wakelock锁在PMS中都会反映为SuspendBlocker锁。这里获取的两个Suspend锁在申请Wakelock时会用到;最后调用了native方法,这几个方法会通过JNI层调用到HAL层。

       在该方法中,首先对该服务进行Binder注册和本地注册,当进行Binder注册后,在其他模块中就可以通过Binder机制获取其实例,同理,当进行本地注册后,只有在System进程才能获取到其实例;最后设置Watchdog的监听。

       在这个方法中,设置mBootCompleted为true,表示启动完成,将mDirty置位,mDirty是一个二进制的标记位,用来表示电源状态哪一部分发生了改变,通过对其进行置位(| *** 作)、清零(~ *** 作),得到二进制数各个位的值(0或1),进行不同的处理 ,然后执行updatePowerStateLocked()方法,这是整个PMS中最重要的方法,这块会在下文中具体功能中进行详细分析。

        该方法内主要有5个重要功能

a获取各类本地服务和远程服务,如屏保(DreamMangerService)、窗口(PhoneWindowManager)、电池状态监听服务(BatteryService)等服务,用于进行交互;

b注册用于和其他Sytem交互的广播;

c调用updateSettingsLocked()方法更新Settings中值的变化;

d调用readConfigurationLocked()方法读取配置文件中的默认值;

e注册SettingsObserver监听;

       当通过系统设置等进行屏幕亮度调节时,会调用到PMS内部的updatePowerStateLocked(),通过该方法进行接下来的逻辑执行,最终通过HAL层来设置屏幕亮度,本文基于 Android 81 版本进行分析:

       当亮度调节时,处理逻辑主要是在phase 2,即updateDisplayPowerStateLocked(dirtyPhase2),一起看一下该方法:

       该方法内部主要做了两件事:

a封装DisplayPowerRequest实例mDisplayPowerRequest,包括通过

getDesiredScreenPolicyLocked()获取policy,此处为DisplayPowerRequest POLICY_BRIGHT ;还有screenBrightness等;

b调用DisplayManagerInternal的requestPowerState(),上步封装的DisplayPowerRequest作为参数传入,mDisplayManagerInternal是在systemReady()内部获取的;

       DisplayManagerInternal内部的requestPowerState为抽象方法,因此会调用到实现类中,具体是在DisplayManagerService内部的LocalService,看一下具体实现:

       最终会调用到DisplayPowerController内部的requestPowerState()方法:

       在该方法内部先将传入的DisplayPowerRequest对象赋值给mPendingRequestLocked,后续会使用;接着执行sendUpdatePowerStateLocked(),在该方法内部会发送异步消息 MSG_UPDATE_POWER_STATE ,对应处理方法为updatePowerState();

       该方法内部主要干了6件事:

a将要更新的DisplayPowerRequest实例赋值给mPowerRequest;

b如果为首次执行的话,需要执行initialize()来执行初始化 *** 作,创建DisplayPowerState实例mPowerState、RampAnimator实例mScreenBrightnessRampAnimator等;

       c根据mPowerRequestpolicy来更新state,此处为Display STATE_ON ;

d执行animateScreenStateChange(state, performScreenOffTransition)来更新DisplayPowerState;

e根据策略进行选择,最终确定要更新的brightness值;

f执行animateScreenBrightness(brightness,slowChange mBrightnessRampRateSlow : mBrightnessRampRateFast)来进行更新;

       可以看到,在animateTo()内部会执行mPropertysetValue(mObject, target),mProperty对应的是

DisplayPowerState SCREEN_BRIGHTNESS, mObject对应的是mPowerState,都是在DisplayPowerController内部的initialize()内部传入的,接下来就进入了DisplayPowerState内部了 ;

       scheduleScreenUpdate()会执行到postScreenUpdateThreadSafe(),然后会post mScreenUpdateRunnable;

       可以看到,在执行setState()后,最终会执行mBlankerrequestDisplayState(state, backlight),mBlanker是DisplayBlanker实例,初始化是在DisplayPowerController构造方法内,在DisplayPowerController内部的initialize()内部传入的,根据调用关系会追溯到DisplayMangerService的LocalService的initPowerManagerment()方法,该方法是在PowerManagerService的systemReady()方法内执行的;

       由于传入的state为Display STATE_ON ,执行到requestGlobalDisplayStateInternal(state, brightness),根据调用关系最终会执行到updateDisplayStateLocked()获取Runnable实例;

       根据调用关系看到,会通过DisplayDevice的requestDisplayStateLocked()来获取到Runnable;

在DisplayManagerService的onStart()内发送消息来执行registerDisplayAdapterLocked(new LocalDisplayAdapter())加载 BUILT_IN_DISPLAY_IDS_TO_SCAN对应 的LocalDisplayDevice(继承DisplayDevice),加载成功后存入mDisplayDevices列表内进行管理;

在创建LocalDisplayDeivce时会通过lightsgetLight(LightsManager LIGHT_ID_BACKLIGHT )获取mBackLight,对应的是LightsService内部的LightImpl继承light;

在通过LocalDisplayDevice获取到Runnable后接着执行run(),会执行到setDisplayBrightness();

       在setDisplayBrightness()内部会执行到mBacklightsetBrightness(brightness),具体实现逻辑是在LightsService内部;

       最终通过native方法调用到HAL层对屏幕的背光进行设置。

       不同的调节方式对应不同的入口及判断逻辑,只分析调用入口及涉及的关键策略来选择最终目标brightness值逻辑;

       系统设置在调节屏幕亮度时,会实时更新Settings SCREEN_BRIGHTNESS 值,DisplayPowerController在监听到该值变化后会执行handleSettingsChanged()方法:

       监听到变化后,先通过getScreenBrightnessSetting()来获取到当前值,然后赋值给mPendingScreenBrightnessSetting,接下来执行sendUpdatePowerState(),最终执行updatePowerState()方法:

        结论:系统设置会根据mCurrentScreenBrightnessSetting的值来设置对应的目标brightness值;

       快速设置来滑动进度条来调节屏幕亮度时,会调用PowerManagerService的setTemporaryScreenBrightnessSettingOverride()方法:

       跟随调用关系,调用到DisplayPowerController的setTemporaryBrightness()方法,发送 MSG_SET_TEMPORARY_BRIGHTNESS 消息,设置mTemporaryScreenBrightness为传入的值,然后执行updatePowerState():

结论:快速设置会根据mTemporaryScreenBrightness的值来设置对应的目标brightness值;

在视频播放界面可以通过左侧上下滑动来调节屏幕亮度,具体实现方式如下:

       Window是activity对应的Window,通过getWindow()可以得到,设置WindowManagerLayoutParams内部的screenBrightness后执行windowsetAttributes(lp)可以来调节屏幕亮度;

Window执行setAttributes()会执行到Activity的onWindowAttributesChanged()方法,调用到WindowManagerGlobal的updateViewLayout()最终调用到ViewRootImpl的setLayoutParams来执行重新绘制;

绘制流程就不陈述了,最终会执行到RootWindowContainer内部的performSurfacePlacement()方法,接下来通过handleNotObscuredLocked()获取到对应WindowState的wmAttrsscreenBrightness赋值给mScreenBrightness,然后在performSurfacePlacement()发送 SET_SCREEN_BRIGHTNESS_OVERRIDE消息, 最终通过WindowManagerService来调用到PowerManagerService内部的setScreenBrightnessOverrideFromWindowManager()方法:

       可以看到,在setScreenBrightnessOverrideFromWindowManagerInternal()内部会将brightness赋值给mScreenBrightnessOverrideFromWindowManager,然后执行updatePowerStateLocked(),先看

updateDisplayPowerStateLocked()方法:

       可以看到,screenBrightness会设置为mScreenBrightnessOverrideFromWindowManager,该值默认为-1,经过设置后screenBrightness>0,接着执行到DisplayPowerController的updatePowerState()方法:

结论:Window属性调节会根据mScreenBrightnessOverrideFromWindowManager的值来设置对应的目标brightness值;

       设置屏幕亮度时,主要是在DisplayPowerController内部的 updatePowerState() 来对目标screenBrightness进行设置,screenBrightness默认值为PowerManagerBRIGHTNESS_DEFAULT = -1,会根据DisplayPowerRequest及其他策略进行选择,顺序依次为:

aif (brightness < 0 && mPowerRequestscreenBrightness >= 0): 通过设置Window属性来进行调节(视频播放界面)

bif (mTemporaryScreenBrightness > 0): 通过快速设置滑动来进行调节

cif (brightness < 0) {brightness = clampScreenBrightness(mCurrentScreenBrightnessSetting)}: 通过系统设置滑动调节

       当系统设置内部设置了自动进入屏保时间后,如果在此段时间后满足进入条件,会进入屏保,该逻辑入口是在PMS内部的updatePowerStateLocked(),一起看一下:

       本流程只分析满足条件后进入屏保的整个过程,关于条件判断会在下一节中进行分析,先从updatePowerStateLocked()看起:

       在该方法内部首先循环执行了三个方法:

aupdateWakeLockSummaryLocked():根据mWakeLocks来计算得到mWakeLockSummary的值;

       bupdateUserActivitySummaryLocked():计算得到mUserActivitySummary以及何时进行下一次check;

       cupdateWakefulnessLocked():判断是否可以进入屏保及更新mWakefulness状态;

dupdateDreamLocked():根据当前状态选择是否进入屏保;

       当mWakefulness为 WAKEFULNESS_AWAKE 时,且isItBedTimeYetLocked(),即:副驾满足进入屏保的条件,看一下该方法的逻辑:

       当满足以上条件后,执行napNoUpdateLocked():

       设置mSandmanSummoned为true,更新当前mWakefulness状态 WAKEFULNESS_DREAMING ,最后返回true;

前面的循环中当结果返回true时,会执行下一次循环,重新计算mWakeLockSummary和mUserActivitySummary,最终返回false,执行break退出循环,执行updateDreamLocked();

       满足条件后执行scheduleSandmanLocked()来发送异步消息,执行handleSandman(),mDisplayReady是在updateDisplayPowerStateLocked()内部最终通过DisplayPowerController内部返回的;

       在handleSandman()先进行判断,满足条件后即startDreaming为true时,执行stopDream()、startDream(),接下来再执行wakeUpNoUpdateLocked(),一起看一下该方法的实现:

       将mWakefulness更新为 WAKEFULNESS_AWAKE ;然后再执行 userActivityNoUpdateLocked()来更新user activity相关信息,接着上面进行分析,执行startDream()进入屏保;

       在startDream()逻辑执行过程中首先获取到本地屏保实现的ComponentName,通过在core/res/res/values/configxml内进行配置:

       接下来最终通过DreamController的startDream()来启动;

       可以看到在startDream()内部通过bindService()来启动本地实现的屏保服务SevenDreamService;

以上就是关于Android宝典|View必考知识点总结全部的内容,包括:Android宝典|View必考知识点总结、如何解决ScrollView快速滚动时的闪烁问题、android点击跳到左右滑动 页面时 可以随意指定先显示第几个页面吗等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/10080703.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-05-05
下一篇2023-05-05

发表评论

登录后才能评论

评论列表(0条)

    保存