Android源码分析:View的绘制流程

  • 本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布

    源码基于安卓8.0分析结果

  • View是何时开始绘制的?Activity走了onCreate方法吗?这篇文章就是从程序的入口ActivityThread入口程序,去解释View中的measure()方法View中的layoutView中的draw怎么开始调用的,非常有意思!虽然好多的技术文档,在半个月前已经做好了,这篇文章,对我自己来讲的话,是个很好的复习~~
  • 为了更好地阐述着这篇文章,我这里就直接抛出结论了,为啥会这样的,在下篇文章会讲到,这里就记住一点,在Activity onResume后,调用了View onAttachedToWindow 才会开始View measure Activity的生命周期和View的生命周期.jpg
  • 为什么会这样子?先看ActivityThread类里面有个内部private class H extends Handler这就是系统的Handler,具体分析请看Android源码分析(Handler机制),里面有个case RESUME_ACTIVITY,获取焦点

    1
    2
    3
    4
    5
    6
    7
    case RESUME_ACTIVITY:
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
    SomeArgs args = (SomeArgs) msg.obj;
    handleResumeActivity((IBinder) args.arg1, true, args.argi1 != 0, true,
    args.argi3, "RESUME_ACTIVITY");
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    break;
  • handleResumeActivity()方法,这里只截取了关键的代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
     final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
    ActivityClientRecord r = mActivities.get(token);
    if (!checkAndUpdateLifecycleSeq(seq, r, "resumeActivity")) {
    return;
    }
    mSomeActivitiesChanged = true;
    //在这里执行performResumeActivity的方法中会执行Activity的onResume()方法
    r = performResumeActivity(token, clearHide, reason);
    if (r != null) {
    final Activity a = r.activity;

    if (localLOGV) Slog.v(
    TAG, "Resume " + r + " started activity: " +
    a.mStartedActivity + ", hideForNow: " + r.hideForNow
    + ", finished: " + a.mFinished);
    final int forwardBit = isForward ?
    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
    boolean willBeVisible = !a.mStartedActivity;
    if (!willBeVisible) {
    try {
    willBeVisible = ActivityManager.getService().willActivityBeVisible(
    a.getActivityToken());
    } catch (RemoteException e) {
    throw e.rethrowFromSystemServer();
    }
    }
    //PhoneWindow在这里获取到
    if (r.window == null && !a.mFinished && willBeVisible) {
    r.window = r.activity.getWindow();
    //DecorView在这里获取到
    View decor = r.window.getDecorView();
    decor.setVisibility(View.INVISIBLE);
    //获取ViewManager对象,在这里getWindowManager()实质上获取的是ViewManager的子类对象WindowManager
    // TODO: 2018/5/24 WindowManager
    ViewManager wm = a.getWindowManager();
    WindowManager.LayoutParams l = r.window.getAttributes();
    a.mDecor = decor;
    l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
    l.softInputMode |= forwardBit;
    if (r.mPreserveWindow) {
    a.mWindowAdded = true;
    r.mPreserveWindow = false;
    // Normally the ViewRoot sets up callbacks with the Activity
    // in addView->ViewRootImpl#setView. If we are instead reusing
    // the decor view we have to notify the view root that the
    // callbacks may have changed.
    //获取ViewRootImpl对象
    ViewRootImpl impl = decor.getViewRootImpl();
    if (impl != null) {
    impl.notifyChildRebuilt();
    }
    }
    }
    }
  • 第一点分析得出performResumeActivity()肯定先于wm.addView(decor, l);执行的~这也是为啥我们 Activity先获取焦点了,才去绘制View

  • performResumeActivity(),可以得出调用的是r.activity.performResume();
    performResumeActivity
  • 关于r.activity.performResume();;这里也可以,看出,在activity 中的fragment获取焦点要晚于activity,虽然这是常识。注意这个方法mInstrumentation.callActivityOnResume(this);;然后才会执行onPostResume;这也就是为什么,Activity先获取焦点,后执行onPostResume();

    1
    2
    3
    4
    5
    6
    7
    8
    9
    final void performResume() {
    performRestart();
    mInstrumentation.callActivityOnResume(this);
    mCalled = false;
    //这里也可以,看出,在activity 中的fragment获取焦点要晚于activity,虽然这是常识
    mFragments.dispatchResume();
    mFragments.execPendingActions();
    onPostResume();
    }
  • 关注这个方法mInstrumentation.callActivityOnResume(this);果然不出所料,这里执行了activity.onResume();;
    callActivityOnResume.png

  • 既然在上面知道了,activity 获取焦点,会在上面执行,那么View的绘制就会在下面的函数中进行。

    • 1、获取PhoneWindow; activity.getWindow(),Window类的唯一子类
      • 2、获取window.getDecorView();DecorView,PhoneWindow的内部类,private final class DecorView extends FrameLayout ,安卓的事件分发和它密切相关Android源码分析(事件传递),也就是从Activity 传递到 ViewGroup的过程~~
      • 3、获取ViewManager wm = a.getWindowManager();,其实也就是activity.getWindowManager(),也就是获取的是ViewManager的子类对象WindowManager,这里的知道WindowManager其实也是一个接口.
        image.png
    • 4、 wm.addView(decor, l);,也就是到这里来了,WindowManager.addView(decor,l).
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      //PhoneWindow在这里获取到
      if (r.window == null && !a.mFinished && willBeVisible) {
      r.window = r.activity.getWindow();
      //DecorView在这里获取到
      View decor = r.window.getDecorView();
      decor.setVisibility(View.INVISIBLE);
      //获取ViewManager对象,在这里getWindowManager()实质上获取的是ViewManager的子类对象WindowManager
      // TODO: 2018/5/24 WindowManager
      ViewManager wm = a.getWindowManager();
      WindowManager.LayoutParams l = r.window.getAttributes();
      a.mDecor = decor;
      l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
      l.softInputMode |= forwardBit;
      if (r.mPreserveWindow) {
      a.mWindowAdded = true;
      r.mPreserveWindow = false;
      // Normally the ViewRoot sets up callbacks with the Activity
      // in addView->ViewRootImpl#setView. If we are instead reusing
      // the decor view we have to notify the view root that the
      // callbacks may have changed.
      //获取ViewRootImpl对象
      ViewRootImpl impl = decor.getViewRootImpl();
      if (impl != null) {
      impl.notifyChildRebuilt();
      }
      if (!a.mWindowAdded) {
      a.mWindowAdded = true;
      //在这里WindowManager将DecorView添加到PhoneWindow中
      wm.addView(decor, l);
      }
      }
  • 分析到这里来了,会通过WindowManager.addView(decor,l).我们需要去找WindowManager的实现。WindowManagerImpl;

    1
    2
    3
    4
    5
    6
    7
    8
    public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
    }
  • 去寻找WindowManagerGlobaladdView()方法。这里有个单利模式,在源码好多地方使用的单利模式都是这样,并没有进行双重判断,在老牌的图片加载框架ImageLoader也是这样获取单利对象,如果想了解更多设计模式的姿势,可以看这片文章二十三种设计模式.

    1
    2
    3
    4
    5
    6
    7
    8
    public static WindowManagerGlobal getInstance() {
    synchronized (WindowManagerGlobal.class) {
    if (sDefaultWindowManager == null) {
    sDefaultWindowManager = new WindowManagerGlobal();
    }
    return sDefaultWindowManager;
    }
    }
  • 在这里!就是WindowManagerGlobal.addView()的关键的方法,我做了两个注释,一个是view.setLayoutParams(wparams);,这个方法非常有意思,最近在研究ViewGroup的源码,发现不论什么情况下,View或者是ViewGroup都会有两次测量,这里是根本的原因,我先给结论。
    api26:执行2次onMeasure、1次onLayout、1次onDraw。
    api25-24:执行2次onMeasure、2次onLayout、1次onDraw,
    api23-21:执行3次onMeasure、2次onLayout、1次onDraw,
    api19-16:执行2次onMeasure、2次onLayout、1次onDraw,
    API等级24:Android 7.0 Nougat
    API等级25:Android 7.1 Nougat
    API等级26:Android 8.0 Oreo
    API等级27:Android 8.1 Oreo
    后续我会做一篇文章详细解释下,为什么会这样,这里不过多的解释了,自提一句,非常有意思的代码!以前还会有两次的layout,说明谷歌也在优化安卓 framework。todo

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) {
    ...
    root = new ViewRootImpl(view.getContext(), display);
    //view setLLayoutParams()在这里
    view.setLayoutParams(wparams);
    try {
    // TODO: 2018/6/4 这里呢?就是ViewRootImpl 调用的setView的方法,就在这里
    root.setView(view, wparams, panelParentView);
    } catch (RuntimeException e) {
    // BadTokenException or InvalidDisplayException, clean up.
    if (index >= 0) {
    removeViewLocked(index, true);
    }
    throw e;
    }
  • ok,现在继续的关注这个方法ViewRootImpl.setView(view, wparams, panelParentView)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) { try {
    // TODO: 2018/6/4 这里传入的attrs 决定了View 或者是ViewGroup是否会onMeasure 两次
    mWindowAttributes.copyFrom(attrs);
    } catch (RemoteException e) {

    // TODO: 2018/5/24 就会调动这里的来
    unscheduleTraversals();
    } finally {
    if (restore) {
    attrs.restore();
    } if (res < WindowManagerGlobal.ADD_OKAY) {

    // TODO: 2018/5/24 就会调动这里的来
    unscheduleTraversals();}
    }
  • unscheduleTraversals(),没有Activity获取焦点的时候,这个方法肯定会执行

    1
    2
    3
    4
    5
    6
    7
    8
    void unscheduleTraversals() {
    if (mTraversalScheduled) {
    mTraversalScheduled = false;
    mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
    mChoreographer.removeCallbacks(
    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
    }
    }
  • 关注mTraversalRunnable对象

    1
    2
    3
    4
    5
    6
    final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
    doTraversal();
    }
    }
  • doTraversal()方法,Traversal翻译过来就是遍历的意思~~

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    // TODO: 2018/5/24 到这里来了 ----> Traversal 遍历
    void doTraversal() {
    if (mTraversalScheduled) {
    mTraversalScheduled = false;
    mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
    if (mProfile) {
    Debug.startMethodTracing("ViewAncestor");
    }
    performTraversals();
    if (mProfile) {
    Debug.stopMethodTracing();
    mProfile = false;
    }
    }
    }

