源码分析android 系统framework(一)之Activity 与 Window 与 View 之间的关系

1.先从第一步Activity 中看起
public class HelloActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_hello);  // 设置UI都是这里开始的}}
2. Activity.java
    /**     * Set the activity content from a layout resource.  The resource will be     * inflated, adding all top-level views to the activity.     *     * @param layoutResID Resource ID to be inflated.     *     * @see #setContentView(android.view.View)     * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)     */    public void setContentView(@LayoutRes int layoutResID) {        getWindow().setContentView(layoutResID); 从这里看到我们setContentView 直接set到window中        initWindowDecorActionBar();    }
   public Window getWindow() {        return mWindow;    }
  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*/);        mWindow = new PhoneWindow(this, window, activityConfigCallback); // 可以看到这里就是个PhoneWindow 对象        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;        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());            }        }        mWindow.setWindowManager(                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),                mToken, mComponent.flattenToString(),                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);                //每个 Activity 会有一个 WindowManager 对象,这个 mWindowManager 就是和 WindowManagerService 进行通信,也是 WindowManagerService 识别 View 具体属于那个 Activity 的关键,创建时传入 IBinder 类型的 mToken。这个 Activity 的 mToken 是一个 IBinder,WindowManagerService 就是通过这个 IBinder 来管理 Activity 里的 View。        if (mParent != null) {            mWindow.setContainer(mParent.getWindow());        }        mWindowManager = mWindow.getWindowManager();        mCurrentConfig = config;        mWindow.setColorMode(info.colorMode);        setAutofillCompatibilityEnabled(application.isAutofillCompatibilityEnabled());        enableAutofillCompatibilityIfNeeded();    }

可看出 Activity 里新建一个 PhoneWindow 对象。在 Android 中,Window 是个抽象的概念, Android 中 Window 的具体实现类是 PhoneWindow,Activity 和 Dialog 中的 Window 对象都是 PhoneWindow。

同时得到一个 WindowManager 对象,WindowManager 是一个抽象类,这个 WindowManager 的具体实现是在 WindowManagerImpl 中。

3.Window.java
  /**     * Convenience for     * {@link #setContentView(View, android.view.ViewGroup.LayoutParams)}     * to set the screen content from a layout resource.  The resource will be     * inflated, adding all top-level views to the screen.     *     * @param layoutResID Resource ID to be inflated.     * @see #setContentView(View, android.view.ViewGroup.LayoutParams)     */    public abstract void setContentView(@LayoutRes int layoutResID);// 这里看到这个window是一个抽象类
