android内核剖析 创建窗口过程读书笔记

在介绍窗口创建之前,先要介绍几个Android Framework中比较重要的概念,和后面的事件分发等一系列都有很重要的关系

ActivityThread类:首先ActivityThread并不是一个线程,它所在的线程就是Adnroid的UI线程,Activity的实例化,looper的实例化,都在ActivityThread的main()入口完成

Activity:这个就不多介绍了,APK的一个最小运行单元

PhoneWindow:window的子类,内部包含了一个DecorView(FrameLayout)。window类提高一些API帮助Activity管理窗体。例如我们常用的setContentView()就简介的调用了PhoneWindow的setContentView()

DecorView:是FrameLayout的一个子类,同时也是PhoneWindow的内部类。它可以帮助Activity传递一些事件等。后面在分析事件传递会讲到。它也是phoneWindow的根视图

ViewRoot:是一个handler,handler最本质的最用就是异步处理,处理来自W(ViewRoot.stub)类接收到的WMS的消息,并通过dispatchMessage来异步处理消息。

W类:ViewRoot的内部类,继承于Binder,用于接收WMS的消息。


了解了这些知识,我们就可以正式开始了解窗口的创建了。

在Framework中定了三种窗口类型:应用窗口,子窗口和系统窗口

我们这主要是介绍应用窗口的创建。


首先每个应用窗口都需要对应一个Activity对象,因此我们在创建窗口之前需要创建一个Activity对象。Activity说白了也是一个对象,但是我们在使用Activity的时候,都没有见过new Activity的操作,只要实现onCreate这些生命周期的函数就可以了。那是因为Activity的实例化在android.app.ActivityThread.performLaunchActivity()已经完成了。让我们来看看源码。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

    ...
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
        StrictMode.incrementExpectedActivityCount(activity.getClass());
        r.intent.setExtrasClassLoader(cl);
        if (r.state != null) {
            r.state.setClassLoader(cl);
        }
    } catch (Exception e) {
        if (!mInstrumentation.onException(activity, e)) {
            throw new RuntimeException(
                "Unable to instantiate activity " + component
                + ": " + e.toString(), e);
        }
    }

    try {
        ...
        if (activity != null) {
            ...

            activity.attach(appContext, this, getInstrumentation(), r.token,
                    r.ident, app, r.intent, r.activityInfo, title, r.parent,
                    r.embeddedID, r.lastNonConfigurationInstances, config);
            ...
            activity.mCalled = false;
            mInstrumentation.callActivityOnCreate(activity, r.state);
            ...
        }
        ...
    }
    ...
}
首先我们看到ClassLoader会从程序中加载我们所写好的class文件,并用这个文件,还有Intent新建了一个Activity。这里也告诉了我们,为什么Intent能传递数据,我们在Activity调用的getIntent()是怎么来的。

然后调用了Activity的attach方法,为Activity内部一些重要的变量赋值

appContext,这个就是前面讲的作为Activity的baseContext,它不是contextWrapper,而是ContextImpl。

this指的是当前的ActivityThread对象

r.Token指的的ActivityRecord对象。Token的本质都是Ibinder,用于完成IPC调用

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) {
    attachBaseContext(context);

    mFragments.attachActivity(this, mContainer, null);

    mWindow = PolicyManager.makeNewWindow(this);

    ... //将各种参数赋给Activity的成员变量

    mWindow.setWindowManager(
            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
            mToken, mComponent.flattenToString(),
            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    if (mParent != null) {
        mWindow.setContainer(mParent.getWindow());
    }
    mWindowManager = mWindow.getWindowManager();
    mCurrentConfig = config;
}
然后我们在进入attch方法内部看看。当给一些重要成员变量赋值之前,首先Activity会给内部创建一个Window对象,也就是我们之前提到的PhoneWindow对象。 首先给Activity. mWindow 成员变量赋值,然后给mWindow变量设置WindowManager,然后给Activity. mWindowManager 赋值。

每一个Window内部都有一个WindowManager。这个操作类似我们的Aactivity继承的ContextWrapper。Window内部的是LocalWindowManager,它仅仅只是引用了WindowManagerImpl对象,真正实现的逻辑都在WindowManagerImpl里面。这种设计思想在Android内部已经不是第一次见到了。

然后上面的attach()方法调用完成后,就自然而然的调用了Activity的onCreate()方法了。进入onCreate之后,对于我们这些Android应用开发人员来说就应该很熟练了,调用setContextView(R.layout.xx),然后一堆的findViewById()。但是我们有没想过为什么setContentView这个方法调用了就能把布局引进来。让我们点进去看看

1 public void setContentView(View view, ViewGroup.LayoutParams params) {
2  getWindow().setContentView(view, params);
3     initActionBar();
4 }
原来是调用了phonewindow的setContentView,那接着点进去看看phoneWindow的setContentView是怎么工作的

@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
    if (mContentParent == null) {
        installDecor();
    } else {
        mContentParent.removeAllViews();
    }
    mContentParent.addView(view, params);
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
}
首先installDecor为window安装了一个窗口修饰

private void installDecor() {
    if (mDecor == null) {
        mDecor = generateDecor();
        ...
    }
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);
        ...
    }
}
protected DecorView generateDecor() {
2     return new DecorView(getContext(), -1);
3 }
为我们刚提到的DecorView赋值

然后又在generateLayout中调用

  View in = mLayoutInflater.inflate(layoutResource, null);
    decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));

    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);  
把刚指定的布局给引进来,到这我们就把需要展示的视图给准备好了,接下来就是通知WMS把我们需要的窗口给创建好。

 
 
void makeVisible ( ) {
         if ( ! mWindowAdded ) {
             ViewManager wm = getWindowManager ( ) ;
             wm . addView ( mDecor , getWindow ( ) . getAttributes ( ) ) ;
             mWindowAdded = true ;
         }
         mDecor . setVisibility ( View . VISIBLE ) ;
     }
获得windowManager对象,然后把decorView加到wm中,并且设置其可见。

然后在addView的时候,我们会new一个ViewRoot对象,然后调用它的setView

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
            mView = view;
            ...

            // Schedule the first layout -before- adding to the window
            // manager, to make sure we do the relayout before receiving
            // any other events from the system.
            requestLayout();

            ...

            view.assignParent(this);
            ...
        }
    }
}
这里还调用了requestLayout对界面进行了重绘请求。

最后也是最关键的得到sWindowSession对象,这个对象本质我想大家也应该猜到了是一个bindler,它是WmS中的session子类,只要调用它的add就能将我们创建好的window显示到屏幕上了。

有些人可能想问了为啥不直接获得一个windowSession对象,就能添加一个窗口了,还要进行前面那么漫长的过程。Framework为了怕我们乱创建窗口,这个对象是无法直接访问的,就想我们在写应用的时候,无法得到Activity的对象,它的权限是一个包内访问。这就意味着,你只有走完了之前所有的流程才能创建一个窗口




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值