performTraversals这里就是整个View绘制的开始,所有的绘制,都会从这里开始,虽然这个方法代码有点多,但是关键的地方我都做了注释,下面一步一步的分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
   /**
* 最终会调动到这里
* ViewRootImpl的performTraversals()方法完成具体的视图绘制流程
*/
private void performTraversals() {
// cache mView since it is used so much below...
//mView 就是DecorView根布局
final View host = mView;

if (DBG) {
System.out.println("======================================");
System.out.println("performTraversals");
host.debug();
}
//如果host=null 或者是mAdded=false 直接就return了
if (host == null || !mAdded)
return;
//是否正在遍历
mIsInTraversal = true;
//是否马上需要绘制View
mWillDrawSoon = true;
boolean windowSizeMayChange = false;
boolean newSurface = false;
boolean surfaceChanged = false;
/**
* 这个WindowMananger 这里标记了 是否是需要 onMeasure 两次 哈哈
*/
// TODO: 2018/6/4
WindowManager.LayoutParams lp = mWindowAttributes;
/*
顶层视图DecorView所需要的窗口的宽度和高度
*/
int desiredWindowWidth;
int desiredWindowHeight;

final int viewVisibility = getHostVisibility();
final boolean viewVisibilityChanged = !mFirst
&& (mViewVisibility != viewVisibility || mNewSurfaceNeeded);
final boolean viewUserVisibilityChanged = !mFirst &&
((mViewVisibility == View.VISIBLE) != (viewVisibility == View.VISIBLE));

WindowManager.LayoutParams params = null;
if (mWindowAttributesChanged) {
mWindowAttributesChanged = false;
surfaceChanged = true;
params = lp;
}
CompatibilityInfo compatibilityInfo =
mDisplay.getDisplayAdjustments().getCompatibilityInfo();
if (compatibilityInfo.supportsScreen() == mLastInCompatMode) {
params = lp;
mFullRedrawNeeded = true;
mLayoutRequested = true;
if (mLastInCompatMode) {
params.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
mLastInCompatMode = false;
} else {
params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
mLastInCompatMode = true;
}
}

mWindowAttributesChangesFlag = 0;

Rect frame = mWinFrame;
//在构造方法中mFirst已经设置为true,表示是否是第一次绘制DecorView
if (mFirst) {
mFullRedrawNeeded = true;
mLayoutRequested = true;

final Configuration config = mContext.getResources().getConfiguration();
// TODO: 2018/5/25 注意这个方法内部做了什么
/*
return lp.type == TYPE_STATUS_BAR_PANEL
|| lp.type == TYPE_INPUT_METHOD
|| lp.type == TYPE_VOLUME_OVERLAY;
*/
// 如果窗口的类型是有状态栏的,那么顶层视图DecorView所需要的窗口的宽度和高度
//就是除了状态栏
if (shouldUseDisplaySize(lp)) {
// NOTE -- system code, won't try to do compat mode.
Point size = new Point();
mDisplay.getRealSize(size);
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
} else {
//否者顶层视图DecorView所需要的窗口的宽度和高度就是整个屏幕的宽度
desiredWindowWidth = dipToPx(config.screenWidthDp);
desiredWindowHeight = dipToPx(config.screenHeightDp);
}

// We used to use the following condition to choose 32 bits drawing caches:
// PixelFormat.hasAlpha(lp.format) || lp.format == PixelFormat.RGBX_8888
// However, windows are now always 32 bits by default, so choose 32 bits
mAttachInfo.mUse32BitDrawingCache = true;
mAttachInfo.mHasWindowFocus = false;
mAttachInfo.mWindowVisibility = viewVisibility;
mAttachInfo.mRecomputeGlobalAttributes = false;
mLastConfigurationFromResources.setTo(config);
mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
// Set the layout direction if it has not been set before (inherit is the default)
if (mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) {
host.setLayoutDirection(config.getLayoutDirection());
}
host.dispatchAttachedToWindow(mAttachInfo, 0);
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
dispatchApplyInsets(host);
//Log.i(mTag, "Screen on initialized: " + attachInfo.mKeepScreenOn);

} else {
desiredWindowWidth = frame.width();
desiredWindowHeight = frame.height();
if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
if (DEBUG_ORIENTATION) Log.v(mTag, "View " + host + " resized to: " + frame);
mFullRedrawNeeded = true;
mLayoutRequested = true;
windowSizeMayChange = true;
}
}

