Activity的setContentView解析

原创 2017年08月16日 19:46:37

我们一般设置Activity的界面,都是在onCreate方法中通过setContentView完成,之后就会在手机上显示我们设置的界面,现在我们走下流程,看看系统究竟是如何做的。
首先Activity是在ActivityThread中的performLaunchActivity方法中被创建的

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ......
        Activity activity = null;
        ......
        activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
        ......
        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);
        ......
        if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
        } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
        }
        ......
        if (!r.activity.mFinished) {
                    activity.performStart();
                    r.stopped = false;
                }
        ......
        return activity;
    }


public Activity newActivity(ClassLoader cl, String className,
            Intent intent)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        return (Activity)cl.loadClass(className).newInstance();
    }

Activity是通过ClassLoader利用反射创建出来的,之后调用 Activity的attach进行初始化赋值,然后调用Activity生命周期中的onCreat和onStart方法。
首先进入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) {
        ......
        mWindow = new PhoneWindow(this, window);
        ......
        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());
        }
        ......
    }

与界面相关的两个参数分别为mWindow,和mWindowManager
mWindow就是一个PhoneWindow对象,来看看它的setWindowManager方法,它的方法不在PhoneWindow中,而是在Window类中定义好的

public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

就是获取了系统的WINDOW_SERVICE服务后,再生成一个WindowManagerImpl对象
所以,现在我们知道mWindow是一个PhoneWindow对象,而mWindowManager是一个WindowManagerImpl对象。
好的attach到这里完事,接着就到了onCreate方法中,具体的说是setContentView方法,我们去里面看看做了什么

public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        ......
    }

getWindow获取到的就是PhoneWindow,看看PhoneWindow的setContentView方法

public void setContentView(int layoutResID) {
        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);
        }
        ......
    }

如果首次设置,那么就会进入installDecor方法中,代码就不贴了,简单说下里面的逻辑,在installDecor里会新建一个DecorView,他继承自FrameLayout,之后系统会根据你设置的Activity界面风格(比如:android:Theme.NoTitleBar.Fullscreen等
)去加载系统布局,而在布局中一般都是有两部分内容,一个titltBar,一个content,而将content对应的ViewGroup赋值给mContentParent,这样installDecor就执行完毕
接着通过mLayoutInflater.inflate(layoutResID, mContentParent);将我们的布局文件添加进content中,这也是为什么设置布局的方法被叫做setContentView,而不是setView的原因
这样就完成了界面的设置工作,然而这只是在应用内设置完成,系统并不知情,接着往下看
执行完了onCreate和onStart方法后,会接着调用onResume方法,onResume方法是在ActivityThread的handleResumeActivity中被调用的

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        ......
        r = performResumeActivity(token, clearHide, reason);
        ......
        if (r.activity.mVisibleFromClient) {
              r.activity.makeVisible();
        }
        ......
    }

执行完onResume后,会去调用Activity的makeVisible方法

void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
    }

wm前面说过,就是WindowManagerImpl,看看他的addView方法

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

真正实现addView方法的是mGlobal,即是WindowManagerGlobal,而它是一个单例对象,看看他的addView

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

关键的就是新建了一个ViewRootImpl对象,接着调用了ViewRootImpl的setView方法

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        ......
        res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
               getHostVisibility(), mDisplay.getDisplayId(),
               mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
               mAttachInfo.mOutsets, mInputChannel);
        ......
    }

先来看看mWindowSession是什么

final IWindowSession mWindowSession;

public ViewRootImpl(Context context, Display display) {
        ......
        mWindowSession = WindowManagerGlobal.getWindowSession();
        ......
    }

看名字就知道是个Binder,是在ViewRootImp的构造函数中被初始化的

public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance();
                    IWindowManager windowManager = getWindowManagerService();
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            },
                            imm.getClient(), imm.getInputContext());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowSession;
        }
    }

public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"));
                try {
                    sWindowManagerService = getWindowManagerService();
                    ValueAnimator.setDurationScale(sWindowManagerService.getCurrentAnimatorScale());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowManagerService;
        }
    }

mWindowSession 也是一个单例的对象,而且它是由WMS产生的
也就是说,我们的每一个应用在WMS里都有一个Session对象对应,我们可以通过调用mWindowSession 的相关方法,与WMS实现交互

可以看到在ViewRootImpl的setView方法中,我们就是通过mWindowSession.addToDisplay方法,来通知WMS的。
addToDisplay的参数中有两个需要注意下,一个是mWindow,另一个是mInputChannel。
mWindow并不是我们的Window类

final W mWindow;

static class W extends IWindow.Stub {
    ......
}

他是一个W类,是ViewRootImpl的一个内部静态类,也是一个binder
我们通过mWindowSession来和WMS主动通信,而WMS如果想和我们主动通信的话,就要通过这个W类
而mInputChannel主要就是负责接收那些触摸,点击事件,WMS通过管道将信息传送到应用中。
经过上面的一系列步骤,就完成了Activity设置界面的工作。
总结下,每个Activity都有一个PhoneWindow对象,PhoneWindow下面会含有一个DecorView,之后会通过WindowManagerGlobal创建一个ViewRootImp对象
而在应用中我们会持有一个mWindowSession对象,他是由WMS创建的,ViewRootImp通过mWindowSession与WMS通信,而WMS则通过传递过来的W类来和ViewRootImp通信

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

activity setContentView 解析

基于 android 4.4 src: Activity 的 setContentView(): public void setContentView(View view, ViewGroup.Lay...

Android源码解析Activity#setContentView()方法

在Activity初始化的过程中,会调用Activity的attach方法,在该方法中会创建一个PhoneWindow的实例,将其作为Activity的mWindow成员变量。在执行完了Activit...

从源码的角度说说Activity的setContentView的原理

我们在Activity开发的时候天天会用到这个方法,有时候还需要根据需求在setContentView调用的时候做一些动作,因此我们就需要知道它内部是如何工作的,我们来一起看一下: setConte...

Android的Activity中setContentView到底经历了什么?

前面我们分别介绍了Android中的事件分发和视图绘制的核心流程。也掺杂着setContentView介绍了下,今天我们简单扼要专门分析下,希望有个更直观、清晰的认识。(趁这几天不太忙,多多总结…)

浅谈Activity中setContentView()

引言 找入口 PhoneWindow类中相关代码解读 installDecor generateLayout 总结一下引言今天来研究一下Android中setContentView()方法的具体实现。...

从源码分析Activity布局构成(setContentview)

古语有云:学而不思则罔。我们知道setContentView能够设置Activity的UI布局,但是你真正了解这个方法是干嘛的么?你真正了解Activity的UI布局结构么?这篇文章就为你揭开Acti...

setContentView(R.layout.activity_main) Error解决方法

今天在写Android代码的过程中,编译器一直报错,错误出在这一行代码: setContentView(R.layout.activity_main) 提示信息是: activity_mai...

Activity setContentView() 方法浅析

Activity setContentView()方法浅析 Android开发中,众所周知在新创建一个Activity都会覆写Activity的生命周期里面的onCreate(Bundle ...

从源码的角度说说Activity的setContentView的原理(二)

前文http://blog.csdn.net/sahadev_/article/details/49072045虽然讲解了LayoutInflate的整个过程,但是其中很多地方是不准确不充分的,这一节...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)