4. PhoneWindow.java
 @Override    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) {            installDecor();  // 这里DecorView 出现了,初始化DecorView,DecorView 其实是一个 FrameLayout        } 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);//            将布局添加到mContentParent        }        mContentParent.requestApplyInsets();        final Callback cb = getCallback();        if (cb != null && !isDestroyed()) {            cb.onContentChanged();        }        mContentParentExplicitlySet = true;    }

总结下来如上图所示。但是此时我们还没有在代码中看到DecorView与PhoneWindow的关系。
那DecorView是何时被绘制到屏幕中的呢。
我们都知道activity的生命周期中,onCreate时activity还是不可见,只有执行完onResume 才会可见。

5. ActivityThread.java

了解Android hander 机器的应该都知道这个类,这个类看名字就知道,他其实就是我们常说的UI线程,主线程。这里面有个和java 中一模一样的main方法。里面有初始化Loop等操作。
我们接着说正题

 @Override    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,            String reason) {      ...........          //执行到 onResume()    ActivityClientRecord r = performResumeActivity(token, clearHide);        if (r.window == null && !a.mFinished && willBeVisible) {            r.window = r.activity.getWindow();            View decor = r.window.getDecorView();  // 这个decorView 就是我们上面的DecorView            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 (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) {                    a.mWindowAdded = true;                    wm.addView(decor, l);  // 将decorView 添加到wms中                } else {                    // The activity will get a callback for this {@link LayoutParams} change        ............
public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,            String reason) {        final ActivityClientRecord r = mActivities.get(token);       .................        try {            r.activity.onStateNotSaved();            r.activity.mFragments.noteStateNotSaved();            checkAndBlockForNetworkAccess();            if (r.pendingIntents != null) {                deliverNewIntents(r, r.pendingIntents);                r.pendingIntents = null;            }            if (r.pendingResults != null) {                deliverResults(r, r.pendingResults, reason);                r.pendingResults = null;            }            r.activity.performResume(r.startsNotResumed, reason); // 我们看到这里调用到activity中的Resume ...................
6. WindowManagerImpl.java
@SystemService(Context.WINDOW_SERVICE)public interface WindowManager extends ViewManager {}

WindowManager 是一个接口类型,所以真正实现addView的是WindowManagerImpl

  @Override    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {        applyDefaultToken(params);        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);        //这里又会调用WindowManagerGlobal 的addview    }
7.WindowManagerGlobal.java

这个类是一个单例的

 public void addView(View view, ViewGroup.LayoutParams params,            Display display, Window parentWindow) {        if (view == null) {            throw new IllegalArgumentException("view must not be null");        }        if (display == null) {            throw new IllegalArgumentException("display must not be null");        }        if (!(params instanceof WindowManager.LayoutParams)) {            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");        }        // 上面这些错误大家刚学android时应该在log 中有碰到吧,我们自己学框架时也可以这样仿照............            root = new ViewRootImpl(view.getContext(), display);// 1            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 {                root.setView(view, wparams, panelParentView);     将view 添加到wms 中     ...............

以上1处创建一个 ViewRootImpl,并将之前创建的 DecoView 作为参数传入,以后 DecoView 的事件都由 ViewRootImpl 来管理了,比如,DecoView 上添加 View,删除 View。

8.ViewRootImpl.java
 /**     * We have one child     */    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {        synchronized (this) {                // 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();   // 这里这个方法就是用来测量,绘制,已经layout的。                if ((mWindowAttributes.inputFeatures                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {                    mInputChannel = new InputChannel();                }                mForceDecorViewVisibility = (mWindowAttributes.privateFlags                        & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;                try {                    mOrigWindowType = mWindowAttributes.type;                    mAttachInfo.mRecomputeGlobalAttributes = true;                    collectViewAttributes();                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);                            这里真正的将view 交给wms了            ....................
   @Override    public void requestLayout() {        if (!mHandlingLayoutInLayoutRequest) {            checkThread();            mLayoutRequested = true;            scheduleTraversals();        }    }  void checkThread() {        if (mThread != Thread.currentThread()) {            throw new CalledFromWrongThreadException(                    "Only the original thread that created a view hierarchy can touch its views.");        // 这里这句话大家肯定见过,别问我为什么知道。哈哈哈哈。子线程不能更新UI就是这来的。        }    }
9. 回到WindowManagerGlobal.java 中

上面mWindowSession.addToDisplay 中的mWindowSession 如下:

mWindowSession = WindowManagerGlobal.getWindowSession();
    public static IWindowSession getWindowSession() {        synchronized (WindowManagerGlobal.class) {            if (sWindowSession == null) {                try {                //获取 InputManagerService 的代理类                    InputMethodManager imm = InputMethodManager.getInstance();                    //获取 WindowManagerService 的代理类                    IWindowManager windowManager = getWindowManagerService();                    //经过 Binder aidl调用,最终调用 WindowManagerService                     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;        }    }final class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChannel outInputChannel) {        // 这里这个mService 就是WindowManagerService        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,                outContentInsets, outStableInsets, outOutsets, outInputChannel);    }}
10.WindowManagerService.java
public int addWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChannel outInputChannel) {    ...    WindowToken token = mTokenMap.get(attrs.token);    //创建 WindowState    WindowState win = new WindowState(this, session, client, token,                attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);    ...    //调整 WindowManager 的 LayoutParams 参数    mPolicy.adjustWindowParamsLw(win.mAttrs);    res = mPolicy.prepareAddWindowLw(win, attrs);    addWindowToListInOrderLocked(win, true);    // 设置 input    mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);    // 创建 Surface 与 SurfaceFlinger 通信,详见下面[SurfaceFlinger 图形系统]    win.attach();    mWindowMap.put(client.asBinder(), win);        if (win.canReceiveKeys()) {        //当该窗口能接收按键事件,则更新聚焦窗口        focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,                false /*updateInputWindows*/);    }    assignLayersLocked(displayContent.getWindowList());    ...}

到此这里 几个关系就联系在一起了。

总结

总体来说上面的意思可以用下面两张图表示

  • 1.activity 相当于android 提供给window和view的工具管理类一样
  • 2.一个Activity 中有一个window,也就是PhoneWindow对象,在PhoneWindow 中有一个DecorView,在setContentView中会将layout填充到此DecorView 中
  • 3.一个应用进程只有一个WindowManagerGlobal 对象,因为在viewRootImpl 中它是static 类型
  • 4.每一个PhoneWindow 对应一个ViewRootImpl对象
  • 5.WindowMangerGlobal 通过调用ViewRootImpl的setView 方法,完成window 的添加过程

更多相关文章

  1. RemoteviewsService
  2. android传递数据bundle封装传递map对象
  3. 禁止应用获取手机信息
  4. android中返回页面并刷新
  5. Android(安卓)TabLayout 不显示标题的解决方法
  6. Android开发指南(39) —— Testing Fundamentals
  7. LogCat中不输出任何的信息
  8. Android与JS之间的互相调用交互(二)
  9. Android程序运行中动态加载Lib的方法(一)

随机推荐

  1. 2010.12.28(4)——— android tab 过多的
  2. Android(安卓)DVM
  3. android UI 优化系列之 创建RGB565的缓存
  4. Android(安卓)Notes(06) - Touch事件分发
  5. android studio logcat 打印不出信息
  6. [Android] - 官方轉換dp, sp 至 pixel的
  7. 如何打log 检查 Android(安卓)CTS failur
  8. PendingIntent实现原理和代码
  9. Android崩溃
  10. Android中Http网络请求库Asnyc-http的使