Android四大組件的工作過程

         Android四大組件對Android開發者開說再熟悉不過了,他們是ActivityServiceBroadcastReceiverContentProvider。當具有一定開發實踐後會發現很多情況情況下,只有對Android體系結構有一定認識,在實際開發中才能寫出更加優秀的代碼,否者總是隻知其表,不知其裏,難免有一種霧裏看花的感覺。


 1.四大組件的運行狀態

        Android四大組件中BroadcastReceiver既可以在AndroidMenifest.xml中也可以通過java代碼完成註冊,兩者分別稱爲靜態註冊和動態註冊。而其他三者必須在AndroidMenifest.xml中完成註冊。從調用方式來講,ActivityServiceBroadcastReceiver都需要藉助Intent,而ContentProvider不需要藉助Intent

        Activity的調用分爲顯示和隱式兩種,顯式調用無非就是使用startActivity方法,可以明確地指向一個Activity組件,簡單易用不再介紹。隱式調用具體實現在《AndroidIntentFilter的匹配規則》中已有詳細描述,不在贅述。但隱式Intent指向一個或多個Activity組件,當然也可能沒有Activity符合篩選規則。Activity組件的主要作用是展示一個界面並和用戶交互,它扮演的其實是一個前臺界面的角色。

       Service是一種計算型組件,用於在手機後臺執行一系列計算任務。Service有兩種狀態,分爲啓動狀態和綁定狀態兩種,兩者的不同之處在於後者情況下外界可以很方便的和Service組件進行通信,而前者的Service不需要和外界有直接的交互。但需要注意一點,Service本身是處於主線程的,所以耗時的後臺計算仍然需要放到單獨的線程中完成。

       BroadcastReceiver是一種消息型組件,用於在不同的組件乃至不同的應用之間傳遞消息,他事實上工作在系統內部。靜態註冊的廣播不需要應用啓動就可以接收相應的廣播。動態廣播需要通過Context.registerReceiver()來實現,並且在不需要時使用Context.unRegisterReceiver()來解除廣播,這種形式的廣播必須啓動應用才能完成註冊並接收廣播。發送和接收過程的匹配通過廣播接收者的<intent-filter>完成。可以發現,廣播其實是一種低耦合的觀察者模式,也不適合做耗時的操作。

       ContentProvider是一種數據共享型的組件,用於向其他組件或者其他應用提供數據。ContentProvider提供增刪改查四個方法,內部維持一個數據集合,這個集合可以使用數據庫實現,也可以使用其他任何類型,沒有什麼要求。因爲這四個方法是在Binder線程池中被調用的,所以處理好線程同步是很重要的。

 

2.Activity的工作過程

        ActivitystartActivity()有好幾種重載方式,這在我們調用它的時候就會發現,但該方法內部都會調用startActivityForReult方法。下面是截取該方法的部分代碼

public void startActivityForResult(Intent intent , int requestCode, @Nullable Bundle options)

{

    If(mParent == null){

       Instrumentation.ActivityResult ar =

            mInstrumentation.execStartActivity(......);

       .......

     }

     .......

}

         mParent代表的是ActivityGroup,API 13之後基本使用Fragment取代ActivityGroup了。下面再看exceStartActivity方法,該方法有一個ApplicationThread類型的參數,而ApplicationThread實際上是ActivityThread的一個內部類,瞭解Activity啓動模式的朋友就會發現ApplicationThreadActivityThreadActivity的啓動過程中多麼重。在該方法中有兩句重要的代碼:

......

int result = ActivityManagerNative.getDefault().startActivity(.......);

checkStartActivityResult(result,intent);

......

        第二句很明顯是檢查啓動Activity的結果,如果無法正確的啓動一個Activity就會拋出異常錯誤。啓動Activity的時候有一個常見錯誤“Unable to find explicit activity class;have you declared this activity int your AndroidMenifest.xml”,就是從這拋出的,看到這有疑惑的朋友你是不是豁然開朗了。

   既然啓動Activity是上面的第一句代碼完成的,ActivityManagerNative.getDefault()的具體實現的ActivityManagerService(簡稱AMS),所以查看一下AMSstartActivity方法即可。在該方法中又繞了一大圈,最終回到AplicationThreadscheduleLaunchActivity()啓動ActivityscheduleLaunchActivity方法中發送一個啓動Activity的消息交給Handler處理,這個Handler的名字很簡潔,叫H。後面介紹其他三個組件的啓動時都會用到H。處理這個啓動消息的的時候會先判斷是否Application已經創建過了,如果已經創建過了就不會重複創建了,這也就揭示了爲什麼一個應用只會有一個ApplicationApplication創建完畢後,系統緊接着調用ApplicationonCreate方法。接着會通過Activityattach方法來完成一些重要數據的初始化,完成Window的創建並關聯Activity,這樣當Window接收到外部事件後就可以將事件傳遞給Activity。最後就是調用ActivityonCreate方法,完成整個啓動過程。

 

3.Service的啓動過程

        Service有啓動和綁定兩種狀態,值得注意的是,這兩種狀態是可以共存的,也就是說Service可以同時處於啓動和綁定狀態

啓動一個Service如下所示:

Intent intentService = new Intent(this,oneService.class);

startService(intentService);

通過綁定的方式啓動一個Service

Intent intentService = new Intent(this,oneService.class);

bindService(intentService,mServiceConnection,BIND_AUTO_CREATE);

         Service的啓動從ContextWrapperstartService方法開始,該方法又調用了ContextImplstartService方法。之前已經說過Context的具體實現交給了ContextImpl來完成,ContextImplstartService源碼很複雜,分析Service的啓動過程沒必要一句一句代碼的深究。Activity的啓動類似,最終都是發送消息給Handler H來完成啓動。H通過ActivityThreadhandleCreateService完成Service的啓動。

         handleCreateService主要完成下面幾件事:

1.通過類加載器創建Service實例;

2.創建Application對象,並調用ApplicationonCreate方法,當然Application只能創建一次;

3.創建ConTextImpl並通過Serviceattach建立兩者的關係,注意ServiceActvity都是一個Context

4.最後調用ServiceonCreate方法,並將Service對象保存到ActivityThread的一個ArrayMap列表中。到這裏Service已經啓動了,下面就是Service的綁定過程。

          Service的綁定同樣是從ContextWrapper開始的,再最終調用ContextImplbandServiceCommon方法。這個方法主要做了下面的兩件事:

 · ServiceConnection對象轉化爲ServiceDiapatcher.InnerConnection。這是因爲服務的綁定可能是跨進程的,所以ServiceConnection必須藉助Binder才能讓遠程服務端回調自己的方法,InnerConnection正好充當Binder這個角色。在ServiceDiapatcher中保存了ServiceConnectionInnerConnection對象。

·  bindServiceCommon通過AMSbindService完成具體的綁定過程。AMS通過一系列的調用,最終調用了ApplicationThreadSchedualBindServiceApplicationThread中一系列由schedule開頭的方法都是通過Handler H來中轉的,這次也不例外。在H內部,會交給ActivityThreadhandleBindService處理,首先根據Servicetoken取出Service對象,然後調用ServiceonBind方法,onBind方法會返回一個Binder對象給客戶端使用。這個時候客戶端並不知道已經成功連接Service了,所以需要調用客戶端中ServiceConnectiononServiceConnected方法。到這裏,Service的綁定過程就算完成了。

 

4.BroadcastReceiver的工作過程

   先回顧一下BroadcastReceiver的使用方法,首先要定義廣播的接收者,只需要繼承BroadcastReceiver並重寫onReceiver方法即可。比如:

 

public class oneReceiver extends BroadcastReceiver{

   @Override

   public void onReceiver(Context context,Intent intent){

       //onReceiver不能做耗時的工作

      Log.e(“......”,intent.getAction()+””);

   }

}

 

定義過接收者後,還需要註冊廣播接收者

·靜態註冊

  <receiver android:name= “.oneReceiver”>

      <intent-filter>

           <action android:name = “com.ryg.receiver.LAUNCH”/>

      <intent-filter>

  </receiver>

·動態註冊

  IntentFilter filter = new IntentFilter();

  filter.addAction(“com.ryg.receiver.LAUNCH”);

  registerReceiver(new oneReceiver(),filter);

·發送廣播

  Intent intent = new Intent();

  intent.setAction(“com.rgy.receiver.LAUNCH”);

  sendBroadcast(intent);

         當使用send方法發送廣播時,AMS會查找出匹配的廣播接收者並把廣播發送給他們處理。廣播分爲普通廣播、有序廣播和粘性廣播。最普遍的還是普通廣播。

   廣播的發送和接受實質是一個過程的兩個階段。在Ansroid5.0中,默認情況不會發送廣播給已經停止的應用,實際上在Android3.0就爲Intent添加了兩個標誌位,分別是:

·FLAG_INCLUDE_STOPPED_PACKAGES,表示廣播會發送給已經停止的應用

·FLAG-EXCLUDE_STOPPED_PACKAGES,表示廣播不會發送給已經停止的應用

  系統默認爲Intent添加了第二種標誌位,以免廣播無意間或者喚起不必要的應用。如果確實要喚起已經停止的應用,爲廣播添加上面的第一種標誌位即可。當兩種標誌位共存時,廣播會發送給已經停止的應用。而最終廣播發送的實現還是在前面提到過的Handler H類中完成。

 

5.ContentProvider的工作過程

        ContentProvider是一種內容共享型組件,他通過Binder向其他組件乃至其他應用提供數據。ContentProvider所在的進程啓動時,ContentProvider會同時啓動,並且要注意ContentProvideronCreate是優先於ApplicationonCreate執行的。這和前面介紹的三個組件是完全不同的。

   當一個應用啓動時,入口方法爲ActivityThreadmain方法,在main方法中會創建ActivityThread的實例並創建主線程的消息隊列。在ActivityThreadhandleBandApplication方法中,ActivityThread會創建Application對象並加載ContentProvider。這裏要注意的是,ActivityThread會先加載ContentProvider再調用ApplicationonCreate方法。流程圖如下:

 

 

        ContentProvider是否是單實例是由他的multiprocess屬性決定的。androidmultiprocessfalseContentProvider是單實例的,當他是true的時候,ContentProvider就是多實例。多實例是指每個調用者的進程都會有一個ContentProvider對象,但這樣會帶來進程間通信的開銷,所以大部分還是使用單實例。

   當ContentProvider所在進程還未啓動時,第一次訪問他會出發ContentProvider的創建,當然也伴隨着ContentProvider所在進程的啓動。ContentProvider四個方法中的任何一個都會觸發啓動ContentProvider,下面以查詢query爲例。

   在query方法中會調用ActivityThreadacquireProvider方法,該方法會先從ActivityThread中查找是否已經存在目標ContentProvider了,如果存在直接返回。如果目前ContentProvider還沒啓動,那麼久發送一條進程間請求給AMS讓其啓動ContentProvider。啓動ContentProvider的時候會先啓動它所在的進程(也可以理解爲是應用),然後再啓動當前進程的ContentProviderAMS會把ContentProvider存儲在ProviderMap,這樣一來調用者就可以直接從AMS中獲取ContentProvider了。

發佈了29 篇原創文章 · 獲贊 4 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章