androidview初始化
① Android 重学系列 View的绘制流程(六) 硬件渲染(上)
本文开始聊聊Android中的硬件渲染。如果跟着我的文章顺序,从SF进程到App进程的绘制流程一直阅读,我们到这里已经有了一定的基础,可以试着进行横向比对如Chrome浏览器渲染流程,看看软件渲染,硬件渲染,SF合成都做了什么程度的优化。
先让我们回顾一下负责硬件渲染的主体对象ThreadedRenderer在整个绘制流程中做了哪几个步骤。
在硬件渲染的过程中,有一个很核心的对象RenderNode,作为每一个View绘制的节点对象。
当每一次进行准备进行绘制的时候,都会雷打不动执行如下三个步骤:
如果遇到什么问题欢迎来到 https://www.jianshu.com/p/c84bfa909810 下进行讨论
实际上整个硬件渲染的设计还是比较庞大。因此本文先聊聊ThreadedRender整个体系中主要对象的构造以及相关的原理。
首先来认识下面几个重要的对象有一个大体的印象。
在java层中面向Framework中,只有这么多,下面是一一映射的简图。
能看到实际上RenderNode也会跟着View 树的构建同时一起构建整个显示层级。也是因此ThreadedRender也能以RenderNode为线索构建出一套和软件渲染一样的渲染流程。
仅仅这样?如果只是这么简单,知道我习惯的都知道,我喜欢把相关总结写在最后。如果把总揽写在正文开头是因为设计比较繁多。因为我们如果以流水线的形式进行剖析容易造成迷失细节的困境。
让我继续介绍一下,在硬件渲染中native层的核心对象。
如下是一个思维导图:
有这么一个大体印象后,就不容易迷失在源码中。我们先来把这些对象的实例化以及上面列举的ThreadedRenderer在ViewRootImpl中执行行为的顺序和大家来聊聊其原理,先来看看ThreadedRenderer的实例化。
当发现mSurfaceHolder为空的时候会调用如下函数:
而这个方法则调用如下的方法对ThreadedRenderer进行创建:
文件:/ frameworks / base / core / java / android / view / ThreadedRenderer.java
能不能创建的了ThreadedRenderer则决定于全局配置。如果ro.kernel.qemu的配置为0,说明支持OpenGL 则可以直接返回true。如果qemu.gles为-1说明不支持OpenGL es返回false,只能使用软件渲染。如果设置了qemu.gles并大于0,才能打开硬件渲染。
我们能看到ThreadedRenderer在初始化,做了三件事情:
关键是看1-3点中ThreadRenderer都做了什么。
文件:/ frameworks / base / core / jni / android_view_ThreadedRenderer.cpp
能看到这里是直接实例化一个RootRenderNode对象,并把指针的地址直接返回。
能看到RootRenderNode继承了RenderNode对象,并且保存一个JavaVM也就是我们所说的Java虚拟机对象,一个java进程全局只有一个。同时通过getForThread方法,获取ThreadLocal中的Looper对象。这里实际上拿的就是UI线程的Looper。
在这个构造函数有一个mDisplayList十分重要,记住之后会频繁出现。接着来看看RenderNode的头文件:
文件:/ frameworks / base / libs / hwui / RenderNode.h
实际上我把几个重要的对象留下来:
文件:/ frameworks / base / core / java / android / view / RenderNode.java
能看到很简单,就是包裹一个native层的RenderNode返回一个Java层对应的对象开放Java层的操作API。
能看到这个过程生成了两个对象:
这个对象实际上让RenderProxy持有一个创建动画上下文的工厂。RenderProxy可以通过ContextFactoryImpl为每一个RenderNode创建一个动画执行对象的上下文AnimationContextBridge。
文件:/ frameworks / base / libs / hwui / renderthread / RenderProxy.cpp
在这里有几个十分重要的对象被实例化,当然这几个对象在聊TextureView有聊过( SurfaceView和TextureView 源码浅析 ):
我们依次看看他们初始化都做了什么。
文件:/ frameworks / base / libs / hwui / renderthread / RenderThread.cpp
能看到其实就是简单的调用RenderThread的构造函数进行实例化,并且返回对象的指针。
RenderThread是一个线程对象。先来看看其头文件继承的对象:
文件:/ frameworks / base / libs / hwui / renderthread / RenderThread.h
其中RenderThread的中进行排队处理的任务队列实际上是来自ThreadBase的WorkQueue对象。
文件:/ frameworks / base / libs / hwui / thread / ThreadBase.h
ThreadBase则是继承于Thread对象。当调用start方法时候其实就是调用Thread的run方法启动线程。
另一个更加关键的对象,就是实例化一个Looper对象到WorkQueue中。而直接实例化Looper实际上就是新建一个Looper。但是这个Looper并没有获取当先线程的Looper,这个Looper做什么的呢?下文就会揭晓。
WorkQueue把一个Looper的方法指针设置到其中,其作用可能是完成了某一件任务后唤醒Looper继续工作。
而start方法会启动Thread的run方法。而run方法最终会走到threadLoop方法中,至于是怎么走进来的,之后有机会会解剖虚拟机的源码线程篇章进行讲解。
在threadloop中关键的步骤有如下四个:
在这个过程中创建了几个核心对象:
另一个核心的方法就是,这个方法为WorkQueue的Looper注册了监听:
能看到在这个Looper中注册了对DisplayEventReceiver的监听,也就是Vsync信号的监听,回调方法为displayEventReceiverCallback。
我们暂时先对RenderThread的方法探索到这里,我们稍后继续看看回调后的逻辑。
文件:/ frameworks / base / libs / hwui / thread / ThreadBase.h
能看到这里的逻辑很简单实际上就是调用Looper的pollOnce方法,阻塞Looper中的循环,直到Vsync的信号到来才会继续往下执行。详细的可以阅读我写的 Handler与相关系统调用的剖析 系列文章。
文件:/ frameworks / base / libs / hwui / thread / ThreadBase.h
实际上调用的是WorkQueue的process方法。
文件:/ frameworks / base / libs / hwui / thread / WorkQueue.h
能看到这个过程中很简单,几乎和Message的loop的逻辑一致。如果Looper的阻塞打开了,则首先找到预计执行时间比当前时刻都大的WorkItem。并且从mWorkQueue移除,最后添加到toProcess中,并且执行每一个WorkItem的work方法。而每一个WorkItem其实就是通过从某一个压入方法添加到mWorkQueue中。
到这里,我们就明白了RenderThread中是如何消费渲染任务的。那么这些渲染任务又是哪里诞生呢?
上文聊到了在RenderThread中的Looper会监听Vsync信号,当信号回调后将会执行下面的回调。
能看到这个方法的核心实际上就是调用drainDisplayEventQueue方法,对ui渲染任务队列进行处理。
能到在这里mVsyncRequested设置为false,且mFrameCallbackTaskPending将会设置为true,并且调用queue的postAt的方法执行ui渲染方法。
还记得queue实际是是指WorkQueue,而WorkQueue的postAt方法实际实现如下:
/ frameworks / base / libs / hwui / thread / WorkQueue.h
情景带入,当一个Vsync信号达到Looper的监听者,此时就会通过WorkQueue的drainDisplayEventQueue 压入一个任务到队列中。
每一个默认的任务都是执行dispatchFrameCallback方法。这里的判断mWorkQueue中是否存在比当前时间更迟的时刻,并返回这个WorkItem。如果这个对象在头部needsWakeup为true,说明可以进行唤醒了。而mWakeFunc这个方法指针就是上面传下来:
把阻塞的Looper唤醒。当唤醒后就继续执行WorkQueue的process方法。也就是执行dispatchFrameCallbacks方法。
在这里执行了两个事情:
先添加到集合中,在上面提到过的threadLoop中,会执行如下逻辑:
如果大小不为0,则的把中的IFrameCallback全部迁移到mFrameCallbacks中。
而这个方法什么时候调用呢?稍后就会介绍。其实这部分的逻辑在TextureView的解析中提到过。
接下来将会初始化一个重要对象:
这个对象名字叫做画布的上下文,具体是什么上下文呢?我们现在就来看看其实例化方法。
文件:/ frameworks / base / libs / hwui / renderthread / CanvasContext.cpp
文件:/ device / generic / goldfish / init.ranchu.rc
在init.rc中默认是opengl,那么我们就来看看下面的逻辑:
首先实例化一个OpenGLPipeline管道,接着OpenGLPipeline作为参数实例化CanvasContext。
文件:/ frameworks / base / libs / hwui / renderthread / OpenGLPipeline.cpp
能看到在OpenGLPipeline中,实际上就是存储了RenderThread对象,以及RenderThread中的mEglManager。透过OpenGLPipeline来控制mEglManager进而进一步操作OpenGL。
做了如下操作:
文件:/ frameworks / base / libs / hwui / renderstate / RenderState.cpp
文件:/ frameworks / base / libs / hwui / renderthread / DrawFrameTask.cpp
实际上就是保存这三对象RenderThread;CanvasContext;RenderNode。
文件:/ frameworks / base / core / jni / android_view_ThreadedRenderer.cpp
能看到实际上就是调用RenderProxy的setName方法给当前硬件渲染对象设置名字。
文件:/ frameworks / base / libs / hwui / renderthread / RenderProxy.cpp
能看到在setName方法中,实际上就是调用RenderThread的WorkQueue,把一个任务队列设置进去,并且调用runSync执行。
能看到这个方法实际上也是调用post执行排队执行任务,不同的是,这里使用了线程的Future方式,阻塞了执行,等待CanvasContext的setName工作完毕。
② Android中View的创建过程
我们知道在onCreate里面View还是没有测绘完成的。那么什么时候测绘完成了?答案是onResume。
通过查看源码 我们可以看到在onCreate方法里面调用了getWindow()方法然后在将我们的页面塞到这个window里面。这个window也就是PhonwWindow.
那PhoneWindow是什么时候被创建的?
这就引出了Activity的创建流程。
那Activity是怎么被创建的呢?
由于Activity是一个组件他是由系统使用 ActivityThread 方法去创建的。
现在我来分析下:
先来到ActivityThread类的handleLaunchActivity方法。
可以看到他去调用了Activity的performCreate方法。
现在我们终于看到onCreate方法被调用了。
这里还有个重点,在performLaunchActivity里面去调用Activity的onCreate方法之前还去做了一件很重要的事情,这个事情在第3224行:调用了Activity的attach方法。
现在跟到Activity的attach方法:找到了我们一直找的PhoneWindow的创建。
③ android的textview怎么初始化
你现在是把它创建出来了,但是没有加到activity上,让它显示到哪儿呢。
最简单的操作是调用setContentView(textView), 这样,这一整个Acitivty就只显示这个TextView了,但实际开发中肯定不这么干。
一般是把一个View加到一个Layout上。每一个Layout比如Linerlayout什么的,都是一个GroupView,都有一个addView(View)的方式。
如果你一整个Activity都不想用find。。。那就初始一个Layout 加到 Ac上,加给layout 加view
public void onCreate(Buddle c) {
super.onCreate(c)
LinearLyaout layout = new LinearLayout(this);
setContentView(layout);
TextView tv = new TextView(this);
.......你的那堆代码
layout.addView(tv);
}
④ Android View知识
1, View是除了Android四大组件外,最常用的东西
2,什么是View:
View是android中所有控件的父类,比如TextView,LinearLayout等等
其中LinearLayout继承自控件组ViewGroup,当然ViewGroup也是继承自View
3,View的位置
top:左上角纵坐标
left:左上角横坐标
right:右下角横坐标
bottom:右下角纵坐标
如下图:
4,view的MotionEvent和TouchSlop
4.1MotionEvent:
ACTION_DOWN:手指接触屏幕
ACTION_MOVE:手指在屏幕上滑动
ACTION_UP:手指离开屏幕。
4.2TouchSlop
处理滑动时的过滤条件,简单来说就是,手指在屏幕上的一次操作算不算滑动。
系统默认值:ViewConfiguration.get(context).getScaledTouchSlop()
5,getX()getY()和getRawX()和getRawY()
前两者相对于父控件View 后两者相对于手机屏幕
6,VelocityTracker,GestureDetector,Scroller
6.1VelocityTracker:滑动速度,在view的ontouch事件中,查看速度
6.2 GestureDetector:手势判断,比如长按,点击,双击等,很少用,可以用 ontouch事件来代替
6.3Scroller:弹性滑动对象,实现view的位置改变等
7,原始滑动方式
7.1:ScrollerTo和Scroller By()
实现简单 但是只能滑动view里面的子元素
7.2:改变view参数
实现复杂,但是如果view有交互,这种方式比较好
7.3:动画
适用于没有交互的,或者动画复杂的view的滑动
8View的事件分发:
8.1:Activity-window-View
8.2:view中是从父到子,也就是从外到内,都不处理,返回给最顶级
8.3:ViewGroup默认不拦截任何事件,默认返回false
8.4:分发方法:dispatchTouchEvent,OnInterceptTouchEvent,OnTouchEvent
dispatchTouchEvent:分发
OnInterceptTouchEvent:拦截
OnTouchEvent:处理点击事件
⑤ android:X5WebView首次初始化X5内核耗时,会产生卡顿现象的解决办法
集成腾讯的X5,一般都是在application中进行初始化,不过有一个现象就是第一次启动都睡有一小会产生了UI卡顿,一开始利用IntentService进行后台线程进行初始化,但还是会产生卡顿现象,不过官方在X5 SDK的v3.6版本后添加了一个多进程的service= 设置开启优化方案。
如下做法:
第一种 多进程方案: 设置开启优化方案// 在调用TBS初始化、创建WebView之前进行如下配置,以开启优化方案HashMapmap = new HashMap();map.put(TbsCoreSettings.TBS_SETTINGS_USE_SPEEDY_CLASSLOADER, true);QbSdk.initTbsSettings(map);b)
增加Service声明 :在AndroidManifest.xml中增加内核首次加载时优化Service声明; 该Service仅在TBS内核首次Dex加载时触发并执行dex2oat任务,任务完成后自动结束;
<service android:name="com.tencent.smtt.export.external.DexClassLoaderProviderService"
android:label="dexopt"
android:process=":dexopt"/>
第二种 多进程方案:仅Android 5.1+生效)
1、// 在调用TBS初始化、创建WebView之前进行如下配置,以开启优化方案
HashMapmap = new HashMap();
map.put(TbsCoreSettings.TBS_SETTINGS_USE_SPEEDY_CLASSLOADER,true);
QbSdk.initTbsSettings(map);
2、)
多线程方案策略配置// 在调用TBS初始化、创建WebView之前进行如下配置,以开启优化方案
HashMapmap = new HashMap();
// 配置不使用多进程策略,即该方案仅在Android 5.1+系统上生效。
map.put(TbsCoreSettings.TBS_SETTINGS_USE_DEXLOADER_SERVICE, false);
QbSdk.initTbsSettings(map);
⑥ 【Android】Window/DecorView/ViewRootImpl
根据 Activity启动流程 ,当流程进行到 ActivityThread.performLaunchActivity 的时候,会创建Activity实例,并调用其 attach 方法。在 attach 方法中,会创建Window的实例。
也就是说,在Activity实例创建之初,Window就已经创建好了。
DecorView在第一次调用 Window.getDecorView 的时候被创建。PhoneWindow类的 getDecorView 方法实现如下:
一般情况下,在 onCreate 回调中调用了 setContentView 方法,DecorView就被初始化了。
调用链为:
AppcompatActivity.setContentView ->
AppCompatDelegateImpl.setContentView ->
AppCompatDelegateImpl.ensureSubDecor ->
AppCompatDelegateImpl.createSubDecor ->
PhoneWindow.getDecorView ->
PhoneWindow.installDecor
最终,在 PhoneWindow.installDecor 方法中,DecorView被初始化:
另外,如果不调用 setContentView ,DecorView同样也会在 Activity.performCreate 方法中较后的地方创建。
ViewRootImpl是在WindowManagerGlobal的 addView 方法中被初始化的,并且也是在这里与DecorView进行绑定,成为DecorView的parent。调用链可以追溯到 ActivityThread.handleResumeActivity 中,在 performResumeActivity 调用之后,ViewRootImpl被创建。
也就是说,ViewRootImpl是在 onResume 回调之后才进行初始化的。这可以在 onResume 中打印 getWindow().getDecorView().getParent() 证实。
在 二 中知道了, handleResumeActivity 方法最终会调用 WindowManagerGlobal.addView 方法,在这里 ViewRootImpl 被创建,并且通过 setView 方法绑定DecorView,这些都发生在onResume之后。
在ViewRootImpl的 setView 方法中,又会调用 requestLayout ,在这里就会进行这个View树的第一次测绘。具体的方式是通过 scheleTraversals 方法向 Choreographer 发送一个预定的消息,并在下一次屏幕刷新的时候调用 doTraversal → performTraversals 方法进行ViewTree的测量、布局和绘制。
从这一点我们可以知道,在Activity的 onResume 或之前的生命流程中调用View的 getMesuredWidth 或者 getWidth 都会返回0,因为这个时候还没有开始测量。
当View树还没有被测绘的时候 ,View.post会将这个Runnable发送给内部的一个消息队列(不是系统的消息队列)。这个消息队列中保存的Runnable会在下一次 performTraversals 的时候被执行;调用链为:
ViewRootImpl.performTraversals →
DecorView.dispatchAttachedToWindow →
... →
View.dispatchAttachedToWindow →
executeActions
最终在 executeActions 方法中,Runnable对象被发送给 ViewRootImpl的内部Handler 执行。也就是说,当这个Runnable被执行的时候,已经至少经过一次测绘了,所以可以正确的获取到View的宽高;也说明了为什么通过 View.post 发送的Runnable会在主线中执行。
当View树已经被测绘过了 , View.post 就会直接通过内部的 mHandler 发送消息,这个Handler是在attach的过程中跟着 AttachInfo 一起传递给View的,实质上就是ViewRootImpl的内部Handler。
⑦ android viewpager怎么初始化数据
使用ViewPager的setCurrentItem (int item) 方法设置其初始显示的页面,
不是在其数据适配器中,而是在完成数据适配后设置。
如viewPager.setAdapter(adapter);
viewPager.setCurrentItem(3);
⑧ Android-View的创建从xml到View
基于android 29API
android中的UI主要是通过xml文件编写,从xml文件到View是通过LayoutInflater。
LayoutInflater通过inflater方法从xml文件转换成View,通过createViewFromTag创建View,在createViewFromTag方法中会调用tryCreateView方法进行三次拦截最终调用系统方法生成View。
其中开发者可以通过mFactory2(通过setFactory2进行赋值 )和mFactory(通过setFactory进行赋值 ) 2个对象对View创建进行拦截,通过Activity的
onCreateView方法对mPrivateFactory对象进行拦截
拦截顺序为:mFactory2--mFactory--mPrivateFactory--系统生成View
当tryCreateView方法没有返回一个View,那么就会由系统生成View
⑨ android 在adapter类里面怎么初始化控件
在getView方法内加载动态布局view(就是你想显示的layout),然后获取动态布局view中的组件即可。以下举个例子:
12View view = LayoutInflater.from(context).inflate(R.layout.testlayout , null);TextView text = (TextView)view.findViewById(R.id.testTextView);
如上就可以初始化布局testlayout中id为testTextView的(TextView)组件。
⑩ android 如何初始化LayoutInflater的View里面的文本框的值
settext(“xxx”);