Activity启动及窗口创建过程详解

有这么一个网站,能通过web方式查看Android源码.在你需要的时候,本地又没有源码的时候,通过这个网站可以很方便的查看到源码

网站地址:http://grepcode.com/project/repository.grepcode.com/java/ext/com.google.android/android


1. 应用的入口。 

 以前一直都说Activity的人口是onCreate方法。其实android上一个应用的入口,应该是ActivityThread。和普通的java类一样,入口是一个main方法。 

任何的apk有且仅有一个ActivityThread类,该类为应用程序的主线程类。

    public static final void main(String[] args) {
        SamplingProfilerIntegration.start();

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();
        if (sMainThreadHandler == null) {
            sMainThreadHandler = new Handler();
        }

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        Looper.loop();

        if (Process.supportsProcesses()) {
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }

        thread.detach();
        String name = (thread.mInitialApplication != null)
            ? thread.mInitialApplication.getPackageName()
            : "<unknown>";
        Slog.i(TAG, "Main thread of " + name + " is now exiting");
    }
下面仔细分析一下这个main方法。 

1.1.Looper.prepareMainLooper(); 

ActivityThread其实就是我们经常说的UI thread,也就是主线程。我们都知道主线程可以使用Handler进行异步通信,因为主线程中已经创建了Looper,而这个Looper就是在这里创建的。如果其他线程需要使用Handler通信,就要自己去创建Looper。 

1.2. sMainThreadHandler = new Handler(); 

创建一个Handler。 

ActivityThread thread = new ActivityThread(); 

创建ActivityThread 对象。 
ActivityThread 有几个比较重要的成员变量,会在创建ActivityThread对象时初始化。 
(1)final ApplicationThread mAppThread = new ApplicationThread(); 
ApplicationThread继承自ApplicationThreadNative, 而ApplicationThreadNative又继承自Binder并实现了IApplicationThread接口。IApplicationThread继承自IInterface。这是一个很明显的binder结构,用于于Ams通信。IApplicationThread接口定义了对一个程序(linux的进程)操作的接口。ApplicationThread通过binder与Ams通信,并将Ams的调用,通过下面的H类(也就是Hnalder)将消息发送到消息队列,然后进行相应的操作,入activity的start, stop。 
(2)final H mH = new H(); 
          private final class H extends Handler 

mH负责处理ApplicationThread发送到消息队列的消息,例如: 

      public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + msg.what);
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    ActivityClientRecord r = (ActivityClientRecord)msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo);
                    handleLaunchActivity(r, null);
                } break;
。。。

1.3. handleLaunchActivity(r, null); 

从名字中就可以看出,这里就将进行启动activity的工作了。 
方法中主要调用了这一句: 
Activity a = performLaunchActivity(r, customIntent); 

1.4. performLaunchActivity() 

(1)进行了一些初始化和赋值操作后,创建activity。 

 Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            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);
            }
        }

以上代码中使用ClassLoader从程序中加载指定的activity对应的class文件

(2)然后调用: 

activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstance,
                        r.lastNonConfigurationChildInstances, config);

attach的作用就是为刚构造好的Activity设置内部变量,还有一个主要的工作就是创建Window对象

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

        mWindow = PolicyManager.makeNewWindow(this);
        mWindow.setCallback(this);
        。。。

(3)创建好Window对象后,给Window对象的mWindowManager对象赋值

         接下来看setWindowManager在 Window.java里面

    public void setWindowManager(WindowManager wm,
            IBinder appToken, String appName) {
        mAppToken = appToken;
        mAppName = appName;
        if (wm == null) {
            wm = WindowManagerImpl.getDefault();
        }
        mWindowManager = new LocalWindowManager(wm);
    }
再进一步看 WindowManagerImpl.java
    public static WindowManagerImpl getDefault()
    {
        return mWindowManager;
    }
private static WindowManagerImpl mWindowManager = new WindowManagerImpl();

搞清楚了wm参数之后再看LocalWindowManager在Window.java中

