深入理解Android内核思想 第十一章 笔记
第十一章 GUI系统-View体系
前面分析了ANDROIDGUI系统底层支撑框架,SF和WMS两个系统服务的内部原理。
但是从用户角度,他们不关心。
真正与用户联系的,是View体系,。所有APK应用程序的UI界面都是它描述的。
这里有个AP进程里面的View框架图
Activity是API各个组件中使用率最高的 ,专门设计用于UI界面的显示和处理。
当然其他组件也可以显示UI,但是要多很多额外工作。
对于AP开发,他们用SDK向导生成一个带Activity的AP模板,根据需求 加工ANdroid提供的半成品:
setContentView, onCreate, onStart等方法,快速定制AP。;
从系统实现角度,ANdroid为了提供便捷的半成品,做了很多努力。
讲代码前,有下面几个问题,是很多开发者疑惑的:
View和ViewRoot
以xml描述UI界面的layout,可以发现里面所有元素实际都是树状结构:
<LinearLayout xmlns:..... android:id="@+id/top"..> <LinearLayout android:id="@+id/digits_conteiner"..> <com.android.conteacts.dialpad.DigitsEditTText android:id="@+id/digits".../> <ImageButton android:id="@+id/deleteButton".../> </LinearLayout> <View android:id"@+id/viewEle".../> </LinearLayout>
树形表示:
top
digits_container ViewEle
digits deleteButtion
ViewRoot看起来像是View树的根,但是是误解。
ViewRoot不是View树的一份子。ViewRoot和View对象没有任何血缘关系。
它不是View的子类,也不是父类。ViewRoot可以理解为View树的管理者:
它有一个mView成员变量,指向的是它管理的View树的根,就是图中id为top的元素。
ViewRoot的核心任务是与WindowMnagerService通信,后面说。
★Activity和Window的关系
Activity是支持UI显示的,它是否直接管理View数或者ViewRoot呢?不是。
Activity和他们没有直接联系,中间还有一个被称为Window的对象。
具体说,Acitivity内部有一个mWindow变量:
private Window mWindow;
window就是窗口。Window是基类,根据不同产品衍生不同子类,具体是由系统在Activity.attach中调用PolicyManager.makeNewWindow决定,目前Android系统默认生成的都是phoneWindow。
★,Window与WindowManagerImpl的管理
Window开头的类很多:Window,WindowManager,WindowManagerImpl,为什么那么多相似的类?
先看WIndow,它是面向Activity的,表示UI界面的外框;
框里面包括布局和内容,是由具体的Window子类,如PhoneWIndow去规划的。
但无论最终生成的窗口怎么样,Activity不用修改。
Window的另一层含义是要和WindowManagerService通信,它没有直接在自身实现这个功能。
原因是一个AP可能有多个window,如果都单独与WMS通信,那浪费资源,且管理混乱。所以要统一管理。
就有了WindowManager,
它作为Window的成员变量mWindowManager存在。
WindowManager是一个接口类,真正的实现是WindowManagerImpl,后者同时也是整个AP所有Window的管理者。
所以WindowManager与WindowManagerImpl的关系像是地方与中央。
地方为了实施中央的政策,提供了一个接口,然后汇总给中央进行管理。
★,ViewRoot和WindowManagerImpl的关系
早期系统,WIndowMangerImpl在每个进程只有一个实例。要调用它必须用:WindowManagerImpl.getDefault();
WindowManagerImpl内部,有3个全局变量:
private View[] mViews; private ViewRootImpl[] mRoots; private WindowManger.LayoutParams[] mParams
分别表示View书的根节点,ViewRoot以及Window的属性。
可以看出,一个进程不仅仅有一个ViewRoot,Activity与ViewRoot则是一一对应的关系。
Andorid4.3修改了,WindowManagerImpl不在直接存储那3个数组变量,而是有一个称谓WinndowManagerGlbal的类去统一管理。
另外,还对各个类的关系进程了梳理,提出了一些历史遗留的无关类。
当然,统一管理ViewRoot和View数的本质,是没有变的,换汤不换药。
每一个ViewRootImpl内部,有一个全局变量:
static IWindowSession sWindowSession;
它用于VewiROot到WMS的连接,是ViewROot利用WMS的openSession()接口创建得到得分。
在此基础上,ViewRoot也会通过IWindowSession.add()方法提供一个IWindow对象----从而让WMS也可以通过这个Binder对象来与ViewRoot进行双向通信。
类之间的关系:
这里有个图,很很重要
图11-3 Activity,WindowManagerGlobal,WMS的关系图
每个APP都有一个ActivityThread主线程和mActivities全局变量,后者记录了运行在AP中的所有Activity对象。
一个Activity对应唯一的WindowManager和ViewRootImpl。
WindowManagerGlbal作为全局管理者,内部的mRoots和mViews记录了各个Activity的ViewRootImpl和View数的顶层元素。
ViewRootImpl的另一个重要角色就是负责与WMS通信,从ViewRootImpl到WMS间的通信利用的是IWindowSession,反方向由IWindow完成。
11.2 Activity中ViewTree的创建过程
Actrivity与其他组件的最大的不同,就是它内部拥有完整的界面显示机制,
其中涉及ViewRootImpl,Window和又他们管理的View Tree等,
下面详细说ViewTree,
这里有个流程图:
参与ViewTree创建的有几个主体:ActivityThread, Activity, PhoneWIndow, ViewRootImpl, WM(本地的WM or 服务端WMS,不严格区分先)
流程:
1,作为AP的主线程,ActivityThread负责处理各种核心事件,如AMS通知AP进启动一个Activity,这个任务,最终会转化为ActivitThread管理的LAUNCH_ACTIVITY消息, 然后调用handleLaunchActivity(),这是整个VewiTree建立流程的起点。
2,handleLaunchActivity()内部,细分为两个过程:
→performLaunchActivity;
→handleResumeActivity,(注意Resume的处理时机有多种情况,我们以此为例)
代码:
//frameworks/base/core/java/anroid/.app/ActivityTHrad.java private void handleLaunchActivity(ActivityClientRecoed r, Intent customIntent){ Activity a = performLaunchActivity(r, customIntent) //启动Activity if(a != null){ handleResumeActivityu(r.token, false, r.isForward) //Resume这个Activity }.. }..
下面结合本节开头的序列图,分析这两个函数。
1,performLaunchActivity
private Activity performLaunchActivity(ActivitryClientRecoed r, Intent customnIntent){ ... Activity activity = null; try{ java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); //类加载器 activtiy = mInstrumentation.newActivity(cl, component.getClassName(), r.initen);//加载这个Activity对象。 ... }catch(Exception e){} try{ Application app = r.packageInfo.,makeApplication(false, mInstrumentation); ... if(Activity != null){ .. activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.actrivietyInfo, title, r.parent, r.embeddedID, r.laseNonConfigurationInstances, Config); .... mInstrumentation.callActivityOnCrteate(activity, r.state) //最终会调用Activity.onoCreate() } }catch(SuperNotCalledException e){} }
这个函数主要任务是生成一个Activity对象,然后调用它的attach方法,再通过Instrumentation.callActivityOnCreate间接调用Activity.OnCrreate。
其中attach将为Activity内部众多的全局变量赋值,最重要的就是mWIndow:
mWindow = PolicyManager.makeNewWIndow(this);
这里得到的就是一个PhoneWIndow对象,它在每个Activity中有且仅有一个实例。
WIndow在Acitivity中可以看成界面的框架抽象。
所以有一个Window后,还要生成具体的View内容,就是Activity中的mDecor。
Decor就是装饰,就是说它出列包括Acitivy实际要显示的内容外还要具备所有AP共同的装饰部分。比如Title,ActionBar(是否要显示装饰取决于AP的需求)
产生DecorView的过程是由SsetContentView发起的,也就是开发者需要在onCreate的时候调用这个函数的原因。
而onCreate本身则是由mInstrumentation.callAcitvityOnCreate(activity, r.state)间接调用的。有兴趣自行分析。
Activity的setContentCView只是一个中介,它将通过对应的Window对象完成DecorView的构造:
//frameworks/base/policy/src/com/andorid/internal/policy/impl/PhoneWindow.java public void setContentView(int layoutResID){ if(mContentParent == null) { //如果是第一次调用这个函数 installDecor();//首先要生成mDecor对象 }else{ mContentParent.removaAllViews(); //不是第一次调用,移除旧的 } mLayoutInflater.inflate(layoutResID, mContentParent); //根据ResId创建View对象 }
mContentParen是一个ViewGroupsp对象,它用于容纳ContentView,
当mContentParent是空,说明是第一次调用setContentView。
此时mDecor也一定是空的,所以调用installDecor创建一个DecorView;
否则先清理mContentParent中已有的所有View对象 。
最后通过layoutResID来inflate新的内容,mContentParent即使这个由LaytouResID生成的View树的根。
可以看出,setContentCiew可以被AP多次调用,但一般不这么做。
函数installDecor由两个任务,,生成mDecor和mContentParent。
private void installDecor(){ if(mDecor == null){ mDecor = generateDecor(); } }
函数generateDecor实际只是new一个DecorView对象,返回值赋予mDecor。
DecorView继承自FrameLayout,后面分析原因。
mContentParent的创建过程与mDecor有关联,代码:
if(mContentParent == null){ mContentParent = generateLayout(mDecor); }//installDecor结束
可以看到,mCOntentParent通过generateLayout函数生成,
protected ViewGroup generateLayout(DecorView decor){ TypedAttay a = getWindowStyle(); //获取窗口样式 mIsFloating = a.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, false); ... int layoutResource; int features = getLocalFeatures(); if((features & ((1<<FEATURE_LEFT_ICON)| (1<<FREATURE_RIGHT_ICON))!= 0)){ //根据具体的样式为layoutResource挑选匹配的资源 }else if(features&(1<<FEATRURE_PROGRESS)|(1<<FEATURE_INDETERMINATE_PROGRESS)...){ }else if(features & (1<<FEATURE_CUSTOM_TITLE)!= 0){ }else if (...FEATURE_ACTION_MODE_OVERLAY...){} View in = mLayouInflater.inflate(layoutResource, null) //将资源inflate出来 decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); ViewGoup contentParent = (ViewGroup)findViewByID(ID_ANDROID_CONTENT); ... return contentParent; }
framework提供的这些默认layout文件统一放在frameworks/base./core/res/res/.layout里面
根据layoutResource指定的layout(xml)文件,来inflate出相应的View对象。
然后把这个新对象addView到mDecor,DecorView是一个FrameLayout。
最后,整个generateLayout哈数返回值是一个id为ID_ANDOIRD_CONTENT=con.andoier.internal.R.id.content的对象,也就是mContentParent。
图:DecorView的layout之一
所以,setConteentView实际就是把AP想要显示的视图ContentView加上系统策略的其他元素,如title,action,
合成出用户看到的接种AP的界面,如图。
注意setContentView不负责显示,可以实验,在Activity中不调用setContentView,AP的界面还是可以显示,只是中间的content是空的,。
然后列举了AP常用的Menu
2,handleResumeActivity