从源码理解Android Activity,Window和View的关系

        对于Activity肯定是在熟悉不过的了,对于Activity和View的关系最直观的就是在Activity的onCreate()方法中去通过setContentView()来设置显示界面,那是不是就意味着Activity就直接持有View对象呢?答案是不是,那他们又是一个什么样的关系呢?而window又在其中扮演一个什么样的介绍呢?这里将会给到你答案。

        在上一篇Android Application高级用法和源码分析中有讲到,Activity最先执行的方法是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);

    mFragments.attachHost(null /*parent*/);
    //这里创建的Window对象,Window的唯一实现就是PhoneWindow
    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);
    }
    mUiThread = Thread.currentThread();

    mMainThread = aThread;
    mInstrumentation = instr;
    mToken = token;
    mIdent = ident;
    //将全局的Application传进来
    mApplication = application;
    mIntent = intent;
    mReferrer = referrer;
    mComponent = intent.getComponent();
    mActivityInfo = info;
    mTitle = title;
    mParent = parent;
    mEmbeddedID = id;
    mLastNonConfigurationInstances = lastNonConfigurationInstances;
    if (voiceInteractor != null) {
        if (lastNonConfigurationInstances != null) {
            mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
        } else {
            mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
                    Looper.myLooper());
        }
    }
    //这里设置的WindowManage后面添加view的时候会用到
    mWindow.setWindowManager(
            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
            mToken, mComponent.flattenToString(),
            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    if (mParent != null) {
        mWindow.setContainer(mParent.getWindow());
    }
    mWindowManager = mWindow.getWindowManager();
    mCurrentConfig = config;

    mWindow.setColorMode(info.colorMode);
}

这个方法中就是初始化一些变量以及设置一些回调,这里我们跟着主线走,那就看看mWindow是赋值了PhoneWindow对象。实际上Window的唯一实现就是PhoneWindow,到这就理清了Activity与Window的关系,Activity持有Window对象。

        接下来就看看View,对于View的切入点应该就是Activity的setContentView()方法,来看下:

public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}
public Window getWindow() {
    return mWindow;
}

看到这,是不是觉得就是一个代理的作用,上面说到mWindow的实际指向是一个PhoneWindow对象,所以这里实际调用的是PhoneWindow的setContentView()方法,还得去看PhoneWindow:

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.
    if (mContentParent == null) {
        //这个方法中会生成一个装我们内容的父布局mContentParent,而这
        // 个mContentParent又属于mDecor的子view,mDecor就是真正的最顶层view,
        //后面需要绘制view的时候传的对象这个mDecor对象,
        // 这个方法就不跟下去了,不是这里的重点
        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 {
        //这里就是将我们传进来的xml文件实例化为View对象,并添加到上面创建的view当中
        //到这里,一个窗口视图的view就算创建完了
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
    mContentParentExplicitlySet = true;
}

这里我们就应该明白,View的实际持有者是Window对象,而不是Activity,所以这三者的关系就是Activity持有Window,Window持有View对象,对于这样的一个关系有什么用了呢?继续往下看。

        上面只是讲到了他们的关系,但并没有说到View是在什么时候进行绘制的,我们在Activity的onResume()方法中去获取View的宽高是是获取不到的,这说明这时候View是还没有绘制的。要理清这个关系,还得先回到ActivityThread,在client端Activity的生命周期都是在这里进行的分发的,这里看的是handleResumeActivity(),Activity的onResume()就是从这里分发下去的:

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

    // 这个方法就会执行到Activity的onResume(),并且onRestart()就是在onResume()中最先执行的执行,
    // 只不过会判断是否执行了onStop()方法才会去执行onRestart()
    r = performResumeActivity(token, clearHide, reason);

    if (r != null) {
        ......
        if (r.window == null && !a.mFinished && willBeVisible) {
            //这里拿到的Window对象就是Activity中的PhoneWindow对象
            r.window = r.activity.getWindow();
            //这里就是拿到视图窗口显示的View
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            //在Activity的attach()方法中就有对这个WindowManager进行初始化
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            //将View视图赋值给了Activity的mDecor
            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 impl = decor.getViewRootImpl();
                if (impl != null) {
                    impl.notifyChildRebuilt();
                }
            }
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    //会执行到这里,这里将View添加到了WindowManager当中
                    a.mWindowAdded = true;
                    wm.addView(decor, l);
                } else {
                    a.onWindowAttributesChanged(l);
                }
        } else if (!willBeVisible) {
            if (localLOGV) Slog.v(
                    TAG, "Launch " + r + " mStartedActivity set");
            r.hideForNow = true;
        }

        ......
    } else {
        // 当创建Activity出现异常的时候会执行到这里
        try {
            ActivityManager.getService()
                    .finishActivity(token, Activity.RESULT_CANCELED, null,
                            Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }
}

到目前为止,Activity的onResume()方法已经执行了,但是View视图的绘制并没有开始,通过对上面流程的分析,View视图最终通过WindowManager的addView()添加到WindowManager中去了,View是怎么处理的,所以还得去看WindowManager,它的创建在Activity的attach()方法中有提到,是通过Window的setWindowManager()创建的,那就去看下这个方法:

public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
                             boolean hardwareAccelerated) {
    ......
    mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
    return new WindowManagerImpl(mContext, parentWindow);
}

这下就明白了,获取到的WindowManager实际是WindowManagerImpl对象,那这里就去看下它的addView()方法是怎么处理View视图的:

@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
public static WindowManagerGlobal getInstance() {
    synchronized (WindowManagerGlobal.class) {
        if (sDefaultWindowManager == null) {
            sDefaultWindowManager = new WindowManagerGlobal();
        }
        return sDefaultWindowManager;
    }
}

就是简单的调用了WindowManagerGlobal对象的addView()方法,而这个WindowManagerGlobal是一个单例对象,也就是说不管哪个Activity的View视图都是交由WindowManagerGlobal进行管理的,接下来还是去看下WindowManagerGlobal的addView()方法:

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

    ViewRootImpl root;
    View panelParentView = null;

    synchronized (mLock) {
        ......
        // ViewRootImpl这个对象对于View来说很重要,View的测量,布局以及绘制
        // 都是从这里开始的,还有View事件分发也是从这里开始的
        root = new ViewRootImpl(view.getContext(), display);

        view.setLayoutParams(wparams);
        //WindowManagerGlobal是一个全局的单例对象,这里维护的就是一
        // 个应用所有的View,ViewRootImpl,以及对应的参数
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);

        // do this last because it fires off messages to start doing things
        try {
            //这里就是将我们定义的视图传到ViewRootImpl中去,然后开始视图的测量,布局以及绘制
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            if (index >= 0) {
                removeViewLocked(index, true);
            }
            throw e;
        }
    }
}
到这里就不在跟下去了,跟到ViewRootImpl中去你就可以看到View的测量,绘制了。

总结:

        1、持有关系:Activity持有Window,Window持有View;

       2、Window对象中有WindowManager,WindowManager提供了对View的管理,实际对View的管理是WindowManagerGlobal这个对象,这是一个单例;

        3、View视图的测量、绘制是在Activity   onResume()方法之后执行。



猜你喜欢

转载自blog.csdn.net/tangedegushi/article/details/80823852
今日推荐