if (viewVisibilityChanged) {
mAttachInfo.mWindowVisibility = viewVisibility;
host.dispatchWindowVisibilityChanged(viewVisibility);
if (viewUserVisibilityChanged) {
host.dispatchVisibilityAggregated(viewVisibility == View.VISIBLE);
}
if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
endDragResizing();
destroyHardwareResources();
}
if (viewVisibility == View.GONE) {
// After making a window gone, we will count it as being
// shown for the first time the next time it gets focus.
mHasHadWindowFocus = false;
}
}

// Non-visible windows can't hold accessibility focus.
if (mAttachInfo.mWindowVisibility != View.VISIBLE) {
host.clearAccessibilityFocus();
}

// Execute enqueued actions on every traversal in case a detached view enqueued an action
getRunQueue().executeActions(mAttachInfo.mHandler);

boolean insetsChanged = false;

boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
if (layoutRequested) {

final Resources res = mView.getContext().getResources();

if (mFirst) {
// make sure touch mode code executes by setting cached value
// to opposite of the added touch mode.
mAttachInfo.mInTouchMode = !mAddedTouchMode;
ensureTouchModeLocally(mAddedTouchMode);
} else {
if (!mPendingOverscanInsets.equals(mAttachInfo.mOverscanInsets)) {
insetsChanged = true;
}
if (!mPendingContentInsets.equals(mAttachInfo.mContentInsets)) {
insetsChanged = true;
}
if (!mPendingStableInsets.equals(mAttachInfo.mStableInsets)) {
insetsChanged = true;
}
if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) {
mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: "
+ mAttachInfo.mVisibleInsets);
}
if (!mPendingOutsets.equals(mAttachInfo.mOutsets)) {
insetsChanged = true;
}
if (mPendingAlwaysConsumeNavBar != mAttachInfo.mAlwaysConsumeNavBar) {
insetsChanged = true;
}
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
|| lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
windowSizeMayChange = true;

