View的绘制起点(读源码)笔记

在写代码的时候,因为功能、需求等,需要知道一个控件的宽高,demo如下:

<TextView
    android:id="@+id/text_view"
    android:layout_width="50dp"
    android:layout_height="50dp"/>

Activity中

TextView text_view;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main_5);

    text_view= (TextView) findViewById(R.id.text_view);
    Log.e("onCreate==getMeasuredWidth",text_view.getMeasuredWidth()+"");
    Log.e("onCreate==getWidth",text_view.getWidth()+"");
}

@Override
protected void onResume() {
    super.onResume();
    Log.e("onResume==getMeasuredWidth",text_view.getMeasuredWidth()+"");
    Log.e("onResume==getWidth",text_view.getWidth()+"");
}

日志:

E/onCreate==getMeasuredWidth: 0
E/onCreate==getWidth: 0
E/onResume==getMeasuredWidth: 0
E/onResume==getWidth: 0

说明了,在界面上看到布局的时候,还拿不到view的宽高。接下来,(在onCreate中)添加个方法监听

text_view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {

    Log.e("getMeasuredWidth",text_view.getMeasuredWidth()+"");
    Log.e("getWidth",text_view.getWidth()+"");

    }
});

日志

E/onCreate==getMeasuredWidth: 0
E/onCreate==getWidth: 0
E/onResume==getMeasuredWidth: 0
E/onResume==getWidth: 0
E/getMeasuredWidth: 150
E/getWidth: 150

说明了,绘制(加载)完才能拿到控件的宽高。而在onResume中,控件还没有绘制(加载)。

那么,view是什么时候开始绘制的?

在说明view的绘制之前,我想先说一下,onCreate的流程。算是扩展的知识点吧

在之前的handler源码分析中,我提到了ActivityThread,里面有个handler处理,会处理Activity的各种状态,接下来,去那里的源码看看。这里,我只拿出来onCreate和onResume相关的

public void handleMessage(Message msg) {
    if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
    switch (msg.what) {
        case LAUNCH_ACTIVITY: {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
            final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

            r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);
            handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            } break;

        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;
        }
    Object obj = msg.obj;
    if (obj instanceof SomeArgs) {
        ((SomeArgs) obj).recycle();
    }
    if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}

核心的入口有了,一步步来看。
首先,去看下:handleLaunchActivity(r, null, “LAUNCH_ACTIVITY”);
因为要看的是onCreate相关的,在这个方法下,找onCreate关键字即可,很容易可以找到

if (r.isPersistable()) {
    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
    mInstrumentation.callActivityOnCreate(activity, r.state);
}

两种方法,源码对应的是:

/**
* Perform calling of an activity's {@link Activity#onCreate}
* method.  The default implementation simply calls through to that method.
*  @param activity The activity being created.
* @param icicle The previously frozen state (or null) to pass through to
* @param persistentState The previously persisted state (or null)
*/
public void callActivityOnCreate(Activity activity, Bundle icicle,
            PersistableBundle persistentState) {
    prePerformCreate(activity);
    activity.performCreate(icicle, persistentState);
    postPerformCreate(activity);
}

/**
* Perform calling of an activity's {@link Activity#onCreate}
* method.  The default implementation simply calls through to that method.
*
* @param activity The activity being created.
* @param icicle The previously frozen state (or null) to pass through to onCreate().
*/
public void callActivityOnCreate(Activity activity, Bundle icicle) {
    prePerformCreate(activity);
    activity.performCreate(icicle);
    postPerformCreate(activity);
}

这里,调用了Activity源码下的方法performCreate,去那里看看

final void performCreate(Bundle icicle) {
    restoreHasCurrentPermissionRequest(icicle);
    onCreate(icicle);
    mActivityTransitionState.readState(icicle);
    performCreateCommon();
}

final void performCreate(Bundle icicle, PersistableBundle persistentState) {
    restoreHasCurrentPermissionRequest(icicle);
    onCreate(icicle, persistentState);
    mActivityTransitionState.readState(icicle);
    performCreateCommon();
}

看着这2个方法,有亲切的感觉了,在Activity下,看到了onCreate(两种参数)

protected void onCreate(@Nullable Bundle savedInstanceState) {
    ......
}

public void onCreate(@Nullable Bundle savedInstanceState,
            @Nullable PersistableBundle persistentState) {
    onCreate(savedInstanceState);
}

现在终于知道了,为什么我们创建了MyActivity,构造方法就是:

import android.app.Activity;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.support.annotation.Nullable;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
        super.onCreate(savedInstanceState, persistentState);
    }
}

好,现在,onCreate相关的,说完了,我们再去看onResume,还是回到ActivityThread下的handler,找到

handleResumeActivity((IBinder) args.arg1, true, args.argi1 != 0, true,
                            args.argi3, "RESUME_ACTIVITY");

