• android系统启动框架、Activity界面显示过程详解


    一、Android系统框架

          android的系统架构和其操作系统一样,采用了分层的架构。从架构图看,android分为四个层,从高层到低层分别是应用程序层、应用程序框架层、系统运行库层和linux核心层。盗图如下:

                                                      

    具体每层的功能介绍如下:

         (1) 应用程序层

          该层提供一些核心应用程序包,例如电子邮件、短信、日历、地图、浏览器和联系人管理等。同时,开发者可以利用Java语言设计和编写属于自己的应用程序,而这些程序与那些核心应用程序彼此平等、友好共处。

         (2)应用程序框架层

         该层是Android应用开发的基础,开发人员大部分情况是在和她打交道。应用程序框架层包括活动管理器、窗口管理器、内容提供者、视图系统、包管理器、电话管理器、资源管理器、位置管理器、通知管理器和XMPP服务十个部分。在Android平台上,开发人员可以完全访问核心应用程序所使用的API框架。并且,任何一个应用程序都可以发布自身的功能模块,而其他应用程序则可以使用这些已发布的功能模块。基于这样的重用机制,用户就可以方便地替换平台本身的各种应用程序组件。

         (3) 系统库和Android运行时

         系统库包括九个子系统,分别是图层管理、媒体库、SQLite、OpenGLEState、FreeType、WebKit、SGL、SSL和libc。Android运行时包括核心库和Dalvik虚拟机,前者既兼容了大多数Java语言所需要调用的功能函数,又包括了Android的核心库,比如android.os、android.net、android.media等等。后者是一种基于寄存器的java虚拟机,Dalvik虚拟机主要是完成对生命周期的管理、堆栈的管理、线程的管理、安全和异常的管理以及垃圾回收等重要功能。

         (4) Linux内核

         Android核心系统服务依赖于Linux2.6内核,如安全性、内存管理、进程管理、网络协议栈和驱动模型。Linux内核也是作为硬件与软件栈的抽象层。驱动:显示驱动、摄像头驱动、键盘驱动、WiFi驱动、Audio驱动、flash内存驱动、Binder(IPC)驱动、电源管理等。

          Android的系统架构采用分层架构的思想,架构清晰,层次分明,协同工作。因此,若想从事Android应用开发,则研究研究Android的应用框架层和应用程序层;若想从事Android系统开发,那研究下Android系统库和Android运行时;若想从事Android驱动开发,那试着看看Android的Linux内核。下面分别介绍android系统启动流程和Activity界面显示流程:

    二、Android系统启动流程

          众所周知,在Linux中,它的启动可以归为一下几个流程: Boot Loader——>初始化内核——>创建init进程——>根据inittable文件执行其他的启动项——>..... 。

          由此可知,当初始化内核之后,就会启动一个相当重要的祖先进程,也就是init进程,在Linux中所有的进程都是由init进程直接或间接fork出来的。而对于Android来说,前面的流程都是一样的:

     (1)当init进程创建之后,会fork出一个Zygote进程,这个进程是所有Java进程的父进程。我们知道,Linux是基于C的,而Android是基于Java的(当然底层也是C);

     (2)然后fork出一个Zygote Java进程用来fork出其他的进程。当Zygote(孵化进程)被初始化的时候,会fork出System Server进程,这个进程在整个的Android进程中是非常重要的一个,地位和Zygote等同,它是属于Application Framework层的,Android中的所有服务,例如AMS, WindowsManager, PackageManagerService等等都是由这个SystemServer fork出来的。所以它的地位可见一斑;

     (3)当System Server进程开启的时候,就会初始化AMS,同时,会加载本地系统的服务库,创建系统上下文,创建ActivityThread及开启各种服务等等。而在这之后,就会开启系统的Launcher程序,完成系统界面的加载与显示;

     (4)系统启动完成后,当我们点击屏幕时,触摸屏的两层电极会连接在一起,也就产生了一个电压并通过对应的驱动把当前按压点的XY坐标传给android系统。系统在获取到XY值的时候,就会对按压点的范围进行一个判断,如果确定按压点处于一个APP图标或者是Button等等的范围中时,操作系统也就会认为用户当前已经点击了这个东西,启动对应的监听。而当系统判断我们点击的是APP图标时,该App就由Launcher开始启动了;

      (5)当开始启动时,Launcher进程会采用Binder的方式向AMS(Activity Manager Server)发出startActivity请求;

      (6)AMS在接收到请求之后,就会通过Socket向Zygote进程发送创建进程的请求;

      (7)Zygote进程会fork出新的子进程(APP进程);

      (8)随后APP进程会再向AMS发起一次请求,AMS收到之后经过一系列的准备工作再回传请求;

      (9)APP进程收到AMS返回的请求后,会利用Handler向主线程(即UI线程)发送LAUNCH_ACTIVITY消息;

    注:主线程(也即ActivityThread)里面存在一个main()方法,这也是APP的真正入口,当APP启动时,就会启动ActivityThread中的main方法,在main方法中系统会通过Looper.prepareMainLooper()来创建主线程的Looper以及MessageQueue,并通过Looper.loop来开启消息循环。

     (10)主线程在收到消息之后,就创建目标Activity,并回调onCreate()/onStart()/onResume()等方法,UI渲染结束后便可以看到App主界面。

    三、界面显示流程

    setContentView() 

       在Activity的onCreate()方法中,第一句即是写setContentView(),它的具体实现和功能如何呢?这里就要对你当前继承的Activity分类了:

     (1) 如果是继承的Activity,那么setContentView源码是这样的:

        /**
        * Set the activity content from a layout resource. The resource will be
        * inflated, adding all top-level views to the activity.
        *
        * @param layoutResID Resource ID to be inflated.
        *
        * @see #setContentView(android.view.View)
        * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
        */
        public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
        }
        /**
        * Set the activity content to an explicit view. This view is placed
        * directly into the activity's view hierarchy. It can itself be a complex
        * view hierarchy. When calling this method, the layout parameters of the
        * specified view are ignored. Both the width and the height of the view are
        * set by default to {@link ViewGroup.LayoutParams#MATCH_PARENT}. To use
        * your own layout parameters, invoke
        * {@link #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}
        * instead.
        *
        * @param view The desired content to display.
        *
        * @see #setContentView(int)
        * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
        */
        public void setContentView(View view) {
        getWindow().setContentView(view);
        initWindowDecorActionBar();
        }
        /**
        * Set the activity content to an explicit view. This view is placed
        * directly into the activity's view hierarchy. It can itself be a complex
        * view hierarchy.
        *
        * @param view The desired content to display.
        * @param params Layout parameters for the view.
        *
        * @see #setContentView(android.view.View)
        * @see #setContentView(int)
        */
        public void setContentView(View view, ViewGroup.LayoutParams params) {
        getWindow().setContentView(view, params);
        initWindowDecorActionBar();
        }

          从以上代码可以看出一共存在着3个重载函数,且不管你调用哪一个,最后都会调用到initWindowDecorActionBar()这个方法。 由setContentView上面的注释可知,它会按照一个layout 布局资源去设置Activity的内容,而这个布局资源将会被引入然后添加所有顶级的Views到这个Activity当中。下图copy了一张Activity层级图:

                                                         

          由上图可知,最底层是Activity,它包含里面的所有东西,再上一层是一个PhoneWindow,这个PhoneWindow是由Window类派生出来的,每一个PhoneWindow中都含有一个DecorView对象,Window是一个抽象类。 再上面一层就是一个DecorView,我理解这个DecorView就是一个ViewGroup,就是装View的容器。 且在DecoreView中,最上面的View就是我们的TitleActionBar,下面就是我们要设置的content。所以在上面的initWindowDecorActionBar就能猜到是什么意思了吧。而在initWindowDecorActionBar方法中,有下面一段代码:

        /**
        * Creates a new ActionBar, locates the inflated ActionBarView,
        * initializes the ActionBar with the view, and sets mActionBar.
        */
        private void initWindowDecorActionBar() {
        Window window = getWindow();
        // Initializing the window decor can change window feature flags.
        // Make sure that we have the correct set before performing the test below.
        window.getDecorView();
        if (isChild() || !window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) {
        return;
        }
        mActionBar = new WindowDecorActionBar(this);
        mActionBar.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp);
        mWindow.setDefaultIcon(mActivityInfo.getIconResource());
        mWindow.setDefaultLogo(mActivityInfo.getLogoResource());
        }

          注意上面的window.getDecoreView()方法的注释,该方法会设置一些window的标志位,而当这个方法执行完之后,就再也不能更改了,这也就是为什么很多第三方SDK设置window的标志位时一定要求要在setContentView方法前调用。
    (2)对于一个新的AppcompatActivity,这个Activity里面包含了一些新特性。在AppcompatActivity中,setContentView是这样的:

        @Override
        public void setContentView(@LayoutRes int layoutResID) {
        getDelegate().setContentView(layoutResID);
        }
        @Override
        public void setContentView(View view) {
        getDelegate().setContentView(view);
        }
        @Override
        public void setContentView(View view, ViewGroup.LayoutParams params) {
        getDelegate().setContentView(view, params);
        }

          一样的3个重载函数,只是里面没有了上面的那个init方法,取而代之的是一个getDelegate().setContentView,delegate(即委托/回调)的源码是这样的:

        /**
        * @return The {@link AppCompatDelegate} being used by this Activity.
        */
        @NonNull
        public AppCompatDelegate getDelegate() {
        if (mDelegate == null) {
        mDelegate = AppCompatDelegate.create(this, this);
        }
        return mDelegate;
        }
        而在AppCompatDelegate.Create方法中,则会返回一个很有意思的东西:
        /**
        * Create a {@link android.support.v7.app.AppCompatDelegate} to use with {@code activity}.
        *
        * @param callback An optional callback for AppCompat specific events
        */
        public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
            return create(activity, activity.getWindow(), callback);
        }
        private static AppCompatDelegate create(Context context, Window window,
                              AppCompatCallback callback) {
           final int sdk = Build.VERSION.SDK_INT;
          if (sdk >= 23) {
            return new AppCompatDelegateImplV23(context, window, callback);
          } else if (sdk >= 14) {
            return new AppCompatDelegateImplV14(context, window, callback);
          } else if (sdk >= 11) {
            return new AppCompatDelegateImplV11(context, window, callback);
          } else {
            return new AppCompatDelegateImplV7(context, window, callback);
          }
       }

     findViewById()

         我们通过一个findViewById方法可以实现对象的绑定,那它底层究竟是怎么实现的呢?findViewById根据继承的Activity类型的不同也存在着区别:

    (1)我们的Activity继承Activity类时

        /**
        * Finds a view that was identified by the id attribute from the XML that
        * was processed in {@link #onCreate}.
        *
        * @return The view if found or null otherwise.
        */
        @Nullable
        public View findViewById(@IdRes int id) {
          return getWindow().findViewById(id);
        }

          从源码来看,findViewById的功能是通过一个view的id属性查找view。它同样包含getWindow()方法,说明findViewById()实际上Activity把它也是交给了自己的window来做。而对于getWindow()中的函数代码如下:

        /**
        * Finds a view that was identified by the id attribute from the XML that
        * was processed in {@link android.app.Activity#onCreate}. This will
        * implicitly call {@link #getDecorView} for you, with all of the
        * associated side-effects.
        *
        * @return The view if found or null otherwise.
        */
        @Nullable
        public View findViewById(@IdRes int id) {
        return getDecorView().findViewById(id);
        }

          由以上代码可以看出,又调用了getDecorView的findViewById()方法,这也相当于是一个层层传递的过程,因为DecorView可以理解为就是一个ViewGroup,而当运行getDecorView().findViewById()方法时,就会运行View里面的findViewById方法。它会使用这个被给予的id匹配子View的Id,如果匹配,就返回这个View,完成View的绑定。代码如下:

        /**
        * Look for a child view with the given id. If this view has the given
        * id, return this view.
        *
        * @param id The id to search for.
        * @return The view that has the given id in the hierarchy or null
        */
        @Nullable
        public final View findViewById(@IdRes int id) {
        if (id < 0) {
        return null;
        }
        return findViewTraversal(id);
        }
        /**
        * {@hide}
        * @param id the id of the view to be found
        * @return the view of the specified id, null if cannot be found
        */
        protected View findViewTraversal(@IdRes int id) {
        if (id == mID) {
        return this;
        }
        return null;
        }

        由以上分析可知,Activity中的findViewById()调用过程是这样的: Activity -> Window -> DecorView -> View。

  • 相关阅读:
    端口
    IDEA 快捷建
    Correct the classpath of your application so that it contains a single, compatible version of javax.
    Consider renaming one of the beans or enabling overriding by setting
    Idea 关于SpringBoot的@AutoWired 注入问题--无法自动装配Could not autowire. No beans of 'UserMapper' type found. more...
    数据库相关问题
    Initialization failed for 'https://start.spring.io' Please check URL, network and proxy settings. E
    爬虫
    CMS总结
    rust 打印当前时间
  • 原文地址:https://www.cnblogs.com/snow-flower/p/6111599.html
Copyright © 2020-2023  润新知