if (shouldUseDisplaySize(lp)) {
// NOTE -- system code, won't try to do compat mode.
Point size = new Point();
mDisplay.getRealSize(size);
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
} else {
Configuration config = res.getConfiguration();
desiredWindowWidth = dipToPx(config.screenWidthDp);
desiredWindowHeight = dipToPx(config.screenHeightDp);
}
}
}

// Ask host how big it wants to be
windowSizeMayChange |= measureHierarchy(host, lp, res,
desiredWindowWidth, desiredWindowHeight);
}

if (collectViewAttributes()) {
params = lp;
}
if (mAttachInfo.mForceReportNewAttributes) {
mAttachInfo.mForceReportNewAttributes = false;
params = lp;
}

if (mFirst || mAttachInfo.mViewVisibilityChanged) {
mAttachInfo.mViewVisibilityChanged = false;
int resizeMode = mSoftInputMode &
WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
// If we are in auto resize mode, then we need to determine
// what mode to use now.
if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
final int N = mAttachInfo.mScrollContainers.size();
for (int i=0; i<N; i++) {
if (mAttachInfo.mScrollContainers.get(i).isShown()) {
resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
}
}
if (resizeMode == 0) {
resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
}
if ((lp.softInputMode &
WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) != resizeMode) {
lp.softInputMode = (lp.softInputMode &
~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) |
resizeMode;
params = lp;
}
}
}

if (params != null) {
if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
if (!PixelFormat.formatHasAlpha(params.format)) {
params.format = PixelFormat.TRANSLUCENT;
}
}
mAttachInfo.mOverscanRequested = (params.flags
& WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN) != 0;
}

if (mApplyInsetsRequested) {
mApplyInsetsRequested = false;
mLastOverscanRequested = mAttachInfo.mOverscanRequested;
dispatchApplyInsets(host);
if (mLayoutRequested) {
// Short-circuit catching a new layout request here, so
// we don't need to go through two layout passes when things
// change due to fitting system windows, which can happen a lot.
windowSizeMayChange |= measureHierarchy(host, lp,
mView.getContext().getResources(),
desiredWindowWidth, desiredWindowHeight);
}
}

if (layoutRequested) {
// Clear this now, so that if anything requests a layout in the
// rest of this function we will catch it and re-run a full
// layout pass.
mLayoutRequested = false;
}

boolean windowShouldResize = layoutRequested && windowSizeMayChange
&& ((mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight())
|| (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT &&
frame.width() < desiredWindowWidth && frame.width() != mWidth)
|| (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT &&
frame.height() < desiredWindowHeight && frame.height() != mHeight));
windowShouldResize |= mDragResizing && mResizeMode == RESIZE_MODE_FREEFORM;

// If the activity was just relaunched, it might have unfrozen the task bounds (while
// relaunching), so we need to force a call into window manager to pick up the latest
// bounds.
windowShouldResize |= mActivityRelaunched;

// Determine whether to compute insets.
// If there are no inset listeners remaining then we may still need to compute
// insets in case the old insets were non-empty and must be reset.
final boolean computesInternalInsets =
mAttachInfo.mTreeObserver.hasComputeInternalInsetsListeners()
|| mAttachInfo.mHasNonEmptyGivenInternalInsets;

boolean insetsPending = false;
int relayoutResult = 0;
boolean updatedConfiguration = false;

final int surfaceGenerationId = mSurface.getGenerationId();

