《深入理解Android》学习-View体系

旧巷老猫 提交于 2020-03-01 20:09:29

《深入理解Android》学习-View体系(一)

SurfaceFlinger和WMS支撑起了Android的GUI的底层,但是和终端用户直接交互的还是View系统,本节将对View系统进行介绍,主要描述view的管理以及绘制过程。

1.1 View框架

下图是View框架的核心类图。
View体系
先介绍下上面图中各个类的关系。

  • Activity和Window
    Activity支持UI显示,但是并不是直接进行View树的管理,Activity中定义了一个Window类型的对象mWindow。而Window是个基类,系统根据不同的产品来产生不同的子类。具体是在调用Activity.attach中调用PolicyManager.makeNewWindow来决定的,目前默认生成的都是PhoneWindow。
  • Window和WindowManagerImpl
    Window具有两层含义,一方面它面向Activity,承担着显示UI的作用;另一方面它还要同WindowManagerService通信。当然,这个也不是直接在Window中实现的,不然会造成功能混乱,这个功能由WindowManager来实现,而Window中包含了一个WindowManager的对象mWindowManager,WindowManager是一个接口,其真正的实现是在WindowManagerImpl当中。
  • ViewRoot和WindowManagerImpl的关系
    android4.3 版本之前,WindowManagerImpl在每个进程中只有一个实例,在WindowManagerImpl内部,保存着这样的3个变量:
private View[] mViews;
private ViewRootImpl[] mRoots;
private WindowManager.LayoutParams[] mParams;

android4.3 以后,WindowManagerImpl中不再保存上述的三个变量,而统一交由WindowManagerGlobal管理。

  • ViewRoot与WindowManagerService的关系
    ViewRootImpl的内部包含了一个IWindowSession的对象
static IWindowSession sWindowSession;

ViewRoot通过WMS提供的openSession接口创建一个连接,同时也会通过IWindowSession.add()方法提供一个IWindow对象,WMS可以使用这个Binder类型的对象与ViewRoot进行通信。

1.2 Activity中View Tree的创建过程

同其他组件不同,Activity的内部拥有完整的界面显示机制。下图先展示了这一过程,再对其中的具体步骤进行详细的介绍。
在这里插入图片描述
step1.ActivityThread是应用程序的主线程,当收到AMS发出的启动Activity的消息时,会调用handleLaunchActivity,这是整个ViewTree建立的起点。

setp2: 在handleLaunchActivity内部,包含了两个步骤

  • performLaunchActivity

frameworks/base/core/java/android/app/ActivityThread.java

 @Override
    public Activity handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, Intent customIntent) {
      ... ...
        WindowManagerGlobal.initialize();

        final Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            reportSizeConfigurations(r);
            if (!r.activity.mFinished && pendingActions != null) {
                pendingActions.setOldState(r.state);
                pendingActions.setRestoreInstanceState(true);
                pendingActions.setCallOnPostCreate(true);
            }
        } else {
            try {
                ActivityManager.getService()
                        .finishActivity(r.token, Activity.RESULT_CANCELED, null,
                                Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        }
        return a;
    }

在handleLaunchActivity中调用了performLaunchActivity方法。

frameworks/base/core/java/android/app/ActivityThread.java

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ActivityInfo aInfo = r.activityInfo;
        ... ...
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader(); //类加载器
            activity = mInstrumentation.newActivity( //创建新的Activity
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
          ... ...
        }

        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
... ...
            if (activity != null) {
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
               ... ...
               //attach 新创建的Activity
                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);

                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }
                r.lastNonConfigurationInstances = null;
                checkAndBlockForNetworkAccess();
                activity.mStartedActivity = false;
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }

                activity.mCalled = false;
                if (r.isPersistable()) { //最终调用Activity.onCreate()
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
             ... ...
                r.activity = activity;
            }
            r.setState(ON_CREATE);

            mActivities.put(r.token, r);

        } catch (SuperNotCalledException e) {
            throw e;

        } 
        ... ...
        return activity;
    }

