Android学习笔记-Activity窗口的创建过程

Window表示一个窗口的概念,android中所有的视图都是通过Window来呈现的,Activity,Dialog,Toast他们的视图都是附加到Window上的。这篇博客讨论的是Activity中的视图如何附加到window上的,其实也就是为什么activity中设置的布局我们能够在手机屏幕上可以看到。

我们都知道当Activty对象创建成功之后会回调onCreate()方法,而当onResume()方法执行完之后,我们设置的布局文件就会在屏幕上呈现,基于上面俩点来分析Activity窗口的创建过程

一.当创建完Activity对象之后所做的事情

Activity的创建都在对应应用程序进程ActivityThread类中完成。Activity类的创建完成之后会调用Activity#attach方法,代码如下:

public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback {

    private Window mWindow;

    private WindowManager mWindowManager;

    ........
    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) {
        attachBaseContext(context);
        //----- 1 ------
        mWindow = new PhoneWindow(this);
        //----- 2 ------
        mWindow.setCallback(this);
        //----- 3 ------
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        //----- 4 ------   
        mWindow.setWindowManager(            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        //----- 5 ------
        mWindowManager = mWindow.getWindowManager();
        .........
    }
    ........
}

从上面的代码中可以看到:
1.新建了一个PhoneWindow的对象赋给了Activity的字段mWindow.在该方法中为Activty创建了一个Window对象,让Activty持有他的基础上,并让Activity实现了Window中的CallBack接口。

2.CallBack接口中定义了大量与用户操作有关的方法,这种方式下,就把用户的操作从Window中,传递给了Activity来处理。其中的一些常用的方法有dispathKeyEvent,dispathTouchEvent()等

3.,还有一个setOnWindowDismissedCallback回调。可以理解为什么当我们按下手机back键时,Activity销毁之后,我们的视图也会不见,其实的销毁了Window的原因而已。

4.接下来的代码就是为window设置了一个WindowManager,在把这个WindowManager设置给Activity中的WindowManager,也就是Activty和Window中持有的是同一个WindowManager。

5.WindowManager是用来干啥的,他是用来把视图添加到window里面的。

总结一下上面的过程:Window其实只是一个抽象的概念,可以理解为容器,里面的View才是真正显示在屏幕上的东西。而Activty又持有Window这个对象,这样当我们启动Activity时,会创建Window,并将View加入到Window中。

二.onCreate方法中的setContentView方法内部实现

activity的setContentView方法为:

 public void setContentView(View view) {
        getWindow().setContentView(view);
        initWindowDecorActionBar();
    }

getWindow中通过上面attach方法知为PhoneWindow,所以看PhoneWindow中的setContentView的实现为:

@Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        // 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();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            view.setLayoutParams(params);
            final Scene newScene = new Scene(mContentParent, view);
            transitionTo(newScene);
        } else {
            mContentParent.addView(view, params);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }

上面代码判断逻辑为:
如果mContentParent为空,则首先创建DecorView,mContentParent为我们在Activity中设置布局的父布局,installDecorView方法为

  private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor();
          ........
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
        ........
        }
   }

generateLayout()方法内部会根据Activity不同主题,去解析不同的布局文件加入到DecorView中,一般的布局文件由俩部分组成,上面为一个ActionBar,下面为一个FrameLayout的布局,layout的内容就是加入到这里面的。

通过上面的几步,已经把layout中的内容设置到DecorView中了,也就是onCreate()到此的任务就结束了,下面继续向下分析

这里写图片描述

三.将DecorView加入到Window中

虽然Window有DecorView的字段,但是视图还没有绘制。这些工作将有WindowManager来完成,将View加入到Window中。

Activity创建完成之后,Ams利用Bindler进程间的通信机制通知客户端调用ActivityThread类中的handleResumeActivity方法来启动Activity

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume) {
        ........
        ActivityClientRecord r = performResumeActivity(token, clearHide);

        if (r != null) {
            final Activity a = r.activity;

            final int forwardBit = isForward ?
                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
            boolean willBeVisible = !a.mStartedActivity;
            if (!willBeVisible) {
                try {
                    willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
                            a.getActivityToken());
                } catch (RemoteException e) {
                }
            }
            if (r.window == null && !a.mFinished && willBeVisible) {
                //客户端Activity的窗口Window对象
                r.window = r.activity.getWindow();
                //窗口的顶层视图DecorView对象
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
                //客户端Activity窗口的窗口管理器对象WindowManager
                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) {
                    //标记客户端Activity添加窗口成功
                    a.mWindowAdded = true;
                    //添加窗口
                    wm.addView(decor, l);
                }
    ........
        }
    }

分析:最后一行有wm.addView(decor,l); 到这里DecorView已经被加入到Window里面了,该方法内部会将DecorView中的视图绘制出来。

WindowManager为一个接口,该接口继承自ViewManager,WindowManager的实现类为WindowManagerImpl,

public interface ViewManager
{

    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

WindowManagerImpl内部的addView方法为

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

可以看到,真正起作用的还是mGlobal,该对象为WindowManagerGlobal的实例。
WindowManagerGlobal对象的获取为单例模式,而WindowManagerImpl对象,对于每个应用程序来说也只有一个。也就是说一个应用程序的WindowManager的实例是唯一的。

WindowManagerGlobal的addView方法为:

    //窗口的添加过程
     public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            final Context context = view.getContext();
            if (context != null
                    && context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }

        ViewRootImpl root;
        View panelParentView = null;
        synchronized (mLock) {    
            if (mSystemPropertyUpdater == null) {
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };

            }
            //不能重复添加窗口
            int index = findViewLocked(view, false);
            if (index >= 0) {
                if (mDyingViews.contains(view)) {
                    mRoots.get(index).doDie();
                } else {
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }       
            }
            //判断当前窗口是否为子窗口,如果是则获得其父窗口并保存在panelParentView变量中
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }
            //每一个窗口对应着一个ViewRootImpl对象
            root = new ViewRootImpl(view.getContext(), display);
            //给当前窗口视图设置参数
            view.setLayoutParams(wparams);
            //保存三个数组
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
        }

        try {
            //真正执行窗口的视图View绘制工作的方法
            root.setView(view, wparams, panelParentView);
        } 
    }

在该方法内:

 root = new ViewRootImpl(view.getContext(), display);
//给当前窗口视图设置参数
view.setLayoutParams(wparams);
//保存三个数组
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
try {
//真正执行窗口的视图View绘制工作的方法
root.setView(view, wparams, panelParentView);
} 

新建了ViewRootImpl对象,并将view加入到list中。然后调用root.setView方法。
该方法为真正的绘制图形的方法。那么接下来要做的就是利用ViewRootImpl对DecorView的绘制了,到此方法结束为止,onResume()结束了,视图会显示在屏幕上。

这里写图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值