final boolean isViewVisible = viewVisibility == View.VISIBLE;
final boolean windowRelayoutWasForced = mForceNextWindowRelayout;
if (mFirst || windowShouldResize || insetsChanged ||
viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
mForceNextWindowRelayout = false;

if (isViewVisible) {
// If this window is giving internal insets to the window
// manager, and it is being added or changing its visibility,
// then we want to first give the window manager "fake"
// insets to cause it to effectively ignore the content of
// the window during layout. This avoids it briefly causing
// other windows to resize/move based on the raw frame of the
// window, waiting until we can finish laying out this window
// and get back to the window manager with the ultimately
// computed insets.
insetsPending = computesInternalInsets && (mFirst || viewVisibilityChanged);
}

if (mSurfaceHolder != null) {
mSurfaceHolder.mSurfaceLock.lock();
mDrawingAllowed = true;
}

boolean hwInitialized = false;
boolean contentInsetsChanged = false;
boolean hadSurface = mSurface.isValid();

try {
if (DEBUG_LAYOUT) {
Log.i(mTag, "host=w:" + host.getMeasuredWidth() + ", h:" +
host.getMeasuredHeight() + ", params=" + params);
}

if (mAttachInfo.mThreadedRenderer != null) {
// relayoutWindow may decide to destroy mSurface. As that decision
// happens in WindowManager service, we need to be defensive here
// and stop using the surface in case it gets destroyed.
if (mAttachInfo.mThreadedRenderer.pauseSurface(mSurface)) {
// Animations were running so we need to push a frame
// to resume them
mDirty.set(0, 0, mWidth, mHeight);
}
mChoreographer.mFrameInfo.addFlags(FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED);
}
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);

if (DEBUG_LAYOUT) Log.v(mTag, "relayout: frame=" + frame.toShortString()
+ " overscan=" + mPendingOverscanInsets.toShortString()
+ " content=" + mPendingContentInsets.toShortString()
+ " visible=" + mPendingVisibleInsets.toShortString()
+ " visible=" + mPendingStableInsets.toShortString()
+ " outsets=" + mPendingOutsets.toShortString()
+ " surface=" + mSurface);

final Configuration pendingMergedConfig =
mPendingMergedConfiguration.getMergedConfiguration();
if (pendingMergedConfig.seq != 0) {
if (DEBUG_CONFIGURATION) Log.v(mTag, "Visible with new config: "
+ pendingMergedConfig);
performConfigurationChange(mPendingMergedConfiguration, !mFirst,
INVALID_DISPLAY /* same display */);
pendingMergedConfig.seq = 0;
updatedConfiguration = true;
}

final boolean overscanInsetsChanged = !mPendingOverscanInsets.equals(
mAttachInfo.mOverscanInsets);
contentInsetsChanged = !mPendingContentInsets.equals(
mAttachInfo.mContentInsets);
final boolean visibleInsetsChanged = !mPendingVisibleInsets.equals(
mAttachInfo.mVisibleInsets);
final boolean stableInsetsChanged = !mPendingStableInsets.equals(
mAttachInfo.mStableInsets);
final boolean outsetsChanged = !mPendingOutsets.equals(mAttachInfo.mOutsets);
final boolean surfaceSizeChanged = (relayoutResult
& WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0;
final boolean alwaysConsumeNavBarChanged =
mPendingAlwaysConsumeNavBar != mAttachInfo.mAlwaysConsumeNavBar;
if (contentInsetsChanged) {
mAttachInfo.mContentInsets.set(mPendingContentInsets);
if (DEBUG_LAYOUT) Log.v(mTag, "Content insets changing to: "
+ mAttachInfo.mContentInsets);
}
if (overscanInsetsChanged) {
mAttachInfo.mOverscanInsets.set(mPendingOverscanInsets);
if (DEBUG_LAYOUT) Log.v(mTag, "Overscan insets changing to: "
+ mAttachInfo.mOverscanInsets);
// Need to relayout with content insets.
contentInsetsChanged = true;
}
if (stableInsetsChanged) {
mAttachInfo.mStableInsets.set(mPendingStableInsets);
if (DEBUG_LAYOUT) Log.v(mTag, "Decor insets changing to: "
+ mAttachInfo.mStableInsets);
// Need to relayout with content insets.
contentInsetsChanged = true;
}
if (alwaysConsumeNavBarChanged) {
mAttachInfo.mAlwaysConsumeNavBar = mPendingAlwaysConsumeNavBar;
contentInsetsChanged = true;
}
if (contentInsetsChanged || mLastSystemUiVisibility !=
mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested
|| mLastOverscanRequested != mAttachInfo.mOverscanRequested
|| outsetsChanged) {
mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
mLastOverscanRequested = mAttachInfo.mOverscanRequested;
mAttachInfo.mOutsets.set(mPendingOutsets);
mApplyInsetsRequested = false;
dispatchApplyInsets(host);
}
if (visibleInsetsChanged) {
mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: "
+ mAttachInfo.mVisibleInsets);
}

if (!hadSurface) {
if (mSurface.isValid()) {
// If we are creating a new surface, then we need to
// completely redraw it. Also, when we get to the
// point of drawing it we will hold off and schedule
// a new traversal instead. This is so we can tell the
// window manager about all of the windows being displayed
// before actually drawing them, so it can display then
// all at once.
newSurface = true;
mFullRedrawNeeded = true;
mPreviousTransparentRegion.setEmpty();

// Only initialize up-front if transparent regions are not
// requested, otherwise defer to see if the entire window
// will be transparent
if (mAttachInfo.mThreadedRenderer != null) {
try {
hwInitialized = mAttachInfo.mThreadedRenderer.initialize(
mSurface);
if (hwInitialized && (host.mPrivateFlags
& View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0) {
// Don't pre-allocate if transparent regions
// are requested as they may not be needed
mSurface.allocateBuffers();
}
} catch (OutOfResourcesException e) {
handleOutOfResourcesException(e);
return;
}
}
}
} else if (!mSurface.isValid()) {
// If the surface has been removed, then reset the scroll
// positions.
if (mLastScrolledFocus != null) {
mLastScrolledFocus.clear();
}
mScrollY = mCurScrollY = 0;
if (mView instanceof RootViewSurfaceTaker) {
((RootViewSurfaceTaker) mView).onRootViewScrollYChanged(mCurScrollY);
}
if (mScroller != null) {
mScroller.abortAnimation();
}
// Our surface is gone
if (mAttachInfo.mThreadedRenderer != null &&
mAttachInfo.mThreadedRenderer.isEnabled()) {
mAttachInfo.mThreadedRenderer.destroy();
}
} else if ((surfaceGenerationId != mSurface.getGenerationId()
|| surfaceSizeChanged || windowRelayoutWasForced)
&& mSurfaceHolder == null
&& mAttachInfo.mThreadedRenderer != null) {
mFullRedrawNeeded = true;
try {
// Need to do updateSurface (which leads to CanvasContext::setSurface and
// re-create the EGLSurface) if either the Surface changed (as indicated by
// generation id), or WindowManager changed the surface size. The latter is
// because on some chips, changing the consumer side's BufferQueue size may
// not take effect immediately unless we create a new EGLSurface.
// Note that frame size change doesn't always imply surface size change (eg.
// drag resizing uses fullscreen surface), need to check surfaceSizeChanged
// flag from WindowManager.
mAttachInfo.mThreadedRenderer.updateSurface(mSurface);
} catch (OutOfResourcesException e) {
handleOutOfResourcesException(e);
return;
}
}

final boolean freeformResizing = (relayoutResult
& WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM) != 0;
final boolean dockedResizing = (relayoutResult
& WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED) != 0;
final boolean dragResizing = freeformResizing || dockedResizing;
if (mDragResizing != dragResizing) {
if (dragResizing) {
mResizeMode = freeformResizing
? RESIZE_MODE_FREEFORM
: RESIZE_MODE_DOCKED_DIVIDER;
startDragResizing(mPendingBackDropFrame,
mWinFrame.equals(mPendingBackDropFrame), mPendingVisibleInsets,
mPendingStableInsets, mResizeMode);
} else {
// We shouldn't come here, but if we come we should end the resize.
endDragResizing();
}
}
if (!USE_MT_RENDERER) {
if (dragResizing) {
mCanvasOffsetX = mWinFrame.left;
mCanvasOffsetY = mWinFrame.top;
} else {
mCanvasOffsetX = mCanvasOffsetY = 0;
}
}
} catch (RemoteException e) {
}

if (DEBUG_ORIENTATION) Log.v(
TAG, "Relayout returned: frame=" + frame + ", surface=" + mSurface);

mAttachInfo.mWindowLeft = frame.left;
mAttachInfo.mWindowTop = frame.top;

// !!FIXME!! This next section handles the case where we did not get the
// window size we asked for. We should avoid this by getting a maximum size from
// the window session beforehand.
if (mWidth != frame.width() || mHeight != frame.height()) {
mWidth = frame.width();
mHeight = frame.height();
}

if (mSurfaceHolder != null) {
// The app owns the surface; tell it about what is going on.
if (mSurface.isValid()) {
// XXX .copyFrom() doesn't work!
//mSurfaceHolder.mSurface.copyFrom(mSurface);
mSurfaceHolder.mSurface = mSurface;
}
mSurfaceHolder.setSurfaceFrameSize(mWidth, mHeight);
mSurfaceHolder.mSurfaceLock.unlock();
if (mSurface.isValid()) {
if (!hadSurface) {
mSurfaceHolder.ungetCallbacks();

mIsCreating = true;
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceCreated(mSurfaceHolder);
}
}
surfaceChanged = true;
}
if (surfaceChanged || surfaceGenerationId != mSurface.getGenerationId()) {
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceChanged(mSurfaceHolder, lp.format,
mWidth, mHeight);
}
}
}
mIsCreating = false;
} else if (hadSurface) {
mSurfaceHolder.ungetCallbacks();
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceDestroyed(mSurfaceHolder);
}
}
mSurfaceHolder.mSurfaceLock.lock();
try {
mSurfaceHolder.mSurface = new Surface();
} finally {
mSurfaceHolder.mSurfaceLock.unlock();
}
}
}

