从源码角度看一个activity的绘制过程

我们都知道一个activity的绘制过程是从setContentView方法开始的 那么setContentView背后到底干了些什么不为人知的事情,今天让我们来揭开它神秘的面纱,我们来看看setContentView的源码

  public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);//初始化content界面
        initWindowDecorActionBar();//初始化ActionBar
    }

我们再来看看activity的层次结构
这里写图片描述
这个方法有两个作用 是初始化content和ActionBar 这就是为什么我们使用java代码设置全屏的时候需要在setContentView前面
requestWindowFeature(Window.FEATURE_NO_TITLE);
比如这样
大概的分布是这样
这里写图片描述
actionBar的初始化我们先不谈 先来看看这个 content
我们点进去发现是一个window类 它的定义是这样 public abstract class Window {。。。}
它的具体对象是一个PhoneWindow类 我们来看看这个PhoneWindow到底干了些什么

public class PhoneWindow extends Window implements MenuBuilder.Callback {
...
 public void setContentView(int layoutResID) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        //判断decorView是否存在 不存在的话建一个或者叫安装一个 下面那些if else就是判断java文件中设置的各种属性
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent);//使用inflater加载layout文件
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }
    ...
    }

当加载layout文件后 oncreate()方法就算完成了
但是大家会问 只是加载了 那么怎么显示呢 activity启动最终调用了ActiivityThread中的内部类的ApplicationThread中的scheduleLaunchActivity函数。该函数会发一个这个方法中会给UI线程发一个H.LAUNCH_ACTIVITY消息,然后会调用ActivityThread的handleLaunchActivity(ActivityClientRecord r, Intent customIntent)函数,我们来看看这个函数的源码

 private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
     ...
    //创建并加载activity 里面会调用到setcontentView方法
        Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            Bundle oldState = r.state;
            //使decorView变得可见兵使用viewRootImpl绘制view
            //////重要重要 看这里
            handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed);

            if (!r.activity.mFinished && r.startsNotResumed) {

                try {
                    r.activity.mCalled = false;
                    //回调回Instrumentation的暂停activity方法,有兴趣可以去看看源码 这里不再赘述 这个类还是挺重要的
                    mInstrumentation.callActivityOnPause(r.activity);
                    // We need to keep around the original state, in case
                    // we need to be created again.  But we only do this
                    // for pre-Honeycomb apps, which always save their state
                    // when pausing, so we can not have them save their state
                    // when restarting from a paused state.  For HC and later,
                    // we want to (and can) let the state be saved as the normal
                    // part of stopping the activity.
                    if (r.isPreHoneycomb()) {
                        r.state = oldState;
                    }
                    if (!r.activity.mCalled) {
                        throw new SuperNotCalledException(
                            "Activity " + r.intent.getComponent().toShortString() +
                            " did not call through to super.onPause()");
                    }

                } catch (SuperNotCalledException e) {
                    throw e;

                } catch (Exception e) {
                    if (!mInstrumentation.onException(r.activity, e)) {
                        throw new RuntimeException(
                                "Unable to pause activity "
                                + r.intent.getComponent().toShortString()
                                + ": " + e.toString(), e);
                    }
                }
                r.paused = true;
            }
        } else {
            // If there was an error, for any reason, tell the activity
            // manager to stop us.
            try {
                ActivityManagerNative.getDefault()
                    .finishActivity(r.token, Activity.RESULT_CANCELED, null, false);
            } catch (RemoteException ex) {
                // Ignore
            }
        }
    }

这里面最重要的绘制的方法就是 handleResumeActivity 我们来看看这个方法

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;

        // TODO Push resumeArgs into the activity for consideration
        ActivityClientRecord r = performResumeActivity(token, clearHide);

        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;

            // If the window hasn't yet been added to the window manager,
            // and this guy didn't finish itself or start another activity,
            // then go ahead and add the window.
            boolean willBeVisible = !a.mStartedActivity;
            if (!willBeVisible) {
                try {
                    willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
                            a.getActivityToken());
                } catch (RemoteException e) {
                }
            }
            ///重要的看这里
                if (r.window == null && !a.mFinished && willBeVisible) {
                //得到window对象
                r.window = r.activity.getWindow();
                //得到decorview
                View decor = r.window.getDecorView();
                //设置decorview可见
                decor.setVisibility(View.INVISIBLE);
                //拿到wm对象
                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) {
        把decorview加到window里面
                    wm.addView(decor, l);
                }

我们接下来看看addView的源码

    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {


        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            // Start watching for system property changes.
            if (mSystemPropertyUpdater == null) {
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }

            int index = findViewLocked(view, false);
            if (index >= 0) {
                if (mDyingViews.contains(view)) {
                    // Don't wait for MSG_DIE to make it's way through root's queue.
                    mRoots.get(index).doDie();
                } else {
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
                // The previous removeView() had not completed executing. Now it has.
            }

            // If this is a panel window, then find the window it is being
            // attached to for future reference.
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }

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

            view.setLayoutParams(wparams);
    //mViews是view的一个arraylist
            mViews.add(view);
            //mroots是ViewRootImpl的一个arraylist
            mRoots.add(root);
            //这个是布局参数的一个arraylist
            mParams.add(wparams);
        }

        // do this last because it fires off messages to start doing things
        try {
        //使用setView把root会知道panelparentView上
            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;
        }
    }

接下来就是setView方法了 他会调用 requestLayout();

   public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

看这个scheduleTraversals();

 void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            //这里很重要 有一个mTraversalRunnable他是一个runable实现 我们看它的实现
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

TraversalRunnable的源码

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

他会调用doTraversal();

 void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }
//看这个方法 
            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }

就是这个最终的目的
performTraversals();这个源码实在太长 我就不贴了 它在ViewRootImpl这个类里面最后执行了performMasure(),performLayout(),performDraw();开始绘制 我们下篇博客谈谈怎么绘制一个view

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值