去看它的源码:(我这里只拿出来最关心的一部分代码)

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {

    ActivityClientRecord r = mActivities.get(token);
    ......
    r = performResumeActivity(token, clearHide, reason);

    if (r != null) {

        final Activity a = r.activity;

         ......

        if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            ......
            if (a.mVisibleFromClient && !a.mWindowAdded) {
                a.mWindowAdded = true;
                wm.addView(decor, l);
            }
            ......
        } else if (!willBeVisible) {
            if (localLOGV) Slog.v(
            TAG, "Launch " + r + " mStartedActivity set");
            r.hideForNow = true;
        }
        ......

    } else {
        // If an exception was thrown when trying to resume, then
        // just end this activity.
        try {
            ActivityManagerNative.getDefault()
                .finishActivity(token, Activity.RESULT_CANCELED, null,
                    Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }
}

从这段代码,我们就能明确知道,是先处理onResume的事:performResumeActivity,然后才去处理view的事:addView

关于performResumeActivity中的事,我就简单说了,也是和上面的create一样,一步一步走,就能看到

r.activity.performResume();

Activity 下 performResume()

performResume 中 mInstrumentation.callActivityOnResume(this); 

去到 Instrumentation 下 public void callActivityOnResume(Activity activity)

callActivityOnResume 中 activity.onResume();

Activity中:

@CallSuper
protected void onResume() {
    if (DEBUG_LIFECYCLE) 
        Slog.v(TAG, "onResume " + this);
    getApplication().dispatchActivityResumed(this);
    mActivityTransitionState.onResume(this, isTopOfTask());
    mCalled = true;
}

至此,以demo为例的话,demo中的,onResume中的代码执行完了。就是去获取view的宽度,然后日志打印,但是,没有走到 addView方法。所以,获取到宽度为0,也是正常的了。

现在,去看看 wm.addView(decor, l);这句话。

如果我们直接跳进去看,会发现:

package android.view;

/** Interface to let you add and remove child views to an Activity. To get an instance
  * of this class, call {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
  */
public interface ViewManager
{
    /**
     * Assign the passed LayoutParams to the passed View and add the view to the window.
     * <p>Throws {@link android.view.WindowManager.BadTokenException} for certain programming
     * errors, such as adding a second view to a window without removing the first view.
     * <p>Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a
     * secondary {@link Display} and the specified display can't be found
     * (see {@link android.app.Presentation}).
     * @param view The view to be added to this window.
     * @param params The LayoutParams to assign to view.
     */
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

是个接口,相关处理呢?

既然直接进去看不到,就看看wm.addView中的wm是怎么来的。
上面的handleResumeActivity源码中有这么两句话:

final Activity a = r.activity;
ViewManager wm = a.getWindowManager();

好,继续看,在Activity源码中,是

 public WindowManager getWindowManager() {
        return mWindowManager;
    }

这是获取,那么,mWindowManager是什么时候被创建或者产生的呢?继续找,终于,在Activity的attach方法中,找到两句话句话:

(Window mWindow)
 mWindow = new PhoneWindow(this, window);
 mWindowManager = mWindow.getWindowManager();

然后,在Window的源码中找到

mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);

现在,我们找到了WindowManager的实现:WindowManagerImpl

从流程看过来,所谓的wm.addView(decor, l);,其实调用的是WindowManagerImpl中的addView方法。

WindowManagerImpl 中:

 @Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

去看WindowManagerGlobal中的addView方法

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
    if (view == null) {
        throw new IllegalArgumentException("view must not be null");
    }
    if (display == null) {
        throw new IllegalArgumentException("display must not be null");
    }
    if (!(params instanceof WindowManager.LayoutParams)) {
        throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
    }

    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
    if (parentWindow != null) {
        parentWindow.adjustLayoutParamsForSubWindow(wparams);
    } else {
        // If there's no parent, then hardware acceleration for this view is
        // set from the application's hardware acceleration setting.
        final Context context = view.getContext();
        if (context != null&& (context.getApplicationInfo().flags& ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
            wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
        }
    }

    ViewRootImpl root;
    View panelParentView = null;

    ......

    root = new ViewRootImpl(view.getContext(), display);

    ......

    // do this last because it fires off messages to start doing things
    //最后做这个,因为它会触发消息来开始做事情
    try {
        root.setView(view, wparams, panelParentView);
    } catch (RuntimeException e) {
        // BadTokenException or InvalidDisplayException, clean up.
        synchronized (mLock) {
            final int index = findViewLocked(view, false);
            if (index >= 0) {
                removeViewLocked(index, true);
            }
        }
        throw e;
    }
}

关注ViewRootImpl的setView方法,进去看,会发现一句关键性的代码

requestLayout();

ViewRootImpl 中:

@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();
    }
}

其中,有TraversalRunnable mTraversalRunnable

final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        doTraversal();
    }
}

final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

doTraversal()方法中,有这么一句

performTraversals();

这个方法中的代码很多,经过海选,最后找到3句我最关心的话:

......
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
......                 
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
......

这就是view的测量了。

那么,测量完了,在哪里布局呢(即,layout方法在哪里)?在哪里绘制呢(即:draw方法在哪里调用)?

答:还是在performTraversals();方法中。可以找到:(我这里忽略参数)

performLayout()
performDraw();

至此,view的测量、摆放、绘制。都有了。但是要注意,这些的起点,都是addView,而这个方法,是在onResume执行之后的,所以,onResume中,获取不到控件的宽高。

猜你喜欢

转载自blog.csdn.net/u014620028/article/details/81358667