当前位置:首页 » 安卓系统 » androidview初始化

androidview初始化

发布时间: 2023-01-24 10:44:27

① 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”);

热点内容
安卓手机怎么设定背景墙 发布:2025-05-19 18:29:40 浏览:1001
androidstudio断点调试 发布:2025-05-19 18:20:23 浏览:766
abaqus如何配置证书 发布:2025-05-19 18:19:38 浏览:583
美食的密码是多少 发布:2025-05-19 18:18:58 浏览:72
买合资车选哪个品牌好配置高 发布:2025-05-19 18:01:51 浏览:49
pythonc回调 发布:2025-05-19 17:55:50 浏览:268
苹果如何把通讯录转移到安卓手机 发布:2025-05-19 17:49:13 浏览:255
开机设密码源码 发布:2025-05-19 17:47:20 浏览:885
死锁检测算法 发布:2025-05-19 17:45:17 浏览:109
sql查询列 发布:2025-05-19 17:38:16 浏览:279