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。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM