Android APP 启动过程分析(1)——Activity、Window、DecorView

1,207 阅读7分钟
原文链接: www.jianshu.com

前言:本篇文章通过对APP启动过程(从点击图标—>Activity可见)的分析,来搞懂这一过程遇到的几个重要的知识点,以解心中的疑惑,同时通过对整个过程的分析,让孤立的知识点串联起来,有个更好的理解和掌握。

一、程序入口——ActivityThread.main()

Android程序的入口是ActivityThread类中的main()方法,就是当我们点击一个APP图标时,系统最开始执行的地方。以后当被人问到这个问题时,就不要回答“Activity的onCreate()方法”了。

public final class ActivityThread {

    ......

    public static void main(String[] args) {
       .....
        Looper.prepareMainLooper();      //1

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

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();        //3
        }
      ......
    }  

     ......

    final ApplicationThread mAppThread = new ApplicationThread();      //4
    final Looper mLooper = Looper.myLooper();
    final H mH = new H()
 }

执行的操作

1、调用prepareMainLooper()在主线程中创建一个消息队列。
2、创建ActivityThread对象。
3、创建Handler对象。
4、创建ApplicationThread对象

过程讲解

1、ActivityThread:每个应用程序都对应着一个ActivityThread实例,其中的main()方法是程序的入口,通过一系列的初始化操作,它最终的操作是通过调用ActivityThread.handleMessage()方法接收系统传来的消息,创建Activity。
Q:消息是由谁传递过来的?请看2.

2、ApplicationThread:每个应用程序对应着一个ApplicationThread对象,它是一个Binder对象,Binder是用来进行IPC(进程间的通信)操作的。所以它的作用是接收AMS传来的远程消息,例如AMS发送star某个Activity,然后ApplicationThread再通过Handler(线程间的通信)传递给ActivityThreadActivityThread调用handleMessage()方法来处理这个消息,进行创建Activity的操作。由此可见,ApplicationThreadActivityThread与AMS之间信息传递的桥梁。
Q:AMS又是啥?请看3.

3、AMS(ActivityManagerService):AMS是Android中的一个核心服务,主要负责系统中四大组建(有的文章说不包括BroadcastReceiver)的启动、切换、调度等工作。所以当我们点击打开一个APP时,AMS会向ApplicationThread发送请求开启一个Activity,然后由ApplicationThread传递给ActivityThread来进行具体操作,上面我们已经提到。

现在我们已经知道了消息传递的大体流程,当ActivityThread收到AMS传来的启动一个Activity的消息后,就会调用handleMessage()方法创建指定的Activity,具体如何做的呢?

public final class ActivityThread {

    ......

      public void handleMessage(Message msg) {

            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                ......
                    handleLaunchActivity(r, null);
                ......
                break;
                    }
            }
      }     
  }

从代码可以看出,它调用了handleLaunchActivity()方法,那我们进入看看。

public final class ActivityThread {

        ......

        Activity a = performLaunchActivity(r, customIntent);
        ......
        if (a != null) {
            ......
            handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed);
            ......
        }
         .......
    }
 }

该方法又调用了performLaunchActivity()方法来返回一个Activity,想必Activity应该就是在这个函数中创建完成的了。此方法执行后,又调用了handleResumeActivity()方法,我们先看performLaunchActivity()做了什么。

public final class ActivityThread {

    ......

   private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ......

        Activity activity = null;
       ......
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
       ......
        return activity;
    }
 }

二、performLaunchActivity()

performLaunchActivity()做了哪些事

1、从ActivityClientRecord中得到接下来要启动Activity的ComponentName信息。

2、从ActivityClientRecord中得到接下来要启动Activity的信息
通过Instrumentation.newActivity方法,利用LoadedApk的类加载器尝试创建Activity的对象。

3、通过LoadedApk.makeApplication方法尝试创建Application对象,在这个方法里会通过Class.newInstance()方法创建Application的对象,并且它的onCreate方法也是在此时被调用的。

4、为Activity创建ContextImpl的对象,调用Activity.attach()方法。

5、调用Activity.setTheme方法,如果Activity申明时指定了theme的话。

6、调用Activity.onCreate()方法,然后是onStart()方法,如果之前有保存的状态那么还会调用onRestoreInstanceState()方法,最后调用onPostCreate()方法,至此整个launch过程算是结束了。

进入onCreate()方法

下面终于进入到onCreate()方法了,首要执行的就是setContentView(R.layout.main),这个方法的目的是设置我们在xml文件中定义的布局,我们进入这个方法看看。

    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }
      ……
    private Window mWindow;

    mWindow = new PhoneWindow(this, window);

    public Window getWindow() {
        return mWindow;
    }

该方法中调用了mWindow()setContentView()方法,因为我们可以从代码中看出getWindow()返回的是mWindowmWindowWindow类型的,而Window是一个抽象类,它的实现类是PhoneWindow。所以, getWindow().setContentView()方法就是调用的PhoneWindowsetContentView()方法,然后我们进入看看。

    // This is the view in which the window contents are placed. It is either
    // mDecor itself, or a child of mDecor where the contents go.
    private ViewGroup mContentParent;


    public void setContentView(View view, ViewGroup.LayoutParams params) {

        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            view.setLayoutParams(params);
            final Scene newScene = new Scene(mContentParent, view);
            transitionTo(newScene);
        } else {
            mContentParent.addView(view, params);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }

首先,判断mContentParent是否为空,为空则执行installDecor()mContentParent是什么?从代码中的注释可以看到说“mContentParent是mDecor本身或者是mDecor的一个子元素
”,mDcorDecorView的实例,大家可能还不明白,所以要先搞清DecorView是什么,然后我们继续往下看,看看installDecor()方法做了什么。

private void installDecor() {
    if (mDecor == null) {
        mDecor = generateDecor(); // 1
        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        mDecor.setIsRootNamespace(true);
        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
        }
    }
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor); // 2
        ...
        } 
    }
}

从代码可以看出,它调用了generateDecor()方法:

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

好了,可以看出,这里实例化出了一个DecorView,那DecorView是什么呢?

    private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
}

由代码可见,DecorView继承自FrameLayout,而FrameLayout继承自ViewGroup,所以DecorView是一个ViewGroup。其实,它是ViewTree的最顶层的一个View,它是一个FrameLayout布局,代表了整个应用的界面,由TitlView和ContentView两个子元素,而ContentView则是上面提到的mContentParent,那为什么刚才注释里说“mContentParent是mDecor本身或者是mDecor的一个子元素
”呢?我们进入installDecor()中注释的2号代码:

protected ViewGroup generateLayout(DecorView decor) {
        // Apply data from current theme.
        // 从主题文件中获取样式信息
        TypedArray a = getWindowStyle();

        ...

        if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
            requestFeature(FEATURE_NO_TITLE);
        } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
            // Don't allow an action bar if there is no title.
            requestFeature(FEATURE_ACTION_BAR);
        }

        if(...){
            ...
        }

        // Inflate the window decor.
        // 加载窗口布局
        int layoutResource;
        int features = getLocalFeatures();
        // System.out.println("Features: 0x" + Integer.toHexString(features));
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            layoutResource = R.layout.screen_swipe_dismiss;
        } else if(...){
            ...
        }

        View in = mLayoutInflater.inflate(layoutResource, null);    //加载layoutResource
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); //往DecorView中添加子View,即mContentParent
        mContentRoot = (ViewGroup) in;

        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); // 这里获取的就是mContentParent
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }

        if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
            ProgressBar progress = getCircularProgressBar(false);
            if (progress != null) {
                progress.setIndeterminate(true);
            }
        }

        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            registerSwipeCallbacks();
        }

        // Remaining setup -- of background and title -- that only applies
        // to top-level windows.
        ...

        return contentParent;
    }

由以上代码可以看出:首先根据设置的主题样式来设置DecorView的风格,比如说有没有titlebar之类的,接着为DecorView添加子View,而这里的子View则是上面提到的mContentParent,如果上面设置了FEATURE_NO_ACTIONBAR,那么DecorView就只有mContentParent一个子View,这也解释了上面的疑问:mContentParent是DecorView本身或者是DecorView的一个子元素。


DecorView

三、handleResumeActivity()

```
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) { 
    //...
    ActivityClientRecord r = performResumeActivity(token, clearHide); // 这里会调用到onResume()方法

    if (r != null) {
        final Activity a = r.activity;

        //...
        if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow(); // 获得window对象
            View decor = r.window.getDecorView(); // 获得DecorView对象
            decor.setVisibility(View.INVISIBLE);
            ViewManager wm = a.getWindowManager(); // 获得windowManager对象
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            if (a.mVisibleFromClient) {
                a.mWindowAdded = true;
                wm.addView(decor, l); // 调用addView方法
            }
            //...
        }
    }
}

该方法内部会先获得与该Activity关联的几个对象:
1、WIndow对象
2、DecorView对象
3、WindowManager对象,它的实现类是WIndowManagerImpl
接着会调用WIndowManagerImpladdView()方法:

public final class WindowManagerImpl implements WindowManager {    
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    ...
    @Override
    public void addView(View view, ViewGroup.LayoutParams params) {
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }
}

这里面又调用了mGlobaladdView()方法,mGlobalWindowManagerGlobal的一个实例,我们进入该方法:

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        ...

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            ...

            root = new ViewRootImpl(view.getContext(), display); // 1

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
        }

        // do this last because it fires off messages to start doing things
        try {
            root.setView(view, wparams, panelParentView); // 2
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            synchronized (mLock) {
                final int index = findViewLocked(view, false);
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
            }
            throw e;
        }
    }

1、首先实例化一个ViewRootImpl类。

2、然后调用ViewRootImplsetView()方法,把DecorView作为参数传进去。在这里,ViewRootImplWMSDecorView之间沟通的一个桥梁。

3、在这个方法内部,会通过跨进程的方式向WMS(WindowManagerService)发起一个调用,从而将DecorView最终添加到Window上。

4、最后通过WMS调用ViewRootImpl.performTraverals方法开始View的测量、布局、绘制流程。这个下一篇再讲解。


通过前面的讲解,我们可以如此总结:

Android应用程序窗口 = Activity + PhoneWindow + DecorView

DecorView = TitleView + ContentView(layout.xml文件)

画图表示如下(每一层的大小都是一样的,是覆盖关系,这里是为了方便表示出每一层):


Android应用程序窗口模型

参考:
1、www.voidcn.com/blog/u01282…

2、www.jianshu.com/p/687010cca…

3、www.jianshu.com/p/98c98d1ae…