performLaunchActivity中主要包含了三个过程:1.创建Activity;2.attach新创建的activity;3.通过Instrumentation.callActivityOnCreate方法简介调用Activity.onCreate。其中attach将为Activity内部的全局变量赋值,包含mWindow。

frameworks/base/core/java/android/app/Activity.java

 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) {
            ... ...
            mWindow = new PhoneWindow(this, window, activityConfigCallback);
            ... ...
            }

可以看到,最后会生成一个PhoneWindow对象。当生成PhoneWindow后,就需要填充布局。在我们创建自己的Activity时,在onCreate方法中,通常有setContentView这样一个函数。正是由这个函数,产生DecorView来装饰Window布局。

  • handleResumeActivity
    在android P中handleResumeActivity并没有在handleLaunchActivity中调用,ActivityThread继承自ClientTransactionHandler,在ClientTransactionHandler中提供了抽象的handleResumeActivity并在ActivityThread中实现。
    前面通过performLaunchActivity,activity已经完成了Window和DecorView的创建过程,此时View Tree实际上已经生成了,但是还不为外界(WMS和SurfaceFlinger)所知。接下来还需要将其添加到本地的WindowManagerGloba中,再注册到WMS中。

frameworks/base/core/java/android/app/ActivityThread.java

@Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
    
        // TODO Push resumeArgs into the activity for consideration
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason); //将导致Activity.onResume最终被调用 
        final Activity a = r.activity;
        ... ...
        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) {
            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;
                    wm.addView(decor, l); //添加到WindowManager
                } else {
                   .. ...
                    a.onWindowAttributesChanged(l);
                }
            }
            ... ...
    }

在上述方法中,变量wm是ViewManager类型,这是因为WindowManager继承自ViewManager,而getWindowManager()得到的实际上是一个WindowManagerImpl对象,后者的addView又间接调用了WindowManagerGlobal中的实现。

frameworks/base/core/java/android/view/WindowManagerGlobal.java

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

       synchronized (mLock) {
           // Start watching for system property changes.
           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();
                           }
                       }
                   }
               };
               SystemProperties.addChangeCallback(mSystemPropertyUpdater);
           }

           int index = findViewLocked(view, false); //是否添加过此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 {
               root.setView(view, wparams, panelParentView); //调用此方法将View注册到WMS中
           } catch (RuntimeException e) {
           ... ...
           }
       }
   }

至此,ViewTree已经在Activity本地建立起来了,但是还没有和WMS和SurfaceFinger进行交互。接下来将进行介绍。

1.3 在WMS中注册窗口

在WMS中,Widow中只包含了一个WindowState来描述窗口。在ViewRootImpl创建的时候,需要建立同WMS通信的双向通道。过程如下:
在这里插入图片描述
WMS是在ServiceManager中注册的实名Binder Server,因此可以随时都可以查询得到。
setp1: ViewRootImpl在构造函数中,首先利用WMS的openSession接口打开一条Session通道,并存储到mWindowSession变量中。
step2: 在前面的小节中,函数addView最后会调用ViewRootImpl的setView方法,这个函数一方面将DecorView,即View树的根设置到ViewRootImpl中,另一方面向WMS申请注册一个窗口,同时将ViewRootImpl中的W对象作为参数传递给WMS。在ViewRootImpl的setView方法中,会调用requestLayout引发View树的遍历。

UI显示的3个要素是尺寸大小,位置和内容。因此,ViewTree的遍历需要经过以下几个流程

  • performMeasure
    计算View对象在UI界面上的绘图区域大小
  • performLayout
    计算View对象在UI界面上的绘图位置
  • performDraw
    绘制UI内容
    遍历的过程是在performTraversals函数中完成,实现也复杂,后面搞清楚了再写。
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!