Android 探索Activity和Window,View之间的关系

1,788 阅读8分钟

1、Activity,Window,View是什么?

在开始之前,我们先来回忆一下我们对ActivityWindowView的印象;

ActivityAndroid四大组件之一,也是我们最常见的页面的宿主,通过setContentView将xml布局,解析并展示到页面上;

Window:窗口,这是一个抽象类,真正的实现类在PhoneWindow里,用来管理View的展示以及事件的分发;

ViewAndroid的视图,是各种炫酷控件的最终父类,维护了绘制流程以及事件的分发和处理;

下面通过一张图来了解它们的对应关系:

纸上得来终觉浅,绝知此事要躬行!接下来让我们通过源码深入看看底层实现吧;

2、Activity和Window,View的关系;

(1)从上面的一张关系图了解到ActivityWindow是包含的关系,而Window的实现是在PhoneWindow里面,那么ActivityWindow的关系可以理解为ActivityPhoneWindow的关系;到这里就有一个疑问了,为什么ActivityPhoneWindow是包含的关系?而不是平等的,或者对称的关系呢?

(2)要想理清它们的关系,目前并没有什么好的头绪,但是我们可以先从Activity的来源来进行分析,试着从Activity的来源中能否找到它们的对应关系;说到Activity的来源,我们就不得不来分析一下Activity的启动流程,看看Activity究竟是何方神圣!

(3)Activity的启动流程涉及到很多系统服务,要完整的分析,会花上很大的篇章,等看完Activity的启动流程之后,估计我们都忘了看这篇博客的目的的;为了简化流程,这里会从Activity的创建的方法开始讲起;

Activity的创建是在ActivityThreadhandleLaunchActivity()方法里面,我们就从这个方法进行分析; 在开始之前,我们先来看几个问题:

  • 1、在哪里创建Window,创建的Window用来干嘛的;
  • 2、Activity和Window的关系;
  • 3、Activity和View的关系;

源码分析:

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
        ...
       
        // Initialize before creating the activity
        // 初始化WindowManagerService
        WindowManagerGlobal.initialize();
        
        //step1:
        Activity a = performLaunchActivity(r, customIntent);

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

这里面主要分为两步操作,第一步是调用performLaunchActivity方法,第二步是调用handleResumeActivity方法,先来看一下performLaunchActivity方法;

2.1. performLaunchActivity方法解析:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");

        ...
        // Step1:
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            // Step2:
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            ...
        } catch (Exception e) {
            ...
        }

        try {
            // Step3:
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
          ...
            if (activity != null) {
                ...
                // Step4:
                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);

                ...

                activity.mCalled = false;
                // Step5:
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                ...
                }
            }
            r.paused = true;

            mActivities.put(r.token, r);

        } catch (SuperNotCalledException e) {
            throw e;

        } catch (Exception e) {
           ...
        }

        return activity;
    }
    

Step1:

通过调用createBaseContextForActivity创建Activity的上下文Context,而Context是一个抽象类,具体的实现是在ContextImpl里面;

Step2:

通过调用mInstrumentationnewActivity来创建Activity的实例,里面是通过反射的方式进行创建的;Instrumentation这个类底层实现是代理模式,用户代理Activity的各种生命周期的操作;

newActivity方法里通过工厂模式来创建Activity的实例;

最终通过ClassLoader来创建Activity的实例;

Step3:

通过调用LoadedApkmakeApplication方法来创建全局的上下文Application

Step4:

调用Activityattach来进行初始化,将之前创建的上下文传进去;

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) {
        // 将Context赋值给Activity
        attachBaseContext(context);
        
        // 将fragment添加到host
        mFragments.attachHost(null /*parent*/);

        // 创建PhoneWindo的实例
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }
        ...
        
        // 给Window设置管理器,通过系统服务获取的管理器
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        ...
    }

这里面做的主要操作就是创建了WindowWindowManager的实例,Window的实现是在PhoneWindow里面,而WindowManager的实现是在WindowManagerImpl里面;

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

Step5:

Window创建完了之后,就会触发ActivityonCreate方法,通过代理类mInstrumentationcallActivityOnCreate方法,最终调用到ActivityonCreate方法;

看一下总结流程图:

2.2. Activity中的onCreate方法的调用:

Step1:

ActivityonCreate方法调用了setContentView来进行布局的加载;

Step2:

走了父类AppCompatActivitysetContentView方法,这里调用了getDelegate获取实例AppCompatDelegateAppCompatDelegate是一个抽象类,具体实现是在AppCompatDelegateImpl里面;

Step3:

AppCompatDelegateImpl里的setContentView方法,调用了ensureSubDecor方法来创建DecorView,继续跟踪源码往下看;

Step4:

这里调用了createSubDecor方法,而这里会走到WindowsetContentView方法;

到这里似乎有一些眉目了,Activity的setContentView会通过WindowsetContentView来设置布局,那么可以理解为Window管理着Activity对于View的一些相关操作;那到底是不是这样呢,继续跟踪分析;

Step5:

Window的实现是在PhoneWindow里面,来看一下PhoneWindow里的setContentView的逻辑;

这里调用了installDecor方法;

