DecorView如何添加到Window
来源:互联网 发布:江苏运时数据招聘 编辑:程序博客网 时间:2024/06/11 21:04
前几篇文章讲了
从setContentView开始,了解view的加载过程
LayoutInflater 是怎么把xml添加到decorview?
今天来看一下
DecorView如何添加到Window
1,首先要了解Activity的启动过程
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) { ···省略若干行 Activity a = performLaunchActivity(r, customIntent); ···省略若干行 }
Activity是通过ActivityThead启动的,首先会走handleLaunchActivity方法,之后会走 performLaunchActivity(r, customIntent)方法,如下
performLaunchActivity(xxxxxx){Activity activity = null; try { java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); ···省略若干行 if (activity != null) { Context appContext = createBaseContextForActivity(r, activity); CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()); Configuration config = new Configuration(mCompatConfiguration); if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity " + r.activityInfo.name + " with config " + config); //调用attach方法 activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor); ···省略若干行}
看performLaunchActivity方法,在这个方法里会通过反射来拿到我们当前的Activity,然后会调用attach方法,之后再看attach方法;
final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, //在这里初始化了PhoneWindow。。 mWindow = new PhoneWindow(this); mWindow.setCallback(this);
再回到performLaunchActivity方法,当attch完成之后会走callActivityOnCreate方法。
performLaunchActivity。。省略若干行if (r.isPersistable()) { //斤如callActivityOnCreate方法,它会执行activity.performCreate方法 mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnCreate(activity, r.state); }
也就是说Activity的生命周期,执行顺序,调用流程都会在ActivityThread这个类中执行,例如callActivityOnRestoreInstanceState,callActivityOnPostCreate等。
我们再回到handleLaunchActivity,当它走完performLaunchActivity方法之后,会调用handleResumeActivity,callActivityOnPause等方法
如下,
{//点进去最终会是callActivityOnResume这个方法来调用OnResume方法handleResumeActivity(r.token, false, r.isForward, !r.activity.mFinished && !r.startsNotResumed); if (!r.activity.mFinished && r.startsNotResumed) { try { r.activity.mCalled = false; mInstrumentation.callActivityOnPause(r.activity);}if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); //获取到DecorView View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (a.mVisibleFromClient) { a.mWindowAdded = true; //把decor添加到WindowManager中 wm.addView(decor, l); }
从上面可以看到当走完handleResumeActivity方法后,通过View decor = r.window.getDecorView();获取到DecorView并设置为隐藏。然后获取WindowManager,然后把decor添加到WindowManager中;
之后我们再进入WindowManager中,发现它是一个接口。通过搜索发现它的实现类为WindowManagerImpl,上面代码中的 wm.addView(decor, l) 这个方法就是走的WindowManagerImpl中的addView方法。
我们进WindowManagerImpl这个类看看者方法
@Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); //这里其实是WindowManagerGlobal 实例的addView方法 //这里的view就是我们传进来的DecorView mGlobal.addView(view, params, mDisplay, mParentWindow); }
进入 mGlobal.addView方法
public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) { 。。。省略 //ViewRootImpl 这个类很重要,绘制流程基本都是在这个类完成的。 ViewRootImpl root; View panelParentView = null; 。。。省略 //这里new一个ViewRootImpl,绘制流程基本都是在这个类完成的 root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); //这是三个List,把他们添加到一个list中去,也就是把他们都保存到这个类中,这个类是管理root ,和decorView的。 mViews.add(view); mRoots.add(root); mParams.add(wparams); } // do this last because it fires off messages to start doing things try { //这里调用setView把我们的decorView和一些参数传进去 root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { }
//以上就是Activity的启动过程中生成phonewindow,并加载DecorView,之后发起绘制的基本过程。
进一步深入了解,我们再进入setView()方法,首先把传进去的DecorView赋值给mView,然后调用mWindowSession.addToDisplay这个方法把我们的DecorView显示出来。
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { //把传进去的DecorView赋值给mView mView = view; 。。省略 者其中是一些动画的处理和输入法的处理等 // Schedule the first layout -before- adding to the window // manager, to make sure we do the relayout before receiving // any other events from the system. requestLayout();//这个很常见 。。省略 try { //下面是发起一个跨进程的通信,通过方法名可以猜测,是用来显示的 mOrigWindowType = mWindowAttributes.type; mAttachInfo.mRecomputeGlobalAttributes = true; collectViewAttributes(); res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mInputChannel); } catch (RemoteException e) { } } } ...省略 //关键代码 //DecorView把WindowManagerImpl设置为顶层ViewGroup view.assignParent(this);
我们再进入这个方法view.assignParent(this);这个方法是View中的方法
//就送给我们的View分配Parentvoid assignParent(ViewParent parent) { if (mParent == null) { mParent = parent; } else if (parent == null) { mParent = null; } else { throw new RuntimeException("view " + this + " being added, but" + " it already has a parent"); } }
讲一下requestLayout这个方法
首先,View.requestLayout—>其实是调用View类的mParent .requestLayout–>最终会到DecorView的requestLayout,在这个里面会调用ViewRootImpl的requestLayout。为什么会最终会调用DecorView的requestLayout,是因为在setView()方法的时候利用assignParent(ViewParent parent)设置了DecorView的根部局。所以这个mParent 其实就是ViewRootImpl。 其次,走到ViewRootImpl.requestLayout后就会重新进行绘制流程,代码如下
@Override public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); //遍历操作 } }
scheduleTraversals()方法
void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } }
这里有一个线程,看一下mTraversalRunnable,它会不断的走 doTraversal()方法
final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } }
再看一下 doTraversal()方法,里面重要的是 performTraversals()方法,可以猜测这个就是发起重绘的方法。
void doTraversal() { 。。省略 performTraversals(); 。。省略 }
继续看 performTraversals()
private void performTraversals() { // 这个就是我们的DecorView final View host = mView; //...省略一些操作,大家可以通过变量名来猜测它是干什么的 //顶层视图DecorView所需要窗口的宽度和高度,因为我们必须要知道父容器的宽高才能测量子View的宽高。 int desiredWindowWidth; int desiredWindowHeight; mDisplay.getRealSize(size); desiredWindowWidth = size.x; desiredWindowHeight = size.y; else { //这这里的宽高是是手机屏幕的宽高,见下面代码 DisplayMetrics packageMetrics = mView.getContext().getResources().getDisplayMetrics(); desiredWindowWidth = packageMetrics.widthPixels; desiredWindowHeight = packageMetrics.heightPixels; } 。。。省略,大部分为初始化操作 // Ask host how big it wants to be //重点方法,通过注释可以猜测,这里是测量布局所占用空间。 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); 。。。省略 // performLayout(lp, desiredWindowWidth, desiredWindowHeight); 。。。省略// performDraw();}
进入 performMeasure方法看一看,它是会调用DecorView的measure方法,DecorView是继承View的,所以实际调用的是View的measure方法。所以这些测量,layout,draw都是再ViewRootImpl发起的。
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure"); try { mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } }
//以上就是Activity的启动过程中生成phonewindow,并加载DecorView,之后发起绘制的过程。
总结:DecorView如何添加到Window?
看下面绘制流程图,可以发现最终调用了ViewRootImpl.setView,在setview方法里调用了view.assignParent(this);,将Decorview的mParent设置成ViewRootImpl
这也就是为什么View调用requestLayout方法的时候最终会走到ViewRootImpl的requestLayout
这篇博客也帮大家找到了UI绘制流程的起始点是在生什么地方,也就是下面三个方法:///测量–performMeasure()// 摆放布局–performLayout()
// 绘制–performDraw()
- DecorView如何添加到Window
- DecorView如何添加到Window上
- Window、PhoneWindow与DecorView
- Window和DecorView
- window-phonewindow-decorView
- Android Window PhoneWindow DecorView
- 顶层视图DecorView添加到窗口的过程
- LayoutInflater 到底怎么把xml添加到decorview
- LayoutInflater 是怎么把xml添加到decorview?
- Activity的Window创建及DecorView的添加(Android开发艺术探索学习笔记)
- Window窗口布局 --- DecorView浅析
- Window窗口布局 --- DecorView浅析
- DecorView与window的创建
- 补充说明Window、PhoneWindow与DecorView
- setContentView 与 Window、PhoneWindow及DecorView
- Activity、View、Window、DecorView的关系
- 补充说明Window、PhoneWindow与DecorView
- 很通俗易懂的概念Activity,Window,DecorView
- 写多线程去压测秒杀接口
- 淘淘商城系列——服务调用测试
- 初识Spring-MVC之最小配置运行Hello world的maven项目
- HTML元素类型
- 组合框ComBoBox
- DecorView如何添加到Window
- mysql存储过程
- BSS段、数据段、代码段、堆与栈
- 堆和栈的区别
- android对于window,windowManager窗口管理的理解
- Java中print和println的区别
- Android开发:分享给微信好友以出现闪退的问题
- 又是一道快慢指针与链表的结合题 Linked List Cycle II
- C++学习与基础算法专栏目录