final ThreadedRenderer threadedRenderer = mAttachInfo.mThreadedRenderer;
if (threadedRenderer != null && threadedRenderer.isEnabled()) {
if (hwInitialized
|| mWidth != threadedRenderer.getWidth()
|| mHeight != threadedRenderer.getHeight()
|| mNeedsRendererSetup) {
threadedRenderer.setup(mWidth, mHeight, mAttachInfo,
mWindowAttributes.surfaceInsets);
mNeedsRendererSetup = false;
}
}

if (!mStopped || mReportNextDraw) {
boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
(relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
|| mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
updatedConfiguration) {
// TODO: 2018/5/25 //获得view宽高的测量规格,
// TODO: 2018/5/25 mWidth和mHeight表示窗口的宽高,lp.widthhe和lp.height表示DecorView根布局宽和高
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);

if (DEBUG_LAYOUT) Log.v(mTag, "Ooops, something changed! mWidth="
+ mWidth + " measuredWidth=" + host.getMeasuredWidth()
+ " mHeight=" + mHeight
+ " measuredHeight=" + host.getMeasuredHeight()
+ " coveredInsetsChanged=" + contentInsetsChanged);

// Ask host how big it wants to be
// TODO: 2018/5/25 这里是第一步的 执行测量的操作
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

// Implementation of weights from WindowManager.LayoutParams
// We just grow the dimensions as needed and re-measure if
// needs be
int width = host.getMeasuredWidth();
int height = host.getMeasuredHeight();
boolean measureAgain = false;
/**
*指示额外空间的多少将被水平分配。
*与这些LayOutPARAMs关联的视图。如果视图指定0
*不应被拉伸。否则额外像素将被优先评估。
*在所有重量大于0的视图中。
*/ // TODO: 2018/5/31 这里
// WindowManager.LayoutParams=lp;
if (lp.horizontalWeight > 0.0f) {
width += (int) ((mWidth - width) * lp.horizontalWeight);
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
MeasureSpec.EXACTLY);
measureAgain = true;
}
if (lp.verticalWeight > 0.0f) {
height += (int) ((mHeight - height) * lp.verticalWeight);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
MeasureSpec.EXACTLY);
measureAgain = true;
}
// TODO: 2018/5/31 我知道这里为啥执行两次
if (measureAgain) {
if (DEBUG_LAYOUT) Log.v(mTag,
"And hey let's measure once more: width=" + width
+ " height=" + height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}

layoutRequested = true;
}
}
} else {
// Not the first pass and no window/insets/visibility change but the window
// may have moved and we need check that and if so to update the left and right
// in the attach info. We translate only the window frame since on window move
// the window manager tells us only for the new frame but the insets are the
// same and we do not want to translate them more than once.
maybeHandleWindowMove(frame);
}

