setContentView调用了window.setContentView方法,window是一个抽象类,在Android中,window只有一个实现类,phoneWindow
ActivityThread通过performLaunchActivity创建了Activity,Activity中的生命周期函数如onResume等实际上就是ActivityThread调用了Activity的各个方法而已
如onCreate,是ActivityThread调用了performCreate方法
attach方法是在onCreate方法之前调用的,在这个方法中,window完成了赋值
mWindow = new PhoneWindow(...)
DecorView是window中的顶层容器,用户的xml视图通过DecorView的addView方法被添加到了Decordview中,不同的设置对应不同的父布局xml,在用户不进行任何requestWindowFeature(如隐藏标题等)设置的情况下,最终用户的xml中会添加到screen_simple.xml中,具体是通过inflater.inflate的方式添加到了id为content的FrameLayout中去
复制代码
其他的比如requestWindowFeature(FEATURE_NO_TITLE)其实就是替换了顶层xml布局,甚至其中也包含常见的VISIBLE、GONE操作
screen_simple.xml
对于上图的解释如下:
每一个Activiy都有一个Window对象,它的具体实例是一个PhoneWindow,phoneWindow的最顶层容器是一个DecorView,DecorView是布局顶层容器
每一个Activity都有一个windowManger对象,它的具体实例是WindowMangerImpl,WindowMangerImpl的addView调用了WindowMangerGlobal对象的addView方法
WindowMangerGlobal是一个单例对象,是一个全局掌控者,addView的方法将用户的xml文件填充到了DecorView的布局中
所以实际上我们在setContentView(xml layout)上实际上还包含了多层父布局层级,自顶向下包含了
最顶层Window,包含DecorView对象
DecorView包含了statusBar、actionBar,以及一个id为content的FrameLayout(根据布局属性不同内部有差别)
xml layout最终被添加填充到了FrameLayout中
简单来讲,就是mWindow负责创建了窗体和布局容器DecorView,而mWindowManager负责将布局依附到布局容器DecorView中
除去中间的处理流程,其余的测量布局绘制流程基本上就是View的绘制流程measure、layout、draw
performLaunchActivity创建了Activity,执行Activity初始化,这个过程创建了一些对象,包括PhoneWindow,PhoneWindowManager等,然后会创建出DecorView,执行onCreate函数等
handleResumeActivity调用了onResume,由于这个时候还没有走到绘制路程,所以这时候不能获取view的宽高,之后会调用windowManager的addView方法,它调用了WindowManagerGloabal的addView方法,new创建了viewImpl对象,调用了setView方法,调用之后,会执行scheduleTraversals,会向主线程post一个callback
下一次进行轮询的时候,会执行performTraversals,这次的执行会调用decorView的measure和layout方法,第一次调用并不会马上绘制
然后在下次轮询的时候,再次执行performTraversals的时候,进行真正的绘制draw
CSDN _ Android中子线程真的不能更新UI吗?
onCreate{ //可以更新 thread{ tv_setview.text = "${Thread.currentThread().name}:${SystemClock.uptimeMillis()}" } //不可以更新 thread{ sleep(2000) tv_setview.text = "${Thread.currentThread().name}:${SystemClock.uptimeMillis()}" } } 复制代码
可以更新的原因是ViewRootImpl没有创建出来,不会触发checkThread方法
不可以更新的原因是睡了两秒之后ViewRootImpl被创建出来,在访问UI的时候,ViewRootImpl会去检查当前是哪个线程访问的UI,ViewRootImpl的checkThread方法
void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } }复制代码
如果不是主线程,那就会抛出如下异常:Only the original thread that created a view hierarchy can touch its views
如果想要在子线程中弹吐司,需要添加Looper代码,否则会报异常Can't toast on a thread that has not called Looper.prepare()
//子线程吐司不报错写法 thread{ Looper.prepare() Toast.makeText(this,"sss",0).show(); Looper.loop() }复制代码
主线程申请成功后子线程申请
tv_setview.setOnClickListener(View.OnClickListener { tv_setview.text = "Main" thread{ tv_setview.text = "${Thread.currentThread().name}:${SystemClock.uptimeMillis()}" } })复制代码
(补充解释: textview.text = "Main" 这一行代码会在主线程运行,这会在主线程正常的执行 requsetLayout 的整个流程,这样就完成了「申请」修改布局。
此时,在子线程立即调用 textview.text = "xx.." 这个代码就会因为它已经「申请」过 requestLayout 了,就不会层层往上调用 parent 的 requsetLayout() 方法,也就不会在子线程 触发 checkThread() 方法了!)
在子线程中创建ViewImpl
thread { Looper.prepare() val button = Button(this) windowManager.addView(button, WindowManager.LayoutParams()) button.setOnClickListener(OnClickListener { button.text = "${Thread.currentThread().name}:${SystemClock.uptimeMillis()}" }) Looper.loop() }复制代码
利用硬件加速机制绕开 requestLayout()
在硬件加速的支持下,如果控件只是进行了 invalidate() ,而没有触发 requestLayout() 是 不会触发 ViewRootImpl#checkThread() 的。
SurfaceView
Android 中有一个控件 SurfaceView ,它可以通过 holder 获得 Canvas 对象,可以直接在子线 程中更新 UI。
View.post()只有在View attachedToWindow的时候才会立即执行。
在attach之前调用的话,低于7.0时可以自己new一个Handler或者自定义View重写dispatchAttachedToWindow自己储存pendingTask并在attached之后执行。
与Activity类似,最终顶层布局也是DecorView,默认布局是alert_dialog.xml
其他情况如下,核心类是AlertController
AlertController(...){ ... //获取不同布局在安卓系统中对应的id mAlertDialogLayout = a.getResourceId(com.android.internal.R.styleable.AlertDialog_layout, com.android.internal.R.layout.alert_dialog); mButtonPanelSideLayout = a.getResourceId( com.android.internal.R.styleable.AlertDialog_buttonPanelSideLayout, 0); mListLayout = a.getResourceId( com.android.internal.R.styleable.AlertDialog_listLayout, com.android.internal.R.layout.select_dialog); mMultiChoiceItemLayout = a.getResourceId( com.android.internal.R.styleable.AlertDialog_multiChoiceItemLayout, com.android.internal.R.layout.select_dialog_multichoice); mSingleChoiceItemLayout = a.getResourceId( com.android.internal.R.styleable.AlertDialog_singleChoiceItemLayout, com.android.internal.R.layout.select_dialog_singlechoice); mListItemLayout = a.getResourceId( com.android.internal.R.styleable.AlertDialog_listItemLayout, com.android.internal.R.layout.select_dialog_item); ... }
156-1688-1988
80931912(售前)2580705673(售后)
地址:湖南省长沙市万科金域华府二期15栋A902
10年建站服务经验
服务27家集团公司
服务超2000家中小企业
B2C营销型网站建设供应商
多项大型项目开发经验
营销型网站建设专家
完备的项目流程管理体系
网页设计与网站开发技术并重
COPYRIGHT © 2013-现在 XIANGQU ALL RIGHTS RESERVED