如前面的那篇帖子所述,每个应用窗口对应一个Activity,所以要创建应用窗口的话,首先要创建Activity。
在Context与Activity的关系那篇帖子中也讲解了Activity的创建过程,我下面就直接上源码,直接加一些
必要的注释,如果不明白的,可以自行查看源码,就不对每一句代码一一解释了。
ps:技术能力有限,难免会有错误,敬请各位指正,感谢...
大体步骤入下:
- 创建Activity
- 创建Window
- 向窗口中添加View
- 通知WindowManagerService把窗口装载到屏幕上
创建Activity
ActivityManagerService通过ApplicationThread的代理远程调用到了ApplicationThread的scheduleLaunchActivity()方法,
ApplicationThread这个类是ActivityThread的内部类。
代码入下:
![](https://i-blog.csdnimg.cn/blog_migrate/68a9d796d90928b432d6a284b64d64c0.png)
这个方法做了两件事:一件事创建了一个本地的Activity的数据对象ActivityClientRecorder;一件事是通过H发送一个启动Activity的异步消息到主线程的消息队列中去。
在这里注意一下r.token,这个token是个Binder对象(ActitityRecorder),是ActivityManagerService远程传递过来的,使用这个token可以与远程的ActivityManagerService进行进程间通信。
通过H(Handler)辗转调用掉了ActivityThread的这个方法:handleLaunchActivity()
![](https://i-blog.csdnimg.cn/blog_migrate/842ac1785f8185a19d1ee7863aa0447c.png)
接着调用到performLaunchActivity()方法
![](https://i-blog.csdnimg.cn/blog_migrate/7f86b4a9c080b9c0eaf394ee778d5a50.png)
.......
.......
![](https://i-blog.csdnimg.cn/blog_migrate/d291bb305c9f94c5ff0d09735925a3b9.png)
通过ClassLoader创建了一个Activity实例
继续这个方法:
![](https://i-blog.csdnimg.cn/blog_migrate/f43e6da39619f2fa41c6fe3c643690a4.png)
看下Activity的attach()方法
![](https://i-blog.csdnimg.cn/blog_migrate/1b3c424ce1651f7daa9c365a921e42bb.png)
部分参数解释一下,token就是ActivityServiceManager传递过来的远程的ActivityRecorder,parent是需要启动的Activity的父Activity,这个是ActivityGroup里启动Activity的时候,这个就不为null了,其他情况下是null。
开始创建window了
下面是这个attach()方法的具体代码
![](https://i-blog.csdnimg.cn/blog_migrate/fd69f79fad14d34a1e85d7d4296dd693.png)
从这里就开始创建Window的实例了:3756行
mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);
第一句是创建一个window对象,第二句是为Activity设置一个回调接口,Activity实现了Window.Callback接口
看下这个PolicyManager,
这个PolicyManager是这个包里的com.android.internal.policy,看这个PolicyManager源码可知道,真正的操作类
并不是他,而是com.android.internal.policy.impl.Policy这个,实现的是com.android.internal.policy.IPolicy这个接口,
Activity中的PolicyManager只是包装了一层com.android.internal.policy.impl.Policy而已。
下面是创建Ipolicy实例的源码,直接在加载字节码的时候已经就实例化了。
![](https://i-blog.csdnimg.cn/blog_migrate/dd5021d051fbe45e2273faf1cd26442c.png)
com.android.internal.policy.IPolicy总共三个方法:
![](https://i-blog.csdnimg.cn/blog_migrate/d13b3af19f64b60f5cf2e045e2a79fe6.png)
看下
PolicyManager.makeNewWindow(this);这个方法:
![](https://i-blog.csdnimg.cn/blog_migrate/795438d237cd0f8e0ed55c450bb4f784.png)
调用了com.android.internal.policy.impl.Policy的makeNewWindow()
顺着走下去
![](https://i-blog.csdnimg.cn/blog_migrate/a3540431605c5a17857708af9891d0af.png)
这里直接new了一个PhoneWindow给返回了。参数中的context就是传过来的Activity
PhoneWindow的构造方法里面把context赋值给了它的mContext,创建了一个当前Activity的LayoutInflater
代码入下:
![](https://i-blog.csdnimg.cn/blog_migrate/47fc39be2d17420f98edcb3490e071f0.png)
调用了下父类的构造器:
![](https://i-blog.csdnimg.cn/blog_migrate/ae9a0c71afd687dd396fef7d24c0b996.png)
所以应用窗口的Window.getContext()就是返回的Activity的实例
到这里Window已经建立起来了,咱们再回到Activity的attach()方法:
mWindow就是PhoneWindow的实例。
![](https://i-blog.csdnimg.cn/blog_migrate/c3d2b3b5b284f78e8bebab1514c143fc.png)
3777行是为Window对象赋值WindowManager
进去看下:
![](https://i-blog.csdnimg.cn/blog_migrate/e4d5bd4c8d67ef4b746e43ca8060f993.png)
window的appToken就是Activity中的mToken.
调用这个方法的时候wm传的是null,所以第一次调用的时候肯定会创建一个WindowManagerImpl的实例,是单例的,同一个
程序只有一个WindowManagerImpl实例。然后把wm包装到LocalWindowManager中,可以看出 LocalWindowManager
只是一个壳。然后把这个壳返回给Activity。
再次回到Activity的attach()方法:
3781行把这个WindowManager实例赋值给了Activity的mWindowManager。
再次回到ActivityThread的performLaunchActivity()方法中
![](https://i-blog.csdnimg.cn/blog_migrate/5d8bd49db954c7a21476d4adc7467d0e.png)
1611行调用了Activity的onCreate()方法
Instrumentation中的代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/459f04ec886f7b1534fc204cfe087ebb.png)
1047行调用了activity的onCreate()方法
在Activity的onCreate方法中的重载就是咱们应用程序员的事情了,咱们通常要在conCreate()方法调用setContentView()来
把布局文件或者View对象设置进去,之后界面就出现了。
开始添加View了
下面看看这个过程:
在Activity的onCreate()方法
![](https://i-blog.csdnimg.cn/blog_migrate/6a6701d1e54ef23823df36c44e2ea1d9.png)
mCalled表示代表onCreate方法是否被调用过。
我们自己的Activity的代码通常这么写:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate();
setContentView(R.layout.main);
}
这个方法我们重写的父类的,应用程序写的代码不是由自己调用而是由Android系统调用,那么这种就是所谓的回调模式,通常
平台级的产品都是这么设计,这样设计就是给开发者一个标准的模板,相当于协议,做平台的一个程序,就一般按照平台
的这种模式去写,好处就是:对于平台来说,可控。对于开发者来说,简单。
应用开发者又调用了Activity的
setContentView()方法,有几个重载的方法,只看一个,咱们进去看看这个方法怎么执行的:
![](https://i-blog.csdnimg.cn/blog_migrate/f21337ef2a68c2346682e43c97d74586.png)
getWindow就是咱们上面创建的PhoneWindow对象。
进入 PhoneWindow 的setContentView()方法
![](https://i-blog.csdnimg.cn/blog_migrate/2a8314b5cebd7124c40304e52bd5dcfb.png)
mContentParent是一个ViewGroup,如果为空的话,会执行
installDecor();
看下这个方法:
![](https://i-blog.csdnimg.cn/blog_migrate/a44be53b55d4f9b27f6e2cf3583357fd.png)
第一次加载的时候mDecor是null,generateDecor()方法就会创建一个DecorView对象赋值给mDecor,通过new DecorView(getContext(), -1);
DecorView是个ViewGroup,是FramLayout的子类,这个DecorView就是我们看到的包括标题栏在内的整个窗口的根View
![](https://i-blog.csdnimg.cn/blog_migrate/7ff9c89f1d1745c84eecceb2c2894dc5.png)
generateLayout(mDecor)方法找出mContentParent
这个地方还对标题栏的显示与否做了一些操作,所以在我们不需要系统提供的标题栏的时候为什么要在setContentView()之前就
告诉系统去掉标题栏,在setContentView()之后告诉系统去掉报错的原因。
回到 PhoneWindow 的setContentView()方法
207行,mLayoutInflater.inflate(layoutResID, mContentParent)这个方法就把应用开发者传过来的布局文件给装载到
mContentParent上了。
mContentParent是mDecor的一个子视图,在标题栏下面,mContentParent就是用来存放我们应用开发者的view的。
下面请看下view层次结构:
![](https://i-blog.csdnimg.cn/blog_migrate/e06c5a5e160f768cf0b6213699490361.jpeg)
208行到结束,回调Activity的onContentChanged()方法,告诉activity视图内容改变了。
到这里,回到ActivityThread的performLaunchActivity()
![](https://i-blog.csdnimg.cn/blog_migrate/fc2d4a4055d71a92c0189b5dcd50fc21.png)
在这里可以看出不调用Activity的concreate方法,系统是不允许的。
![](https://i-blog.csdnimg.cn/blog_migrate/c9d0281e756446efc002055cbde04633.png)
mActivities就是放Activity数据对象的集合,用来代表所有的Activity
![](https://i-blog.csdnimg.cn/blog_migrate/a0d4703041dc623c00ab1b295417f132.png)
再次回到ActivityThread的handleLaunchActivity()方法:
![](https://i-blog.csdnimg.cn/blog_migrate/d123b1e19e21f1ea543ab11451df5237.png)
handlerResumeActivity()就是到执行到Actitiviy onResume()方法之前的一系列判断,及从onCreate()到onResume()之间的声明周期函数。
开始添加让窗口显示了
接着看这个
![](https://i-blog.csdnimg.cn/blog_migrate/c445e11d6698f7c80b597e3f924ea8bf.png)
willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
a.getActivityToken());
远程调用ActivityManagerService的willActivityBeVisible()方法,把Activity里面的mToken传递过去,mToken其实是一个远程ActivityRecorder的Binder引用,这个ActivityRecorder代表服务端的Activity对象。ActivityRecorder实现了IBinder。
上面的方法主要是检查远端是否存在这个即将启动Activity。
继续走下去
![](https://i-blog.csdnimg.cn/blog_migrate/80a47463c06aabe3a73da338070f54bc.png)
上面的willBeVisible 是true,
就执行到这里了
调用了Activity的makeVisible()方法
![](https://i-blog.csdnimg.cn/blog_migrate/1bc6dad0be90d69420f15e8361a70d4e.png)
然后又调用了WindowManager的addView()方法,
调用的是Window.LocalWindowManager的addView()方法,先做一些检查,像下面的代码就是检查是否为子窗口,然后做一些数据
的处理。
![](https://i-blog.csdnimg.cn/blog_migrate/3632fcf660f50d3d7118f706b064a1eb.png)
接着就调用了WindowManagerImpl的addView()方法
![](https://i-blog.csdnimg.cn/blog_migrate/5754ad375f6b412fefa3b82fa056fbf1.png)
![](https://i-blog.csdnimg.cn/blog_migrate/bc49428c42a327b0806b3608aaeece4f.png)
重载的addView方法如下:
![](https://i-blog.csdnimg.cn/blog_migrate/507e8a60538b4861611ae8c9cdc2f0fb.png)
先创建一个ViewRoot对象,
创建三个数组
![](https://i-blog.csdnimg.cn/blog_migrate/94b0c781c80332ab0355060a7626c008.png)
mViews存放的是DecorView
mRoots就是和DecorView对应的ViewRoot
mParams就是对应的WindowManager.LayoutParams
每个index都对应同一个窗口。
![](https://i-blog.csdnimg.cn/blog_migrate/e8b90b4a1f0c21d290381853d7826a79.png)
然后调用ViewRoot的setView方法
![](https://i-blog.csdnimg.cn/blog_migrate/5e623298b3e300765d476772cb0dbdf8.png)
先发出重绘请求,在绘制完之后才把窗口显示在屏幕上。
然后通过sWindowSession通知WindowManagerService可以把窗口添加到屏幕上了
这样应用的窗口就被创建成功了。其实最重要的一操作就是上面这段代码,前面的都是为这里做了一个准备而已。
解释一下sWindowSesion对象
![](https://i-blog.csdnimg.cn/blog_migrate/5e99eaa9c4c8ac6e650f9482da4e91fa.png)
这个对象是ViewRoot一个静态方法获得的,也就是说整个应用程序只有一个IWindowSession对象
![](https://i-blog.csdnimg.cn/blog_migrate/5210c2c96232b57521df25c5ccfbdabc.png)
![](https://i-blog.csdnimg.cn/blog_migrate/30263fc4ac6e80670bd46ba4f982c2a1.png)
通过ServiceManager获得IWindowMananger的Binder引用,然后获得远程可以和WindowManagerService进行通信的对象
Session对象的远程引用对象.
在WindowManagerService类里面有Session mSession对象,就是应用的这个对象,可以通过mSession与WindowManagerService进行通信。
下面是各个类之间的关系,ActivityThread没有放进去,大家知道就可以了。
![](https://i-blog.csdnimg.cn/blog_migrate/ef6444bbb5a1f6a0ef8713d4ca55cd96.png)