private class LocalWindowManager implements WindowManager {
        LocalWindowManager(WindowManager wm) {
            mWindowManager = wm;
            mDefaultDisplay = mContext.getResources().getDefaultDisplay(
                    mWindowManager.getDefaultDisplay());
        }
。。。

实现WindowManager这个interface的有两个类,一个是LocalWindowManager,一个是WindowManagerImpl,

而LocalWindowManager只是一个壳而已,真正实现功能的是里面实现WindowManager接口的WindowManagerImpl类。


2、配置好Activity和Window之后,接下来给窗口添加显示视图View或ViewGroup,

还是在ActivityThread.java中performLaunchActivity方法里面

mInstrumentation.callActivityOnCreate(activity, r.state);

这一句就会调用到acitivity的onCreate方法了,就进入了大多数应用开发的入口了。


3、在activity的onCreate方法里面实际上又调用了其Window的方法

     在Activity.java中可以看到

    public void setContentView(int layoutResID) {
        getWindow().setContentView(layoutResID);
    }

getWindow()返回的是一个PhoneWindow类,接下来看PhoneWindow.java

    @Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mLayoutInflater.inflate(layoutResID, mContentParent);
        final Callback cb = getCallback();
        if (cb != null) {
            cb.onContentChanged();
        }
    }

关键是在下面

 private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor();
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);

            mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
            if (mTitleView != null) {
                if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
                    View titleContainer = findViewById(com.android.internal.R.id.title_container);
                    if (titleContainer != null) {
                        titleContainer.setVisibility(View.GONE);
                    } else {
                        mTitleView.setVisibility(View.GONE);
                    }
                    if (mContentParent instanceof FrameLayout) {
                        ((FrameLayout)mContentParent).setForeground(null);
                    }
                } else {
                    mTitleView.setText(mTitle);
                }
            }
        }
    }


    protected DecorView generateDecor() {
        return new DecorView(getContext(), -1);
    }

generateLayout这个方法内容比多,截取其中最重要的几行

        mDecor.startChanging();

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

        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }

该方法会做如下事情:

   根据窗口的风格修饰类型为该窗口选择不同的窗口布局文件(根视图),具体体现为代码中的代码中的layoutResource。这些窗口修饰布局文件指定一个用来存放Activity自定义布局文件的ViewGroup视图,一般为FrameLayout 其id 为: android:id="@android:id/content"。

        例如窗口修饰类型包括FullScreen(全屏)、NoTitleBar(不含标题栏)等。选定窗口修饰类型有两种:

           ①、指定requestFeature()指定窗口修饰符,PhoneWindow对象调用getLocalFeature()方法获取值;

           ②、为我们的Activity配置相应属性,即android:theme=“”,PhoneWindow对象调用getWindowStyle()方法

              获取值。

        举例如下,隐藏标题栏有如下方法:requestWindowFeature(Window.FEATURE_NO_TITLE);

                   或者 为Activity配置xml属性:android:theme=”@android:style/Theme.NoTitleBar”。

        PS:因此,在Activity中必须在setContentView之前调用requestFeature()方法。


  确定好窗口风格之后,选定该风格对应的布局文件,这些布局文件位于 frameworks/base/core/res/layout/  ,

        典型的窗口布局文件有:

          R.layout.dialog_titile_icons                       

          R.layout.screen_title_icons

          R.layout.screen_progress                           

          R.layout.dialog_custom_title

          R.layout.dialog_title   

          R.layout.screen_title         // 最常用的Activity窗口修饰布局文件

          R.layout.screen_simple    //全屏的Activity窗口布局文件


layoutResource的值确定了,contentParent即为@android:id/content对应的View,接下来就可以把应用中的layout.xml添加到mDecor中。具体是通过setContentView方法中 mLayoutInflater.inflate(layoutResID, mContentParent)这一句来实现的。

 最后调用cb.onContentChanged()通知窗口内容发生改变。cb即为Activity本身,Activity实现了Window.CallBack接口,因为在之前Activity的attach()方法中mWindow.setCallback(this)


