美文网首页
源码分析--Activity是如何显示的?

源码分析--Activity是如何显示的?

作者: 拙峰朽木 | 来源:发表于2018-03-23 17:29 被阅读0次

高级UI系列:
setContentView源码分析_看AppCompatActivity是如何实现兼容的
源码分析_Activity是如何显示的?
源码分析_Android UI何时刷新:Choreographer

activity的创建是通过ActivityThread.java调用handleLaunchActivity()来实现的:

 private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
       .......

        // Initialize before creating the activity
        WindowManagerGlobal.initialize();

        Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
           ......
            handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);

            
    }


这里先调performLaunchActivity();

 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
       ........
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            ......
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }

        try {
           ........

            if (activity != null) {
              ............
                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, window, r.configCallback);

              ........
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }
                .........
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                ......
                r.activity = activity;
                r.stopped = true;
                if (!r.activity.mFinished) {
                    activity.performStart();
                    r.stopp
                    ed = false;
                }
                if (!r.activity.mFinished) {
                    if (r.isPersistable()) {
                        if (r.state != null || r.persistentState != null) {
                            mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                    r.persistentState);
                        }
                    } else if (r.state != null) {
                        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                    }
                }
                if (!r.activity.mFinished) {
                    activity.mCalled = false;
                    if (r.isPersistable()) {
                        mInstrumentation.callActivityOnPostCreate(activity, r.state,
                                r.persistentState);
                    } else {
                        mInstrumentation.callActivityOnPostCreate(activity, r.state);
                    }
                    if (!activity.mCalled) {
                        throw new SuperNotCalledException(
                            "Activity " + r.intent.getComponent().toShortString() +
                            " did not call through to super.onPostCreate()");
                    }
                }
            }
            r.paused = true;

            mActivities.put(r.token, r);

        } catch (SuperNotCalledException e) {
            throw e;

        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to start activity " + component
                    + ": " + e.toString(), e);
            }
        }

        return activity;
    }

activity创建:

            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);

看下newActivity

/**
     * Perform instantiation of the process's {@link Activity} object.  The
     * default implementation provides the normal system behavior.
     * 
     * @param cl The ClassLoader with which to instantiate the object.
     * @param className The name of the class implementing the Activity
     *                  object.
     * @param intent The Intent object that specified the activity class being
     *               instantiated.
     * 
     * @return The newly instantiated Activity object.
     */
    public Activity newActivity(ClassLoader cl, String className,
            Intent intent)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        return (Activity)cl.loadClass(className).newInstance();
    }

通类加载器创建了一个类的实例。

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, window, r.configCallback);

到attach中看具体实现:

final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
        attachBaseContext(context);

        ......

        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        .....
        mUiThread = Thread.currentThread();

        .....

        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
       ......
    }

这里做的东西还不少,PhonewWindow的创建并将其给WindowManager 还有个UI线程的获取等等。
回到performLaunchActivity下面:
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
mInstrumentation.callActivityOnPostCreate(activity, r.state);
猜也能猜到这些就是Actitvity的那些生命周期

/**
     * 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);
    }

看下 activity.performCreate(icicle, persistentState);


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

终于到我们熟悉的onCreate了吧


    /**
     * Called when the activity is starting.  This is where most initialization
     * should go: calling {@link #setContentView(int)} to inflate the
     * activity's UI, using {@link #findViewById} to programmatically interact
     * with widgets in the UI, calling
     * {@link #managedQuery(android.net.Uri , String[], String, String[], String)} to retrieve
     * cursors for data being displayed, etc.
     *
     * <p>You can call {@link #finish} from within this function, in
     * which case onDestroy() will be immediately called without any of the rest
     * of the activity lifecycle ({@link #onStart}, {@link #onResume},
     * {@link #onPause}, etc) executing.
     *
     * <p><em>Derived classes must call through to the super class's
     * implementation of this method.  If they do not, an exception will be
     * thrown.</em></p>
     *
     * @param savedInstanceState If the activity is being re-initialized after
     *     previously being shut down then this Bundle contains the data it most
     *     recently supplied in {@link #onSaveInstanceState}.  <b><i>Note: Otherwise it is null.</i></b>
     *
     * @see #onStart
     * @see #onSaveInstanceState
     * @see #onRestoreInstanceState
     * @see #onPostCreate
     */
    @MainThread
    @CallSuper
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        ....    }

