安卓Activity创建界面过程-API源码解析

来源:互联网 发布:cf出现网络异常 编辑:程序博客网 时间:2024/06/03 05:07

我们知道Android 软件每一个界面 都是Activity构成的   那么Activity  是怎么创建一个界面的呢   ?

接下来是看源码时间   过程复杂   耐心观看

setContentView(R.layout.activity_main);
这个大家再熟不过了     onCreate中调用这个就可以创建对应布局文件的界面 

接下来看API源码  :

activity中:

public void setContentView(int layoutResID) {        getWindow().setContentView(layoutResID);        initWindowDecorActionBar();    }
getWindow()得到的是一个Window对象

public Window getWindow() {        return mWindow;    }

Window是一个抽象类  不可能创建对象   

那么我们找一下它的实现类       (Activity六千行代码   这是真难找啊  )。

在attach方法中:    

 //  attach 方法是Activity通过反射实例化之后调用的第一个方法 ,之后生命周期的方法才会被调用  



可以看到调用了PolicyManager的静态方法 

看一下这个PolicyManager   

咦  SDK看不了   代码中也调用不了   什么鬼 

看一下它所在的包名 :  

  • com.android.internal.policy.PolicyManager


!!!这根本就不在 android.jar  里

这怎么才能找到这个包里的源码呢   
用我上一篇博客说的:http://grepcode.com/  这个源码浏览网站     
找到这个PolicyManager  类里的:
public static Window  makeNewWindow(Context context) {        return sPolicy.makeNewWindow(context);    }
再看一下  sPolicy 是什么  



它是一个接口      被Policy实现   代码用反射构建了一个Policy对象  
(看来离Window类的真面目不远了)。。。。

Policy中的makeNewWindow()方法 
public Window  makeNewWindow(Context context) {        return new PhoneWindow(context);    }
构造了一个PhoneWindow对象     这就是Window的实现类   也就是它调用了setContentView()  方法     方法内部:

判断没ContentParent 是否为null    是:调用installDecor() ,否:移除内部所有View   。
然后通过 mLayoutInflater.inflate()  
 LayoutInflater也是个抽象类    ,在PhoneWindow的构造方法中通过静态方法from创建的    它由哪个子类继承的还真不知道   
 

接着说installDecor()     ,就截取了开头一段  


首先初始化了 mDecor ,通过generateDecor方法  ,看一下:
protected  DecorView  generateDecor() {        return new DecorView(getContext(), -1);    }
然后找这个构造方法   发现 DecorView是PhoneWindow的内部类 并且是 private  final 类型
 private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
public DecorView(Context context, int featureId) {     super(context);     mFeatureId = featureId;
}
每个PhoneWindow中都有一个DecorView对象,它是PhoneView 的框架View(待会说它是干嘛的)

然后通过 generateLayut(mDecor)方法  把这个上面创建好的mDecor作为参数传递 


这个方法很长  ,找到有一段:
        View in = mLayoutInflater.inflate(layoutResource, null);        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));        mContentRoot = (ViewGroup) in;        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
这里的layoutResource是 int值      对应我们mainfest中设置的theme主题所对应的屏幕布局
可以是R.layout.screen_custom_title;R.layout.screen_action_bar等等
现在我们知道了  DecorView 就是当前屏幕的除状态栏的区域
下面贴上这个图,你就可以看明白了(转自 工匠若水)

ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
把contentParent 变成了 ID_ANDROID_CONTENT 对应的ViewGroup    ,这是什么东西  ,看下这个   id
 public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
R.id.content ?  这怎么像是屏幕状态栏下方的内容区域的  id
通过findViewById给contentParent加上这么个 id 
好吧 暂时不理解    必须要看一下上面提到的    decor.addView  到底干了什么 ,看一下R.layout.screen_custom_title   这个布局吧   
// 文件位置:frameworks\base\core\res\res\layout            (frameworks包下)
<!--This is a custom layout for a screen.--><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    android:fitsSystemWindows="true">    <!-- Popout bar for action modes -->    <ViewStub android:id="@+id/action_mode_bar_stub"              android:inflatedId="@+id/action_mode_bar"              android:layout="@layout/action_mode_bar"              android:layout_width="match_parent"              android:layout_height="wrap_content"              android:theme="?attr/actionBarTheme" />    <FrameLayout android:id="@android:id/title_container"         android:layout_width="match_parent"         android:layout_height="?android:attr/windowTitleSize"        android:transitionName="android:title"        style="?android:attr/windowTitleBackgroundStyle">    </FrameLayout>    <FrameLayout android:id="@android:id/content"        android:layout_width="match_parent"         android:layout_height="0dip"        android:layout_weight="1"        android:foregroundGravity="fill_horizontal|top"        android:foreground="?android:attr/windowContentOverlay" /></LinearLayout>
我们知道这个布局是一个屏幕的布局文件:上面的title_container是用来放自定义Title的容器,而下面的  content  就是放置我们设置的布局的容器  
再看一下generateLayut的方法:
View in = mLayoutInflater.inflate(layoutResource, null);        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));        mContentRoot = (ViewGroup) in;        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
啊哦 getDecorView
把整个手机界面给了DecorView 感觉要到了见证奇迹的时候了呢!!!
看下findViewById方法的实现 :
public View findViewById(int id) {        return getDecorView().findViewById(id);    }

getDecorView  显然是当前PhoneWindow中的DecorView对象  ,通过它把id 为 content的部分给了这个contentParent  然后return
记得  PhoneWindow中的setContentView 方法是这么写的  
 mLayoutInflater.inflate(layoutResID, mContentParent);
把我们为Activity设置的布局(layoutResId)设置给mContentParent(上面return出来的contentParent)
到这里  我们已经把布局给放到DecorView中  接下来安卓系统就可以为我们显示界面了
在onResume()刚执行之后,界面还是不可见的,只有执行完Activity.makeVisible(),DecorView才对用户可见

最后拿一张别人的Android的窗口管理框架(转自ariesjzj










1 0