我们已经知道了Activity的生命周期,如onCreate、onDestroy等,但大家是否考虑过这样一个问题:
- 如果没有创建Activity,那么onCreate和onDestroy就没有任何意义,可这个Activity究竟是在哪里创建的?。
第4章中的“Zygote分裂”一节已讲过,Zygote在响应请求后会fork一个子进程,这个子进程是App对应的进程,它的入口函数是ActivityThread类的main函数。ActivityThread类中有一个handleLaunchActivity函数,它就是创建Activity的地方。一起来看这个函数,代码如下所示:
**ActivityThread.java**
~~~
private final voidhandleLaunchActivity(ActivityRecord r, Intent customIntent) {
//①performLaunchActivity返回一个Activity
Activitya = performLaunchActivity(r, customIntent);
if(a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
//②调用handleResumeActivity
handleResumeActivity(r.token, false, r.isForward);
}
......
}
~~~
handleLaunchActivity函数中列出了两个关键点,下面对其分别介绍。
1. 创建Activity
第一个关键函数performLaunchActivity返回一个Activity,这个Activity就是App中的那个Activity(仅考虑App中只有一个Activity的情况),它是怎么创建的呢?其代码如下所示:
handleLaunchActivity函数中列出了两个关键点,下面对其分别介绍。
**ActivityThread.java**
~~~
private final ActivityperformLaunchActivity(ActivityRecord r,
Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
......//完成一些准备工作
//Activity定义在Activity.java中
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
/*
mInstrumentation为Instrumentation类型,源文件为Instrumentation.java。
它在newActivity函数中根据Activity的类名通过Java反射机制来创建对应的Activity,
这个函数比较复杂,待会我们再分析它。
*/
activity = mInstrumentation.newActivity(
cl,component.getClassName(), r.intent);
r.intent.setExtrasClassLoader(cl);
if (r.state != null) {
r.state.setClassLoader(cl);
}
}catch (Exception e) {
......
}
try {
Application app =
r.packageInfo.makeApplication(false,mInstrumentation);
if (activity != null) {
//在Activity中getContext函数返回的就是这个ContextImpl类型的对象
ContextImpl appContext = new ContextImpl();
......
//下面这个函数会调用Activity的onCreate函数
mInstrumentation.callActivityOnCreate(activity, r.state);
......
return activity;
}
~~~
好了,performLaunchActivity函数的作用明白了吧?
- 根据类名以Java反射的方法创建一个Activity。
- 调用Activity的onCreate函数,开始SDK中大书特书Activity的生命周期。
那么,在onCreate函数中,我们一般会做什么呢?在这个函数中,和UI相关的重要工作就是调用setContentView来设置UI的外观。接下去,需要看handleLaunchActivity中第二个关键函数handleResumeActivity。
2. 分析handleResumeActivity
上面已创建好了一个Activity,再来看handleResumeActivity。它的代码如下所示:
**ActivityThread.java**
~~~
final void handleResumeActivity(IBinder token,boolean clearHide,
boolean isForward) {
boolean willBeVisible = !a.mStartedActivity;
if (r.window == null && !a.mFinished&& willBeVisible) {
r.window= r.activity.getWindow();
//①获得一个View对象
Viewdecor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
//②获得ViewManager对象
ViewManagerwm = a.getWindowManager();
......
//③把刚才的decor对象加入到ViewManager中
wm.addView(decor,l);
}
......//其他处理
}
~~~
上面有三个关键点。这些关键点似乎已经和UI部分(如View、Window)有联系了。那么这些联系是在什么时候建立的呢?在分析上面代码中的三个关键点之前,请大家想想在前面的过程中,哪些地方会和UI挂上钩呢?
- 答案就在onCreate函数中,Activity一般都在这个函数中通过setContentView设置UI界面。
看来,必须先分析setContentView,才能继续后面的征程。
3. 分析setContentView
setContentView有好几个同名函数,现在只看其中的一个就可以了。代码如下所示:
**Activity.java**
~~~
public void setContentView(View view) {
//getWindow返回的是什么呢?一起来看看。
getWindow().setContentView(view);
}
public Window getWindow() {
returnmWindow; //返回一个类型为Window的mWindow,它是什么?
}
~~~
上面出现了两个和UI有关系的类:View和Window[①]。来看SDK文档是怎么描述这两个类的。这里先给出原文描述,然后进行对应翻译:
- Window:abstract base class for a top-levelwindow look and behavior policy. An instance of this class should be used asthe top-level view added to the window manager. It provides standard UIpolicies such as a background, title area, default key processing, etc.
中文的意思是:Window是一个抽象基类,用于控制顶层窗口的外观和行为。做为顶层窗口它有什么特殊的职能呢?即绘制背景和标题栏、默认的按键处理等。
这里面有一句比较关键的话:它将做为一个顶层的view加入到Window Manager中。
- View:This class represents the basicbuilding block for user interface components. A View occupies a rectangulararea on the screen and is responsible for drawing and event handling.
View的概念就比较简单了,它是一个基本的UI单元,占据屏幕的一块矩形区域,可用于绘制,并能处理事件。
从上面的View和Window的描述,再加上setContentView的代码,我们能想象一下这三者的关系,如图8-2所示:
:-: ![](http://img.blog.csdn.net/20150802162126884?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
图8-2 Window/View的假想关系图
根据上面的介绍,大家可能会产生两个疑问:
- Window是一个抽象类,它实际的对象到底是什么类型?
- Window Manager究竟是什么?
如果能有这样的疑问,就说明我们非常细心了。下面试来解决这两个问题。
(1)Activity的Window
据上文讲解可知,Window是一个抽象类。它实际的对象到底属于什么类型?先回到Activity创建的地方去看看。下面正是创建Activity时的代码,可当时没有深入地分析。
~~~
activity = mInstrumentation.newActivity(
cl,component.getClassName(), r.intent);
~~~
代码中调用了Instrumentation的newActivity,再去那里看看。
**Instrumentation.java**
~~~
public Activity newActivity(Class<?>clazz, Context context,
IBinder token, Application application, Intent intent,
ActivityInfo info, CharSequencetitle, Activity parent,
String id,Object lastNonConfigurationInstance)
throws InstantiationException, IllegalAccessException{
Activity activity = (Activity)clazz.newInstance();
ActivityThread aThread = null;
//关键函数attach!!
activity.attach(context, aThread, this, token, application, intent,
info, title,parent, id, lastNonConfigurationInstance,
new Configuration());
return activity;
}
~~~
看到关键函数attach了吧?Window的真相马上就要揭晓了,让我们用咆哮体②来表达内心的激动之情吧!!!!
**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,
Object lastNonConfigurationInstance,
HashMap<String,Object> lastNonConfigurationChildInstances,
Configuration config) {
......
//利用PolicyManager来创建Window对象
mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);
......
//创建WindowManager对象
mWindow.setWindowManager(null, mToken, mComponent.flattenToString());
if(mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
//保存这个WindowManager对象
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
}
~~~
此刻又有一点失望吧?这里冒出了个PolicyManager类,Window是由它的makeNewWindow函数所创建,因此还必须再去看看这个PolicyManager。
(2)水面下的冰山——PolicyManager
PolicyManager定义于PolicyManager.java文件,该文件在一个非常独立的目录下,现将其单独列出来:
- frameworks/policies/base/phone/com/android/internal/policy/impl
* * * * *
**注意**,上面路径中的灰色目录phone是针对智能手机这种小屏幕的;另外还有一个平级的目录叫mid,是针对Mid设备的。mid目录的代码比较少,可能目前还没有开发完毕。
* * * * *
下面来看这个PolicyManager,它比较简单。
**PolicyManager.java**
~~~
public final class PolicyManager {
private static final String POLICY_IMPL_CLASS_NAME =
"com.android.internal.policy.impl.Policy";
private static final IPolicy sPolicy;
static{
//
try {
Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
//创建Policy对象
sPolicy = (IPolicy)policyClass.newInstance();
}catch (ClassNotFoundException ex) {
......
}
private PolicyManager() {}
//通过Policy对象的makeNewWindow创建一个Window
publicstatic Window makeNewWindow(Context context) {
return sPolicy.makeNewWindow(context);
}
......
}
~~~
这里有一个单例的sPolicy对象,它是Policy类型,请看它的定义。
(3)真正的Window
Policy类型的定义代码如下所示:
**Policy.java**
~~~
public class Policy implements IPolicy {
private static final String TAG = "PhonePolicy";
private static final String[] preload_classes = {
"com.android.internal.policy.impl.PhoneLayoutInflater",
"com.android.internal.policy.impl.PhoneWindow",
"com.android.internal.policy.impl.PhoneWindow$1",
"com.android.internal.policy.impl.PhoneWindow$ContextMenuCallback",
"com.android.internal.policy.impl.PhoneWindow$DecorView",
"com.android.internal.policy.impl.PhoneWindow$PanelFeatureState",
"com.android.internal.policy.impl.PhoneWindow$PanelFeatureState$SavedState",
};
static{
//加载所有的类
for (String s : preload_classes) {
try {
Class.forName(s);
} catch (ClassNotFoundException ex) {
......
}
}
}
public PhoneWindow makeNewWindow(Contextcontext) {
//makeNewWindow返回的是PhoneWindow对象
return new PhoneWindow(context);
}
......
}
~~~
至此,终于知道了代码:
~~~
mWindow = PolicyManager.makeNewWindow(this);
~~~
返回的Window,原来是一个PhoneWindow对象。它的定义在PhoneWindow.java中。
mWindow的真实身份搞清楚了,还剩下个WindowManager。现在就来揭示其真面目。
(4)真正的WindowManager
先看WindowManager创建的代码,如下所示:
**Activity.java**
~~~
......//创建mWindow对象
//调用mWindow的setWindowManager函数
mWindow.setWindowManager(null, mToken,mComponent.flattenToString());
.....
~~~
上面的函数设置了PhoneWindow的WindowManager,不过第一个参数是null,这是什么意思?在回答此问题之前,先来看PhoneWindow的定义,它是从Window类派生。
**PhoneWindow.java::PhoneWindow定义**
~~~
public class PhoneWindow extends Windowimplements MenuBuilder.Callback
~~~
前面调用的setWindowManager函数,其实是由PhoneWindow的父类Window类来实现的,来看其代码,如下所示:
**Window.java**
~~~
public void setWindowManager(WindowManagerwm,IBinder appToken, String appName) { //注意,传入的wm值为null
mAppToken = appToken;
mAppName = appName;
if(wm == null) {
//如果wm为空的话,则创建WindowManagerImpl对象
wm = WindowManagerImpl.getDefault();
}
//mWindowManager是一个LocalWindowManager
mWindowManager = new LocalWindowManager(wm);
}
~~~
LocalWindowManager是在Window中定义的内部类,请看它的构造函数,其定义如下所示:
**Window.java::LocalWindowManager定义**
~~~
private class LocalWindowManager implementsWindowManager {
LocalWindowManager(WindowManager wm) {
mWindowManager = wm;//还好,只是简单地保存了传入的wm参数
mDefaultDisplay = mContext.getResources().getDefaultDisplay(
mWindowManager.getDefaultDisplay());
}
......
~~~
如上面代码所示,LocalWindowManager将保存一个WindowManager类型的对象,这个对象的实际类型是WindowManagerImpl。而WindowManagerImpl又是什么呢?来看它的代码,如下所示:
**WindowManagerImpl.java**
~~~
public class WindowManagerImpl implementsWindowManager {
......
public static WindowManagerImpl getDefault()
{
return mWindowManager; //返回的就是WindowManagerImpl对象
}
private static WindowManagerImpl mWindowManager= new WindowManagerImpl();
}
~~~
看到这里,是否有点头晕眼花?很多朋友读我的一篇与此内容相关的博文后,普遍也有如此反应。对此,试配制了一剂治晕药方,如图8-3所示:
:-: ![](http://img.blog.csdn.net/20150802162145510?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
图8-3 Window和WindowManger的家族图谱
根据上图,可得出以下结论:
- Activity的mWindow成员变量其真实类型是PhoneWindow,而mWindowManager成员变量的真实类型是LocalWindowManager。
- LocalWindowManager和WindowManagerImpl都实现了WindowManager接口。这里采用的是Proxy模式,表明LocalWindowManager将把它的工作委托WindowManagerImpl来完成。
(5)setContentView的总结
了解了上述知识后,重新回到setContentView函数。这次希望能分析得更深入些。
**Activity.java**
~~~
public void setContentView(View view) {
getWindow().setContentView(view);//getWindow返回的是PhoneWindow
}
~~~
一起来看PhoneWindow的setContentView函数,代码如下所示:
**PhoneWindow**
~~~
public void setContentView(View view) {
//调用另一个setContentView
setContentView(view,
new ViewGroup.LayoutParams(MATCH_PARENT,MATCH_PARENT));
}
public void setContentView(View view,ViewGroup.LayoutParams params) {
//mContentParent为ViewGroup类型,它的初值为null
if(mContentParent == null) {
installDecor();
}else {
mContentParent.removeAllViews();
}
//把view加入到ViewGroup中
mContentParent.addView(view, params);
......
}
~~~
mContentParent是一个ViewGroup类型,它从View中派生,所以也是一个UI单元。从它名字中“Group”所表达的意思分析,它还可以包含其他的View元素。这又是什么意思呢?
- 也就是说,在绘制一个ViewGroup时,它不仅需要把自己的样子画出来,还需要把它包含的View元素的样子也画出来。读者可将它想象成一个容器,容器中的元素就是View。
这里采用的是23种设计模式中的Composite模式,它是UI编程中常用的模式之一。
再来看installDecor函数,其代码如下所示:
**PhoneWindow.java**
~~~
private void installDecor() {
if (mDecor == null) {
//创建mDecor,它为DecorView类型,从FrameLayout派生
mDecor= generateDecor();
......
}
if(mContentParent == null) {
//得到这个mContentParent
mContentParent = generateLayout(mDecor);
//创建标题栏
mTitleView= (TextView)findViewById(com.android.internal.R.id.title);
......
}
~~~
generateLayout函数的输入参数为mDecor,输出为mContentParent,代码如下所示:
**PhoneWindow**
~~~
protected ViewGroup generateLayout(DecorViewdecor){
......
intlayoutResource;
intfeatures = getLocalFeatures();
if((features & ((1 << FEATURE_LEFT_ICON) |(1 <<FEATURE_RIGHT_ICON))) != 0) {
if(mIsFloating) {
//根据情况取得对应标题栏的资源id
layoutResource = com.android.internal.R.layout.dialog_title_icons;
}
......
}
mDecor.startChanging();
View in =mLayoutInflater.inflate(layoutResource, null);
//加入标题栏
decor.addView(in,new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
/*
ID_ANDROID_CONTENT的值为”com.android.internal.R.id.content”
这个contentParent由findViewById返回,实际上就是mDecorView的一部分。
*/
ViewGroupcontentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
......
mDecor.finishChanging();
returncontentParent;
}
~~~
下面看findViewById是如何实现的。它定义在Window.java中,代码如下所示:
**Window.java**
~~~
public View findViewById(int id) {
//getDecorView将返回mDecorView,所以contentParent确实是DecorView的一部分
returngetDecorView().findViewById(id);
}
~~~
大家还记得图8-2吗?介绍完上面的知识后,根据图8-2,可绘制更细致的图8-4:
:-: ![](http://img.blog.csdn.net/20150802162205057?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
图8-4 一个Activity中的UI组件
可从上图中看出,在Activity的onCreate函数中,通过setContentView设置的View,其实只是DecorView的子View。DecorView还处理了标题栏显示等一系列的工作。
注意,这里使用了设计模式中的Decorator(装饰)模式,它也是UI编程中常用的模式之一。
4. 重回handleResumeActivity
看完setContentView的分析后,不知大家是否还记得这样一个问题:为什么要分析这个setContentView函数?在继续前行之前,先来回顾一下被setContentView打断的流程。
当时,我们正在分析handleResumeActivity,代码如下所示:
**ActivityThread.java**
~~~
final void handleResumeActivity(IBinder token,boolean clearHide,
boolean isForward) {
booleanwillBeVisible = !a.mStartedActivity;
......
if (r.window == null && !a.mFinished&& willBeVisible) {
r.window= r.activity.getWindow();
//①获得一个View对象。现在知道这个view就是DecorView
Viewdecor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
//②获得ViewManager对象,这个wm就是LocalWindowManager
ViewManagerwm = a.getWindowManager();
WindowManager.LayoutParamsl = r.window.getAttributes();
a.mDecor= decor;
l.type =WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
if(a.mVisibleFromClient) {
a.mWindowAdded= true;
//③把刚才的decor对象加入到ViewManager中
wm.addView(decor,l);
}
......//其他处理
}
~~~
在上面的代码中,由于出现了多个之前不熟悉的东西,如View、ViewManager等,而这些东西的来源又和setContentView有关,所以我们才转而去分析setContentView了。想起来了吧?
由于代码比较长,跳转关系也很多,在分析代码时,请读者把握流程,在大脑中建立一个代码分析的堆栈。
下面就从addView的分析开始。如前面所介绍的,它的调用方法是:
~~~
wm.addView(decor, l);//wm类型实际是LocalWindowManager
~~~
来看这个addView函数,它的代码如下所示:
**Window.javaLocalWindowManager**
~~~
public final void addView(View view,ViewGroup.LayoutParams params) {
WindowManager.LayoutParams wp =(WindowManager.LayoutParams)params;
CharSequence curTitle = wp.getTitle();
...... //做一些操作,可以不管它
//还记得前面提到过的Proxy模式吗?mWindowManager对象实际上是WindowManagerImpl类型
mWindowManager.addView(view, params);
}
~~~
看来,要搞清楚这个addView函数还是比较麻烦的,因为现在必须到WindowManagerImpl中去看看。它的代码如下所示:
**WindowManagerImpl.java**
~~~
private void addView(View view,ViewGroup.LayoutParams params, boolean nest)
{
ViewRootroot; //ViewRoot,幕后的主角终于登场了!
synchronized(this) {
//①创建ViewRoot
root =new ViewRoot(view.getContext());
root.mAddNesting = 1;
view.setLayoutParams(wparams);
if(mViews == null) {
index = 1;
mViews = new View[1];
mRoots= new ViewRoot[1];
mParams = new WindowManager.LayoutParams[1];
} else{
......
}
index--;
mViews[index]= view;
mRoots[index]= root;//保存这个root
mParams[index]= wparams;
//②setView,其中view是刚才我们介绍的DecorView
root.setView(view,wparams, panelParentView);//
}
~~~
“ViewRoot,ViewRoot ....”,主角终于出场了!即使没介绍它的真实身份,不禁也想欢呼几声。可为避免高兴得过早,还是应该先冷静地分析一下它。这里,列出了ViewRoot的两个重要关键点。
(1)ViewRoot是什么?
ViewRoot是什么?看起来好像和View有些许关系,至少名字非常像。事实上,它的确和View有关系,因为它实现了ViewParent接口。SDK的文档中有关于ViewParent的介绍。但它和Android基本绘图单元中的View却不太一样,比如:ViewParent不处理绘画,因为它没有onDraw函数。
如上所述,ViewParent和绘画没有关系,那么,它的作用是什么?先来看它的代码,如下所示:
**ViewRoot.java::ViewRoot定义**
~~~
public final class ViewRoot extends Handlerimplements ViewParent,
View.AttachInfo.Callbacks //从Handler类派生
{
private final Surface mSurface = new Surface();//这里创建了一个Surface对象
final W mWindow; //这个是什么?
View mView;
}
~~~
上面这段代码传达出了一些重要信息:
- ViewRoot继承了Handler类,看来它能处理消息。ViewRoot果真重写了handleMessage函数。稍侯再来看它。
- ViewRoot有一个成员变量叫mSurface,它是Surface类型。
- ViewRoot还有一个W类型的mWindow和一个View类型的mView变量。
其中,W是ViewRoot定义的一个静态内部类:
~~~
static class W extends IWindow.Stub
~~~
这个类将参与Binder的通信,以后对此再做讲解,先来介绍Surface类。
(2)神笔马良乎?
这里冒出来一个Surface类。它是什么?在回答此问题之前,先来考虑这样一个问题:
- 前文介绍的View、DecorView等都是UI单元,这些UI单元的绘画工作都在onDraw函数中完成。如果把onDraw想象成画图过程,那么画布是什么?
Android肯定不是“马良”,它也没有那支可以在任何物体上作画的“神笔”,所以我们需要一块实实在在的画布,这块画布就是Surface。SDK文档对Surface类的说明是:Handle on to a raw buffer thatis being managed by the screen compositor。这句话的意思是:
- 有一块Raw buffer,至于是内存还是显存,不必管它。
- Surface操作这块Raw buffer。
- Screen compositor(其实就是SurfaceFlinger)管理这块Raw buffer。
Surface和SF、ViewRoot有什么关系呢?相信,聪明的你此时已经明白些了,这里用图8-5描绘一下心中的想法:
:-: ![](http://img.blog.csdn.net/20150802162116569?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
图8-5 马良的神笔工作原理
结合之前所讲的知识,图8-5清晰地传达了如下几条信息:
- ViewRoot有一个成员变量mSurface,它是Surface类型,它和一块Raw Buffer有关联。
- ViewRoot是一个ViewParent,它的子View的绘画操作,是在画布Surface上展开的。
- Surface和SurfaceFlinger有交互,这非常类似AudioTrack和AudioFlinger之间的交互。
既然本章题目为“深入理解Surface系统”,那么就需要重点关注Surface和SurfaceFlinger间的关系。建立这个关系需ViewRoot的参与,所以应先来分析ViewRoot的创建和它的setView函数。
(3)ViewRoot的创建和对setView的分析
来分析ViewRoot的构造。关于它所包含内容,代码如下所示:
**ViewRoot.java**
~~~
public ViewRoot(Context context) {
super();
....
// getWindowSession?我们进去看看
getWindowSession(context.getMainLooper());
......//ViewRoot的mWindow是一个W类型,注意它不是Window类型,而是IWindow类型
mWindow= new W(this, context);
}
~~~
getWindowsession函数,将建立Activity的ViewRoot和WindowManagerService的关系。代码如下所示:
**ViewRoot.java**
~~~
ublic static IWindowSessiongetWindowSession(Looper mainLooper) {
synchronized (mStaticInit) {
if(!mInitialized) {
try {
InputMethodManagerimm =
InputMethodManager.getInstance(mainLooper);
//下面这个函数先得到WindowManagerService的Binder代理,然后调用它的openSession
sWindowSession = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"))
.openSession(imm.getClient(), imm.getInputContext());
mInitialized = true;
} catch (RemoteException e) {
}
}
return sWindowSession;
}
}
~~~
WindowSession?WindowManagerService?第一次看到这些东西时,我快疯了。复杂,太复杂,无比复杂!要攻克这些难题,应先来回顾一下与Zygote相关的知识:
- WindowManagerService(以后简称WMS)由System_Server进程启动,SurfaceFlinger服务也在这个进程中。
看来,Activity的显示还不单纯是它自己的事,还需要和WMS建立联系才行。继续看。先看setView的处理。这个函数很复杂,
注意其中关键的几句。
openSession的操作是一个使用Binder通信的跨进程调用,暂且记住这个函数,在精简流程之后再来分析。
代码如下所示:
**ViewRoot.java**
~~~
public void setView(View view, WindowManager.LayoutParamsattrs,
View panelParentView){//第一个参数view是DecorView
......
mView= view;//保存这个view
synchronized (this) {
requestLayout(); //待会先看看这个。
try {
//调用IWindowSession的add函数,第一个参数是mWindow
res =sWindowSession.add(mWindow, mWindowAttributes,
getHostVisibility(), mAttachInfo.mContentInsets);
}
......
}
~~~
ViewRoot的setView函数做了三件事:
- 保存传入的view参数为mView,这个mView指向PhoneWindow的DecorView。
- 调用requestLayout。
- 调用IWindowSession的add函数,这是一个跨进程的Binder通信,第一个参数是mWindow,它是W类型,从IWindow.stub派生。
先来看这个requestLayout函数,它非常简单,就是往handler中发送了一个消息。注意,ViewRoot是从Handler派生的,所以这个消息最后会由ViewRoot自己处理,代码如下所示:
**ViewRoot.java**
~~~
public void requestLayout() {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
public void scheduleTraversals() {
if(!mTraversalScheduled) {
mTraversalScheduled = true;
sendEmptyMessage(DO_TRAVERSAL); //发送DO_TRAVERSAL消息
}
}
~~~
好,requestLayout分析完毕。
从上面的代码中可发现,ViewRoot和远端进程SystemServer的WMS有交互,先来总结一下它和WMS的交互流程:
- ViewRoot调用openSession,得到一个IWindowSession对象。
- 调用WindowSession对象的add函数,把一个W类型的mWindow对象做为参数传入。
5. ViewRoot和WMS的关系
上面总结了ViewRoot和WMS的交互流程,其中一共有两个跨进程的调用。一起去看。
(1)调用流程分析
WMS的代码在WindowManagerService.java中:
**WindowManagerService.java**
~~~
public IWindowSessionopenSession(IInputMethodClient client,
IInputContextinputContext) {
......
return new Session(client, inputContext);
}
~~~
Session是WMS定义的内部类。它支持Binder通信,并且属于Bn端,即响应请求的服务端。
再来看它的add函数。代码如下所示:
**WindowManagerService.java::Session**
~~~
public int add(IWindow window,WindowManager.LayoutParams attrs,
int viewVisibility, Rect outContentInsets) {
//调用外部类对象的addWindow,也就是WMS的addWindow
returnaddWindow(this, window, attrs, viewVisibility,
outContentInsets);
}
~~~
**WindowManagerService.java**
~~~
public int addWindow(Session session, IWindowclient,
WindowManager.LayoutParams attrs, int viewVisibility,
Rect outContentInsets) {
......
//创建一个WindowState
win = new WindowState(session, client, token,
attachedWindow, attrs,viewVisibility);
......
//调用attach函数
win.attach();
......
return res;
}
~~~
WindowState类也是在WMS中定义的内部类,直接看它的attach函数,代码如下所示:
**WMS.java::WindowState**
~~~
void attach() {
//mSession就是Session对象,调用它的windowAddedLocked函数
mSession.windowAddedLocked();
}
~~~
**WMS.java::Session**
~~~
void windowAddedLocked() {
if(mSurfaceSession == null) {
......
//创建一个SurfaceSession对象
mSurfaceSession= new SurfaceSession();
......
}
mNumWindow++;
}
~~~
这里出现了另外一个重要的对象SurfaceSession。在讲解它之前,急需理清一下现有的知识点,否则可能会头晕。
(2)ViewRoot和WMS的关系梳理
ViewRoot和WMS之间的关系,可用图8-6来表示:
:-:
图8-6 ViewRoot和WMS的关系
总结一下图8-6中的知识点:
- ViewRoot通过IWindowSession和WMS进程进行跨进程通信。IWindowSession定义在IWindowSession.aidl文件中。这个文件在编译时由aidl工具处理,最后会生成类似于Native Binder中Bn端和Bp端的代码,后文会介绍它。
- ViewRoot内部有一个W类型的对象,它也是一个基于Binder通信的类,W是IWindow的Bn端,用于响应请求。IWindow定义在另一个aidl文件IWindow.aidl中。
为什么需要这两个特殊的类呢?简单介绍一下:
首先,来看IWindowSession.aidl对自己的描述:
- System private per-application interface to the window manager:也就是说每个App进程都会和WMS建立一个IWindowSession会话。这个会话被App进程用于和WMS通信。后面会介绍它的requestLayout函数。
再看对IWindow.adil的描述:
- API back to a client window that the Window Manager uses to informit of interesting things happening:这句话的大意是IWindow是WMS用来做事件通知的。每当发生一些事情时,WMS就会把这些事告诉某个IWindow。可以把IWindow想象成一个回调函数。
IWindow的描述表达了什么意思呢?不妨看看它的内容,代码如下所示:
**IWindow.aidl定义**
~~~
void dispatchKey(in KeyEvent event);
void dispatchPointer(in MotionEvent event, longeventTime,
boolean callWhenDone);
void dispatchTrackball(in MotionEvent event,long eventTime,
boolean callWhenDone);
~~~
明白了?这里的事件指的就是按键、触屏等事件。那么,一个按键事件是如何被分发的呢?下面是它大致的流程:
- WMS所在的SystemServer进程接收到按键事件。
- WMS找到UI位于屏幕顶端的进程所对应的IWindow对象,这是一个Bp端对象。
- 调用这个IWindow对象的dispatchKey。IWindow对象的Bn端位于ViewRoot中,ViewRoot再根据内部View的位置信息找到真正处理这个事件的View,最后调用dispatchKey函数完成按键的处理。
其实这些按键事件的分发机制可以拿Windows的UI编程来做类比,在Windows中应用程序的按键处理流程是:
- 每一个按键事件都会转化成一个消息,这个消息将由系统加入到对应进程的消息队列中。该进程的消息在派发处理时,会根据消息的句柄找到对应的Window(窗口),继而该消息就由这个Window处理了。
* * * * *
**注意**:上面的描述实际上大大简化了真实的处理流程,读者可在了解大体知识后进行更深入的研究。
* * * * *
上面介绍的是ViewRoot和WMS的交互,但是我们最关心的Surface还没有正式介绍,在此之前,还是先介绍Activity的流程。
- 前言
- 第1章 阅读前的准备工作
- 1.1 系统架构
- 1.1.1 Android系统架构
- 1.1.2 本书的架构
- 1.2 搭建开发环境
- 1.2.1 下载源码
- 1.2.2 编译源码
- 1.3 工具介绍
- 1.3.1 Source Insight介绍
- 1.3.2 Busybox的使用
- 1.4 本章小结
- 第2章 深入理解JNI
- 2.1 JNI概述
- 2.2 学习JNI的实例:MediaScanner
- 2.3 Java层的MediaScanner分析
- 2.3.1 加载JNI库
- 2.3.2 Java的native函数和总结
- 2.4 JNI层MediaScanner的分析
- 2.4.1 注册JNI函数
- 2.4.2 数据类型转换
- 2.4.3 JNIEnv介绍
- 2.4.4 通过JNIEnv操作jobject
- 2.4.5 jstring介绍
- 2.4.6 JNI类型签名介绍
- 2.4.7 垃圾回收
- 2.4.8 JNI中的异常处理
- 2.5 本章小结
- 第3章 深入理解init
- 3.1 概述
- 3.2 init分析
- 3.2.1 解析配置文件
- 3.2.2 解析service
- 3.2.3 init控制service
- 3.2.4 属性服务
- 3.3 本章小结
- 第4章 深入理解zygote
- 4.1 概述
- 4.2 zygote分析
- 4.2.1 AppRuntime分析
- 4.2.2 Welcome to Java World
- 4.2.3 关于zygote的总结
- 4.3 SystemServer分析
- 4.3.1 SystemServer的诞生
- 4.3.2 SystemServer的重要使命
- 4.3.3 关于 SystemServer的总结
- 4.4 zygote的分裂
- 4.4.1 ActivityManagerService发送请求
- 4.4.2 有求必应之响应请求
- 4.4.3 关于zygote分裂的总结
- 4.5 拓展思考
- 4.5.1 虚拟机heapsize的限制
- 4.5.2 开机速度优化
- 4.5.3 Watchdog分析
- 4.6 本章小结
- 第5章 深入理解常见类
- 5.1 概述
- 5.2 以“三板斧”揭秘RefBase、sp和wp
- 5.2.1 第一板斧--初识影子对象
- 5.2.2 第二板斧--由弱生强
- 5.2.3 第三板斧--破解生死魔咒
- 5.2.4 轻量级的引用计数控制类LightRefBase
- 5.2.5 题外话-三板斧的来历
- 5.3 Thread类及常用同步类分析
- 5.3.1 一个变量引发的思考
- 5.3.2 常用同步类
- 5.4 Looper和Handler类分析
- 5.4.1 Looper类分析
- 5.4.2 Handler分析
- 5.4.3 Looper和Handler的同步关系
- 5.4.4 HandlerThread介绍
- 5.5 本章小结
- 第6章 深入理解Binder
- 6.1 概述
- 6.2 庖丁解MediaServer
- 6.2.1 MediaServer的入口函数
- 6.2.2 独一无二的ProcessState
- 6.2.3 时空穿越魔术-defaultServiceManager
- 6.2.4 注册MediaPlayerService
- 6.2.5 秋风扫落叶-StartThread Pool和join Thread Pool分析
- 6.2.6 你彻底明白了吗
- 6.3 服务总管ServiceManager
- 6.3.1 ServiceManager的原理
- 6.3.2 服务的注册
- 6.3.3 ServiceManager存在的意义
- 6.4 MediaPlayerService和它的Client
- 6.4.1 查询ServiceManager
- 6.4.2 子承父业
- 6.5 拓展思考
- 6.5.1 Binder和线程的关系
- 6.5.2 有人情味的讣告
- 6.5.3 匿名Service
- 6.6 学以致用
- 6.6.1 纯Native的Service
- 6.6.2 扶得起的“阿斗”(aidl)
- 6.7 本章小结
- 第7章 深入理解Audio系统
- 7.1 概述
- 7.2 AudioTrack的破解
- 7.2.1 用例介绍
- 7.2.2 AudioTrack(Java空间)分析
- 7.2.3 AudioTrack(Native空间)分析
- 7.2.4 关于AudioTrack的总结
- 7.3 AudioFlinger的破解
- 7.3.1 AudioFlinger的诞生
- 7.3.2 通过流程分析AudioFlinger
- 7.3.3 audio_track_cblk_t分析
- 7.3.4 关于AudioFlinger的总结
- 7.4 AudioPolicyService的破解
- 7.4.1 AudioPolicyService的创建
- 7.4.2 重回AudioTrack
- 7.4.3 声音路由切换实例分析
- 7.4.4 关于AudioPolicy的总结
- 7.5 拓展思考
- 7.5.1 DuplicatingThread破解
- 7.5.2 题外话
- 7.6 本章小结
- 第8章 深入理解Surface系统
- 8.1 概述
- 8.2 一个Activity的显示
- 8.2.1 Activity的创建
- 8.2.2 Activity的UI绘制
- 8.2.3 关于Activity的总结
- 8.3 初识Surface
- 8.3.1 和Surface有关的流程总结
- 8.3.2 Surface之乾坤大挪移
- 8.3.3 乾坤大挪移的JNI层分析
- 8.3.4 Surface和画图
- 8.3.5 初识Surface小结
- 8.4 深入分析Surface
- 8.4.1 与Surface相关的基础知识介绍
- 8.4.2 SurfaceComposerClient分析
- 8.4.3 SurfaceControl分析
- 8.4.4 writeToParcel和Surface对象的创建
- 8.4.5 lockCanvas和unlockCanvasAndPost分析
- 8.4.6 GraphicBuffer介绍
- 8.4.7 深入分析Surface的总结
- 8.5 SurfaceFlinger分析
- 8.5.1 SurfaceFlinger的诞生
- 8.5.2 SF工作线程分析
- 8.5.3 Transaction分析
- 8.5.4 关于SurfaceFlinger的总结
- 8.6 拓展思考
- 8.6.1 Surface系统的CB对象分析
- 8.6.2 ViewRoot的你问我答
- 8.6.3 LayerBuffer分析
- 8.7 本章小结
- 第9章 深入理解Vold和Rild
- 9.1 概述
- 9.2 Vold的原理与机制分析
- 9.2.1 Netlink和Uevent介绍
- 9.2.2 初识Vold
- 9.2.3 NetlinkManager模块分析
- 9.2.4 VolumeManager模块分析
- 9.2.5 CommandListener模块分析
- 9.2.6 Vold实例分析
- 9.2.7 关于Vold的总结
- 9.3 Rild的原理与机制分析
- 9.3.1 初识Rild
- 9.3.2 RIL_startEventLoop分析
- 9.3.3 RIL_Init分析
- 9.3.4 RIL_register分析
- 9.3.5 关于Rild main函数的总结
- 9.3.6 Rild实例分析
- 9.3.7 关于Rild的总结
- 9.4 拓展思考
- 9.4.1 嵌入式系统的存储知识介绍
- 9.4.2 Rild和Phone的改进探讨
- 9.5 本章小结
- 第10章 深入理解MediaScanner
- 10.1 概述
- 10.2 android.process.media分析
- 10.2.1 MSR模块分析
- 10.2.2 MSS模块分析
- 10.2.3 android.process.media媒体扫描工作的流程总结
- 10.3 MediaScanner分析
- 10.3.1 Java层分析
- 10.3.2 JNI层分析
- 10.3.3 PVMediaScanner分析
- 10.3.4 关于MediaScanner的总结
- 10.4 拓展思考
- 10.4.1 MediaScannerConnection介绍
- 10.4.2 我问你答
- 10.5 本章小结