里面做了什么 我们可以不看,但是这个方法的注释我们不看就可惜了:
Called when the activity is starting. This is where most initialization should go: calling {@link #setContentView(int)} to inflate the activity's UI, using {@link #findViewById} to programmatically interact
with widgets in the UI
一看这个注释发现原来我们在onCreate()中写的代码都是按人家注释来的setContentView(),findViewById()。

除了performLaunchActivity还有很多performxxxxx 一看你就知道是干啥滴。

performActivityConfigurationChanged();
performConfigurationChanged
performDestroyActivity
performNewIntents
performPauseActivity
performStopActivity
performRestartActivity
performResumeActivity

好吧 基本生命周期一套都全了

跳回之前的handleLaunchActivity,接着看handleResumeActivity

 final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        ActivityClientRecord r = mActivities.get(token);
        .......
            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();
                .....
                if (a.mVisibleFromClient) {
                    if (!a.mWindowAdded) {
                        a.mWindowAdded = true;
                        wm.addView(decor, l);
                    } else {
                       ......
                    }
                }

           .......
    }

我们看到 这里面有将DecorView加到了WindowManager中去了,WindowManager是个接口,实现类是WindowManagerImp


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);
    }
        .......
}

我们发现addView的具体实现是在WindowManagerGlobal中执行的,



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

        ViewRootImpl root;
        .....

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

           ......
            try {
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

我们看到WindowManagerGlobal 创建了个ViewRootImp并把DecorView添加给他了。去ViewRootImp凑凑


public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
        ....
    /**
     * We have one child
     */
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
      ........
              mView = view;
              .....
              requestLayout
              .....
               view.assignParent(this);
             ....
    }

}

里面代码巨多,先看view.assignParent(this)。不看代码实现就看字面意思:给DecorView分配父亲。这个方法名取的非常注准,这个方法就是把当前的ViewRootImp分配给这个DecorView做为他的mParentView,不过这个方法具体实现是在DecorView的父类View中,上代码:

 /*
     * Caller is responsible for calling requestLayout if necessary.
     * (This allows addViewInLayout to not request a new layout.)
     */
    void 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");
        }
    }

想画个图表示下不知道该怎么画,还是文字说明吧:在WindowManagerGlobal中,ViewRootImp通过setView方法将DecorView添加给自己了,然后DecorView又通过assignParent()方法将ViewRootImp指为自己的mParent,所以我们经常看到的不停向上遍历mParent到最后获取到的就是ViewRootImp。上面也显示了怎样将DecorView添加到窗口的。


image.png

其实在ViewRootImpl的setView方法里面还有个重要操作:requestLayout,这个方法大家都知道,调用绘制流程,所以我们知道DecorView是在被添加到WindowManger后并被ViewRootImp执行setView后才显示的。

这里穿插个常碰到的问,Q:为什么调用requestLayout最后都是调用ViewRootImp的requestLayout呢?
我们看下View的requestLayout:


    public void requestLayout() {
       ....
        if (mParent != null && !mParent.isLayoutRequested()) {
            mParent.requestLayout();
        }
        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
            mAttachInfo.mViewRequestingLayout = null;
        }
    }

我们看到它是调自己mParent的requestLayout,不管你有多少parent。那么最终都是调到的mParent肯定是DecorView,那么DecorView的mParent是谁呢?上面已经分析过了就是ViewRootImp。所以说所有View的重新绘制的请求到最后都会汇聚到ViewRootImp,所以这就能回答上面的问题了。

接下来我们看下ViewRootImp的requestLayout是如何实现重新绘制的:


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

除去检测当前是否在执行requestLayout和当期线程,所以核心就是 scheduleTraversals();可以翻译为安排遍历,进去看看:

  void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

果然是安排干啥事啊,因为它使用了个post和Handler,所以一看重点就在这个mTraversalRunnable到底做了啥。

   final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            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();看命名就是执行遍历。这个方法没法贴出来了,有近800行代码:


image.png

里面我们关心的就三个方法:

    private void performTraversals() {
.....

performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

.....

            performLayout(lp, mWidth, mHeight);

.....
            performDraw();

}

好了看到这个三个方法名基本就能知道performTraversals()干了啥,它就是重新执行了绘制流程。
看下 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

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

我们发现它又去调用View的measure方法,然后一层一层往下测量。其他两个也基本差不多。

总结一下:ActivityThread创建一个新的Activity,并调用handleResumeActivity()方法,获取当前Activity的WindowManager,并将DecorView添加给WindowManager。WindowManager通过WindowManagerGlobal实现具体的addView();在这个过程中WindowManagerGlobal创建了ViewRootImp,并把调用SetView将DecorView设置给ViewRootImp,在这方法中DecorView将ViewRootImp设为自己的mParent而ViewRootImp则将DecorView设为自己的mView并调用requestLayout方法。requestLayout方法调用scheduleTraversals来通知获取遍历的通知,然后执行doTraversals(),在分别执行performMeasure,performLayout和performDraw来实现重新绘制。这样就让DecorView里的布局显示出来了,而DecorView里面是什么呢,我们上一遍中分析过了,就是你setContentView()的布局。至此Activity显示的整个流程就分析完了。

相关文章

网友评论

      本文标题:源码分析--Activity是如何显示的?

      本文链接:https://www.haomeiwen.com/subject/cubxcftx.html