從源碼剖析Android中的Intent組件_Android

來源:互聯網
上載者:User

我們知道,Intent主要用來啟用安卓幾大組件,那麼它具體是怎樣來啟用的?啟用時是否可以攜帶java對象?為何要將對象序列化後才能傳遞?

一、Intent官網解釋
Intent可以被startActivity用來載入Activity,也可以被broadcastIntent發送給指定的BroadReceiver組件,
或者被startService、bingService來與後台service通訊。
Intent最主要作用就是載入Activity,好比Activity之間的膠水。
Intent資料結構:

  • action:所要執行的動作;(例如:ACTION_CALL建立打電話Activity;ACTION_BATTERY_LOW 發出廣播警告電池電量低,)
  • data: 要使用的資料(Uri);
  • category:關於目標組件的資訊;
  • component:目標組件的類名;
  • extras :這是Bundle資料。

Intent解析:

  • 顯式Intent,指定了目標組件的類名,即component,則已知目標組件,不需解析;
  • 隱式Intent,未指定目標組件component,或者不知道、不關心誰來接收Intent,需要Android自己去解析找到目標組件。

隱式Intent解析方法:

1.在AndroidManifest.xml裡所有<intent-filter>及其中定義的Intent;
2.通過PackageManager(擷取當前裝置所安裝的應用程式package)尋找能處理這個Intent的component。匹配Action、type、category三個變數來尋找。
二、簡單解釋:
Intent可以啟用Andorid的三大組件:Activity、Service和BroadcastReceiver。使用Intent時一般要顯式指定目標組件,若未指定則要根據Intent附帶的action、type、category三個值來解析,尋找能處理的組件。

三、問題:Intent如何?組件的切換,具體流程?
1、基本方法:(以啟動Activity為例)

Intent i = new Intent(MainActivity.this, TargetActivity.class);startActivity(i);

2、執行個體化Intent:

/*** 建立一個Intent,直接指定Intent要啟用的**組件類名**,而不用依賴**系統去解析**合適的類來處理intent* @param packageContext 要執行這個intent的context對象* @param cls intent要啟用的組件類名*/public Intent(Context packageContext, Class cls) {//建立一個組件並賦值給Intent的Component成員  mComponent = new ComponentName(packageContext, cls);}

3、啟動Activity

startActivity(i) ->startActivity(Intent intent, @Nullable Bundle options)->startActivityForResult(intent, -1, options)/** *startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options)* 載入一個Activity並擷取結果* @param intent 要啟動的intent.* @param requestCode 如果大於0則會被返回,且只有傳回值返回成功後才會顯示視圖* @param options 其他資訊.*/public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {if (mParent == null) { //如果沒有父Activity;Instrumentation是用來與程式指南清單AndroidManifest檔案互動的。  Instrumentation.ActivityResult ar =  mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this,intent, requestCode, options); //執行startActivity命令..... } else { //如果有父Activity   if (options != null) {     mParent.startActivityFromChild(this, intent, requestCode, options);   } .....}

4、執行startActivity命令核心代碼:
啟動Activity的任務交給了底層ActivityManagerNative來做。

intent.migrateExtraStreamToClipData(); //將intent裡的bundle資料進行處理以便給底層處理intent.prepareToLeaveProcess(); //準備離開應用程式進程,進入ActivityManagerService進程(意味著bundle的資料要在進程間傳遞)int result = ActivityManagerNative.getDefault().startActivity(whoThread,   who.getBasePackageName(), intent,intent.resolveTypeIfNeeded(who.getContentResolver()),token, target != null ? target.mEmbeddedID : null,requestCode, 0, null, options); //調用系統的activity manager服務來啟動新的Activity。考慮如果是顯式Intent,則直接找對對應的組件類(此處是Activity組件);如果是隱式Intent,為指定目標組件類名,則自動去Application->system搜尋合適的組件來處理。//todo:具體的系統級代碼下次進行分析

四、核心問題:為何Intent不能直接在組件間傳遞對象而要通過序列化機制?
根據上面代碼可以看到,Intent在啟動其他組件時,會離開當前應用程式進程,進入ActivityManagerService進程(intent.prepareToLeaveProcess()),這也就意味著,Intent所攜帶的資料要能夠在不同進程間傳輸。首先我們知道,Android是基於Linux系統,不同進程之間的java對象是無法傳輸,所以我們此處要對對象進行序列化,從而實現對象在 應用程式進程 和 ActivityManagerService進程 之間傳輸。
而Parcel或者Serializable都可以將對象序列化,其中,Serializable使用方便,但效能不如Parcel容器,後者也是Android系統專門推出的用於處理序間通訊等的介面。

附加知識:
在不同進程之間,常規資料類型可以直接傳遞,如整數,以傳遞字串為例,要從A進程傳遞到B進程,只需在B進程的記憶體區開闢一樣大小的空間,然後複製過去即可。
但是,對象卻不能直接跨進程傳遞。即使成員變數值能傳遞過去,成員方法是無法傳遞過去的,此時如果B進程要調用成員方法則出錯。
具體傳遞對象的方法:
1. 在進程A中把類中的非預設值的屬性和類的唯一標誌打成包(這就叫序列化);
2. 把這個包傳遞到進程B;
3. 進程B接收到包後,根據類的唯一標誌把類建立出來(java反射機制);
4. 然後把傳來的屬性更新到類對象中。
這樣進程A和進程B中就包含了兩個完全一樣的類對象。

五、Intent如何?對象傳遞?
Object implements Serializable {...};bundle.putSerializable(Key, Object);
Object implements Parcelable {...} ; bundle.putParcelable(Key, Object);
Serializable介面:這是Java的序列化技術,將Java對象序列化為二進位檔案。讓對象實現Serializable介面,使用ObjectInputStream 和 ObjectOutputStream 進行對象讀寫。
Parcelable介面:這是Android提供的用作封裝資料的容器,封裝後的資料可以通過Intent或IPC來傳遞。只有基本類型和實現了Parcelable介面的類才能被放入Parcel中。
六、Serializable介面 - Java
屬於java序列化機制:只需讓java類實現該介面,不用實現任何方法,即可標記該類可序列化。

class Person implements Serializable {...}Person per = new Person();bundle.putSerializable("person", per); //傳遞Person對象的引用Person mPerson = (Person)getIntent().**getSerializableExtra**("person");

注意:如果此處序列化類別Person內部包含其他類(如:PersonInfo)的引用,如:

class Person implements Serializable {   PersonInf**o info;}

那麼所引用的類必須也可序列化,即實現Serializable介面。因為Person對象在序列化過程中,也會對成員變數序列化。

七、Parcelable介面 - Android
此處圍繞 - Android中如何使用Parcel實現對象的傳遞 - 簡單介紹一下原因。
首先要瞭解Android裡面的Parcel容器。

Parcel是一個容器,用來儲存可通過IBindler傳送的訊息(資料或對象引用)。
主要用於輕量級、高效能IPC處理序間通訊的訊息容器。在Android裡,一個“process”是一個標準Linux進程,一般而言一個進程無法接觸到另一個進程的記憶體區。而通過Parcel,Android系統會將對象分解成可序列化與還原序列化,從而實現處理序間通訊。
不過,Parcel同樣可用於進程內通訊,主要實現在應用程式的不同組件之間傳遞資料。例如,我們可以使用Intent封裝Parcel對象在Activity之間傳遞。
簡單來說,Parcel容器實現了進程內與處理序間通訊,而且還能實現遠程調用。

組件間傳遞對象的具體方法:

讓要傳遞的對象所屬類實現 Parcelable 介面;
實現 describeContents 方法;
實現抽象方法 writeToParcel,用於擷取對象的目前狀態並寫入一個Parcel容器中;
給該目標類添加一個靜態域 CREATOR ,它是一個實現了Parcelable.Creator介面的對象;
添加一個參數為一個Parcel對象的建構函式,CREATOR會調用這個建構函式來重新改造我們的對象。
問題:
為什麼已經有了Java的Serializable介面還要建立一個Parcelable介面?
效能
雖然Parcelable使用起來更複雜一點,但是它的效能更好。

Parcelable的限制:

當使用Parcelable來傳遞圖片Bitmap時不太理想,雖然Bitmap也實現了Parcelable介面。比較優的方法是傳遞
Parcelable不能用來當做常規的序列化儲存,因為Android系統版本不同,Parcelable的具體實現方法也不完全一樣,可能導致無法讀取Parcel資料。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.