final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
// TODO: 2018/5/25 执行布局操作
performLayout(lp, mWidth, mHeight);

// By this point all views have been sized and positioned
// We can compute the transparent area

if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
// start out transparent
// TODO: AVOID THAT CALL BY CACHING THE RESULT?
host.getLocationInWindow(mTmpLocation);
mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1],
mTmpLocation[0] + host.mRight - host.mLeft,
mTmpLocation[1] + host.mBottom - host.mTop);

host.gatherTransparentRegion(mTransparentRegion);
if (mTranslator != null) {
mTranslator.translateRegionInWindowToScreen(mTransparentRegion);
}

if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
mPreviousTransparentRegion.set(mTransparentRegion);
mFullRedrawNeeded = true;
// reconfigure window manager
try {
mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
} catch (RemoteException e) {
}
}
}

if (DBG) {
System.out.println("======================================");
System.out.println("performTraversals -- after setFrame");
host.debug();
}
}

if (triggerGlobalLayoutListener) {
mAttachInfo.mRecomputeGlobalAttributes = false;
mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();
}

if (computesInternalInsets) {
// Clear the original insets.
final ViewTreeObserver.InternalInsetsInfo insets = mAttachInfo.mGivenInternalInsets;
insets.reset();

// Compute new insets in place.
mAttachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
mAttachInfo.mHasNonEmptyGivenInternalInsets = !insets.isEmpty();

// Tell the window manager.
if (insetsPending || !mLastGivenInsets.equals(insets)) {
mLastGivenInsets.set(insets);

// Translate insets to screen coordinates if needed.
final Rect contentInsets;
final Rect visibleInsets;
final Region touchableRegion;
if (mTranslator != null) {
contentInsets = mTranslator.getTranslatedContentInsets(insets.contentInsets);
visibleInsets = mTranslator.getTranslatedVisibleInsets(insets.visibleInsets);
touchableRegion = mTranslator.getTranslatedTouchableArea(insets.touchableRegion);
} else {
contentInsets = insets.contentInsets;
visibleInsets = insets.visibleInsets;
touchableRegion = insets.touchableRegion;
}

try {
mWindowSession.setInsets(mWindow, insets.mTouchableInsets,
contentInsets, visibleInsets, touchableRegion);
} catch (RemoteException e) {
}
}
}

if (mFirst && sAlwaysAssignFocus) {
// handle first focus request
if (DEBUG_INPUT_RESIZE) Log.v(mTag, "First: mView.hasFocus()="
+ mView.hasFocus());
if (mView != null) {
if (!mView.hasFocus()) {
mView.restoreDefaultFocus();
if (DEBUG_INPUT_RESIZE) Log.v(mTag, "First: requested focused view="
+ mView.findFocus());
} else {
if (DEBUG_INPUT_RESIZE) Log.v(mTag, "First: existing focused view="
+ mView.findFocus());
}
}
}

final boolean changedVisibility = (viewVisibilityChanged || mFirst) && isViewVisible;
final boolean hasWindowFocus = mAttachInfo.mHasWindowFocus && isViewVisible;
final boolean regainedFocus = hasWindowFocus && mLostWindowFocus;
if (regainedFocus) {
mLostWindowFocus = false;
} else if (!hasWindowFocus && mHadWindowFocus) {
mLostWindowFocus = true;
}

if (changedVisibility || regainedFocus) {
// Toasts are presented as notifications - don't present them as windows as well
boolean isToast = (mWindowAttributes == null) ? false
: (mWindowAttributes.type == WindowManager.LayoutParams.TYPE_TOAST);
if (!isToast) {
host.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
}

mFirst = false;
mWillDrawSoon = false;
mNewSurfaceNeeded = false;
mActivityRelaunched = false;
mViewVisibility = viewVisibility;
mHadWindowFocus = hasWindowFocus;

if (hasWindowFocus && !isInLocalFocusMode()) {
final boolean imTarget = WindowManager.LayoutParams
.mayUseInputMethod(mWindowAttributes.flags);
if (imTarget != mLastWasImTarget) {
mLastWasImTarget = imTarget;
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null && imTarget) {
imm.onPreWindowFocus(mView, hasWindowFocus);
imm.onPostWindowFocus(mView, mView.findFocus(),
mWindowAttributes.softInputMode,
!mHasHadWindowFocus, mWindowAttributes.flags);
}
}
}

// Remember if we must report the next draw.
if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
reportNextDraw();
}

boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;

if (!cancelDraw && !newSurface) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
// TODO: 2018/5/25 执行绘制的操作
performDraw();
} else {
if (isViewVisible) {
// Try again
scheduleTraversals();
} else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).endChangingAnimations();
}
mPendingTransitions.clear();
}
}

