美文网首页Android FrameworkAndroidAndroid面试
Android APP启动过程分析(1)——Activity、W

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

作者: thinkChao | 来源:发表于2017-05-04 21:30 被阅读703次

    前言:本篇文章通过对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()##

    performLaunchActivity()执行完了之后,接着会执行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、http://www.voidcn.com/blog/u012827296/article/p-4991222.html

    2、http://www.jianshu.com/p/687010ccad66

    3、http://www.jianshu.com/p/98c98d1ae3c3

    相关文章

      网友评论

      本文标题:Android APP启动过程分析(1)——Activity、W

      本文链接:https://www.haomeiwen.com/subject/golrtxtx.html