最终调用了generateDecor来创建DecorView

看一下流程图:

小结:到这里,performLaunchActivity的方法就分析完了,这里的源码看到创建了Window,并且通过PhoneWindowsetContentView来创建DecorView的操作;这里可以理解为Window管理着Activity关于View的一些操作;

这里并没有发现ActivityDecorView的关联,接下来看一下handleResumeActivity方法,进一步跟踪看看是否有关联;

2.3. handleResumeActivity方法解析:

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        // 通过token获取记录当前Activity的信息类
        ActivityClientRecord r = mActivities.get(token);
        

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

        if (r != null) {
            ...
            // 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.
            // 判断是否要添加window
            // Stpe2:
            boolean willBeVisible = !a.mStartedActivity;
            if (!willBeVisible) {
                try {
                    willBeVisible = ActivityManager.getService().willActivityBeVisible(
                            a.getActivityToken());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            if (r.window == null && !a.mFinished && willBeVisible) {
                // 将创建Window和decorView赋值给信息记录类
                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;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                
                if (a.mVisibleFromClient) {
                    if (!a.mWindowAdded) {
                        a.mWindowAdded = true;
                        // 通过manager将decorView添加到页面去;
                        // 具体实现是在
                        // Stpe3:
                        wm.addView(decor, l);
                    } else {
                        ...
                    }
                }

            // If the window has already been added, but during resume
            // we started another activity, then don't yet make the
            // window visible.
            } else if (!willBeVisible) {
            // 判断window已经被添加了,就不展示这个Window了;
                if (localLOGV) Slog.v(
                    TAG, "Launch " + r + " mStartedActivity set");
                r.hideForNow = true;
            }
        ...
    }

Step1:

调用了performResumeActivity方法来触发ActivityonResume方法;

主要是调用了ActivityperformResume方法;

@VisibleForTesting
    public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,
            String reason) {
        final ActivityClientRecord r = mActivities.get(token);
        ...
        try {
            ...
            r.activity.performResume(r.startsNotResumed, reason);
        } catch (Exception e) {
           ...
        }
        return r;
    }

来看一下performResume方法,

final void performResume(boolean followedByPause, String reason) {
        // 触发Activity的onStart方法;
        performRestart(true /* start */, reason);

        mFragments.execPendingActions();

        mLastNonConfigurationInstances = null;

        mCalled = false;
        // mResumed is set by the instrumentation
        // 通过代理类Instrumentation来回调Activity的onReusme方法;
        mInstrumentation.callActivityOnResume(this);
       
        // 回调fragment的onResume方法;
        mFragments.dispatchResume();
        mFragments.execPendingActions();

        onPostResume();
        if (!mCalled) {
            throw new SuperNotCalledException(
                "Activity " + mComponent.toShortString() +
                " did not call through to super.onPostResume()");
        }
    }

这里面主要分为三步:
第一步:通过performRestart回调ActivityonStart方法;

第二步:通过代理类Instrumentation来回调ActivityonReusme方法;

第三步:回调fragmentonResume方法;

Step2:

通过系统服务来判断当前窗口如果还没有被添加到窗口管理器中,就添加该窗口,将页面设置为可见状态;

Step3:

DecorView添加到WindowManager里去,ViewManager是一个抽象类,由前面的分析得知,WindowManager的实现是在WindowManagerImpl里面,来看一下WindowManagerImpladdView方法;

最终走的是WindowManagerGlobaladdView方法;

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        ...
       
        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            ...
            // 创建顶层的View视图管理类
            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            try {
                将当前的DecorView设置给ViewRootImpl
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

在这里创建了顶层的View视图管理类ViewRootImpl,并将DecorView设置给ViewRootImpl

来看看ViewRootImplsetView方法做了啥?

/**
     * We have one child
     */
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                // 触发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();
               
            }
        }
    }

这里最终会调用requestLayout方法触发View树的绘制;

第一步:调用了scheduleTraversals方法;

第二步:调用了doTraversal方法;

第三步:调用了performTraversals方法;

performTraversals最终调用了performMeasureperformLayoutperformDraw三个大步骤完成对View树的绘制;

这个流程在之前的一篇博客里面已经分析过了,感兴趣的可以去看看;

到这里handleResumeActivity方法差不多就分析完了,在这里我们理清了ActivityView之间的关系,是通过Window的管理器WindowManger来触发View的绘制的,也就是说ActivityView的绘制流程都是交由WindowWindowManger来管理的;

看一下流程图:

让我们来回忆一下开头提到的几个问题;

  • 1、在哪里创建Window,创建的Window用来干嘛的?
    Activityattach方法里面创建了Window的实现类PhoneWindow,用于管理View的创建以及和ViewRootIml进行一些操作;
  • 2、ActivityWindow的关系?
    Window相当于Activity的代理类,用于管理View的创建,以及后续View树绘制的一些操作;
  • 3、ActivityView的关系?
    Activity不直接操作View,通过代理类Window来管理View的创建以及绘制流程;

其他

关于我

兄dei,如果我的文章对你有帮助的话,请帮我点个赞吧️,也可以关注一下我的Github博客;