mIsInTraversal = false;
}
  • 1、如果窗口的类型是有状态栏的,那么顶层视图DecorView所需要的窗口的宽度和高度

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 如果窗口的类型是有状态栏的,那么顶层视图DecorView所需要的窗口的宽度和高度
    //就是除了状态栏
    if (shouldUseDisplaySize(lp)) {
    // NOTE -- system code, won't try to do compat mode.
    Point size = new Point();
    mDisplay.getRealSize(size);
    desiredWindowWidth = size.x;
    desiredWindowHeight = size.y;
    } else {
    //否者顶层视图DecorView所需要的窗口的宽度和高度就是整个屏幕的宽度
    desiredWindowWidth = dipToPx(config.screenWidthDp);
    desiredWindowHeight = dipToPx(config.screenHeightDp);
    }
  • 2、/获得view宽高的测量规格

    1
    2
    3
    4
    // TODO: 2018/5/25 //获得view宽高的测量规格,
    // TODO: 2018/5/25 mWidth和mHeight表示窗口的宽高,lp.widthhe和lp.height表示DecorView根布局宽和高
    int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
    int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
  • 3、注意这个对象WindowManager.LayoutParams lp ,如果说lp.horizontalWeight > 0.0f或者是lp.verticalWeight > 0.0f,那么measureAgain =true;horizontalWeight这个标记大概是这个意思指示额外空间的多少将被水平分配。如果视图指定0不应被拉伸。否则额外像素将被优先评估。在所有重量大于0的视图中。一般都指示出还有多少的水平的空间将要被分配。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    /**
    * 这个WindowMananger 这里标记了 是
    */
    // TODO: 2018/6/4
    WindowManager.LayoutParams lp = mWindowAttributes;
    // TODO: 2018/5/25 这里是第一步的 执行测量的操作
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    if (lp.horizontalWeight > 0.0f) {
    width += (int) ((mWidth - width) * lp.horizontalWeight);
    childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
    MeasureSpec.EXACTLY);
    measureAgain = true;
    }
    if (lp.verticalWeight > 0.0f) {
    height += (int) ((mHeight - height) * lp.verticalWeight);
    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
    MeasureSpec.EXACTLY);
    measureAgain = true;
    }
  • 如果这个measureAgain=true的话,就会再次调用performMeasure(),通过代码可以发现这就调用了两次performMeasure;

    其实我这里犯了一个错误,不是这样的子,这个标记不一定是为true

    1
    2
    3
    4
    5
    6
    if (measureAgain) {
    if (DEBUG_LAYOUT) Log.v(mTag,
    "And hey let's measure once more: width=" + width
    + " height=" + height);
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    }
  • 4、关于performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);方法,其实就是调用的是mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);,也就是View第一步是测量。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
    if (mView == null) {
    return;
    }
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
    try {
    mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    } finally {
    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
    }

5、第一次绘制的时候,这个标记一定是didLayout一定是true,一定会走到这个方法里面去performLayout(lp, mWidth, mHeight);

1
2
3
4
5
6
7
8
     final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
// TODO: 2018/5/25 执行布局操作
performLayout(lp, mWidth, mHeight);
}
}

  • 关于performLayout这个方法,直接会调用host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());,也就是View的layout的方法。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
      private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
    int desiredWindowHeight) {
    final View host = mView;
    try {
    host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
    //测试层级
    measureHierarchy(host, lp, mView.getContext().getResources(),
    desiredWindowWidth, desiredWindowHeight);
    } finally {
    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }

6、这两个标记也是!cancelDraw && !newSurfacetrue,那么就会走到performDraw();

1
2
3
4
5
6
7
8
9
10
if (!cancelDraw && !newSurface) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
// TODO: 2018/5/25 执行绘制的操作
performDraw();
}

  • 关于performDraw();方法,直接调用的是draw(fullRedrawNeeded);

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        private void performDraw() {
    mIsDrawing = true;
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
    try {
    draw(fullRedrawNeeded);
    } finally {
    mIsDrawing = false;
    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
    }
  • 关于draw(fullRedrawNeeded);,会调用到这里来drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)

    1
    2
    3
    4
    5
    private void draw(boolean fullRedrawNeeded) {
    if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
    return;
    }
    }
  • 关于这个方法drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)。官方的解释为如果绘图成功,如果发生错误,则为false。返回false的,程序就发生了异常,也就是程序GG掉了,绘制失败,这里仅仅贴出关键的代码~~~,这样字,就调用到了mView.draw(canvas);

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
      private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
    boolean scalingRequired, Rect dirty) {
    try {
    canvas.translate(-xoff, -yoff);
    if (mTranslator != null) {
    mTranslator.translateCanvas(canvas);
    }
    canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
    attachInfo.mSetIgnoreDirtyState = false;
    // TODO: 2018/5/25 调用了View里面的draw方法
    mView.draw(canvas);

    drawAccessibilityFocusedDrawableIfNeeded(canvas);
    } finally {
    if (!attachInfo.mSetIgnoreDirtyState) {
    // Only clear the flag if it was not set during the mView.draw() call
    attachInfo.mIgnoreDirtyState = false;
    }
    }
    }
  • 最后做了两张图
    View的绘制流程(一).jpg

View的绘制流程(二)ViewRootImpl.performTraversals.jpg

  • 说明几点
    • 如果感兴趣的,一定要去打个断点看一下这个流程
    • 限于作者水平有限,一定会存在有些错误,还望指出,谢谢

 上一篇
Web的工作方式 Web的工作方式
对于普通的上网,系统是这样做的:浏览器本身就是一个客户端,当你输入URL的时候,首先浏览器会去请求DNS服务器,通过DNS获取相应域名的对应的Ip地址,通过IP地址找到对应IP对应的服务器,要求建立TCP连接,等浏览器发送完HTTP Re
2018-06-14 Shiming_Li
下一篇 
Android源码分析LayoutInflater Android源码分析LayoutInflater
源码基于安卓8.0分析结果 在这篇文章Android源码分析(Activity.setContentView源码解析),分析得出,底层走的就是LayoutInflater.from(this),inflate(),如果inflate()传入
2018-06-07 Shiming_Li
  目录