4、Window类设置完视图之后,告诉Wms,Wms就把窗口显示在屏幕上了

   首先Activity准备好了会通知Ams,Ams经过一系列判断最终调用Activity的makeVisible方法

    void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
    }

  在上面的方法中,首先获得一个WindowManager对象,wm实际上就是Window.LocalWindowManager对象,该对象的addView方法第一个参数是DecroView对象即为一个Activity全部看的到的视图内容,第二个参数为Window初始化时默认构造的WindowManager.LayoutParams对象,具体代码在Window.java中如下

    public final WindowManager.LayoutParams getAttributes() {
        return mWindowAttributes;
    }

    // The current window attributes.
    private final WindowManager.LayoutParams mWindowAttributes =
        new WindowManager.LayoutParams();

而WindowManager中的构造方法

        public LayoutParams() {
            super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
            type = TYPE_APPLICATION;
            format = PixelFormat.OPAQUE;
        }

综上可见创建的窗口类型为TYPE_APPLICATION,为应用窗口类型。而在LocalWindowManager的addView方法中会先检查第二个参数(WindowManager.LayoutParams类型,以下用params替代,即为代码中的wp)的值并给其内部变量token赋值,以便于添加

 if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                if (wp.token == null) {
                    View decor = peekDecorView();
                    if (decor != null) {
                        wp.token = decor.getWindowToken();
                    }
                }

以上表示添加的为子窗口,如果token为空则把activity对应的窗口token赋值给 params

如果添加的不是子窗口则把mAppToken值赋值给params,若被父activity包含则把父activity的mAppToken赋值给params。

if (wp.token == null) {
                    wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
                }
检查完params参数后,接下来就调用WindowManagerImpl的addView方法。

由此可以看出前面创建Window时,为什么mWindowManager对象是一个Window.LocalWindowManager对象而不是一个WindowManagerImpl对象。

前者比后者多了检查params.token的关卡。


5.WindowManagerImpl的addView方法

一个应用程序无论有多少个Activity,但只有一个WindowManagerImpl对象,在其内部有三个数组用来保存该应用程序所拥有窗口的状态

View[] mViews ; 每一个View都将成为Wms所认为的一个窗口

ViewRoot[] mRoots;所有的ViewRoot对象,mViews中每个对象都对应的ViewRoot对象 

WindowManager.LayoutParams[] mParams;Wms要求每个添加的窗口对象都要对应一个参数,mParams保存了mViews对应的参数

    private void addView(View view, ViewGroup.LayoutParams params, boolean nest)
    {
        if (Config.LOGV) Log.v("WindowManager", "addView view=" + view);

        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException(
                    "Params must be WindowManager.LayoutParams");
        }

        final WindowManager.LayoutParams wparams
                = (WindowManager.LayoutParams)params;
        
        ViewRoot root;
        View panelParentView = null;
        
        synchronized (this) {
            // Here's an odd/questionable case: if someone tries to add a
            // view multiple times, then we simply bump up a nesting count
            // and they need to remove the view the corresponding number of
            // times to have it actually removed from the window manager.
            // This is useful specifically for the notification manager,
            // which can continually add/remove the same view as a
            // notification gets updated.
            int index = findViewLocked(view, false);
            if (index >= 0) {
                if (!nest) {
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
                root = mRoots[index];
                root.mAddNesting++;
                // Update layout parameters.
                view.setLayoutParams(wparams);
                root.setLayoutParams(wparams, true);
                return;
            }
            
            // If this is a panel window, then find the window it is being
            // attached to for future reference.
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews != null ? mViews.length : 0;
                for (int i=0; i<count; i++) {
                    if (mRoots[i].mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews[i];
                    }
                }
            }
            
            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.length + 1;
                Object[] old = mViews;
                mViews = new View[index];
                System.arraycopy(old, 0, mViews, 0, index-1);
                old = mRoots;
                mRoots = new ViewRoot[index];
                System.arraycopy(old, 0, mRoots, 0, index-1);
                old = mParams;
                mParams = new WindowManager.LayoutParams[index];
                System.arraycopy(old, 0, mParams, 0, index-1);
            }
            index--;

            mViews[index] = view;
            mRoots[index] = root;
            mParams[index] = wparams;
        }
        // do this last because it fires off messages to start doing things
        root.setView(view, wparams, panelParentView);
    }





  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值