Android開發中Activity間數據傳遞方法匯總。在Activity間傳遞的數據一般比較簡單,但是有時候實際開發中也會傳一些比較復雜的數據,本節一起來學習更多Activity間數據的傳遞。
一、常用數據類型
在前面幾節我們隻學習瞭一些常用類型的數據傳遞,主要是以下這些重載方法:
putExtra(String name, boolean value)
putExtra(String name, byte value)
putExtra(String name, char value)
putExtra(String name, short value)
putExtra(String name, int value)
putExtra(String name, long value)
putExtra(String name, float value)
putExtra(String name, double value)
putExtra(String name, String value)
putExtra(String name, CharSequence value)
putExtras(Intent src)
putExtras(Bundle extras)
putExtra(String name, Bundle value)
getBooleanExtra(String name, boolean defaultValue)
getByteExtra(String name, byte defaultValue)
getCharExtra(String name, char defaultValue)
getShortExtra(String name, short defaultValue)
getIntExtra(String name, int defaultValue)
getLongExtra(String name, long defaultValue)
getFloatExtra(String name, float defaultValue)
getDoubleExtra(String name, double defaultValue)
getStringExtra(String name)
getCharSequenceExtra(String name)
getExtras()
getBundleExtra(String name)
可以發現主要包括boolean、byte、char、short、int、long、float、double、String、CharSequence幾個,當然也可以先將數據打包為Bundle或Intent對象再傳遞。
二、數組、列表類型數據
然而在實際開發中經常會遇見以上常用類型的數組或列表的組合型數據,其實也非常簡單。
1、數組
認真的同學可能已經發現瞭,每一個基本數據類型都有對應數組數據的重載方法,分別如下:
putExtra(String name, boolean[] value)
putExtra(String name, byte[] value)
putExtra(String name, short[] value)
putExtra(String name, char[] value)
putExtra(String name, int[] value)
putExtra(String name, long[] value)
putExtra(String name, float[] value)
putExtra(String name, double[] value)
putExtra(String name, String[] value)
putExtra(String name, CharSequence[] value)
getBooleanArrayExtra(String name)
getByteArrayExtra(String name)
getShortArrayExtra(String name)
getCharArrayExtra(String name)
getIntArrayExtra(String name)
getLongArrayExtra(String name)
getFloatArrayExtra(String name)
getDoubleArrayExtra(String name)
getStringArrayExtra(String name)
getCharSequenceArrayExtra(String name)
putExtra()方法的參數簡單替換為數組類型的即可,然後使用數組的專用方法獲取,使用起來也非常簡單。
2、列表
在傳遞列表型數據的時候稍微有一些不同瞭,Intent還提供瞭以下這幾個重載方法:
putIntegerArrayListExtra(String name, ArrayList value)
putStringArrayListExtra(String name, ArrayList value)
putCharSequenceArrayListExtra(String name, ArrayList value)
getIntegerArrayListExtra(String name)
getStringArrayListExtra(String name)
getCharSequenceArrayListExtra(String name)
從以上幾個方法可以知道,Intent自帶傳遞Integer、String、CharSequence三種類型的列表數據,如果需要傳遞到額數據是這幾種類型,或能夠轉換為這幾種類型,那麼數據的傳遞也變得很順利瞭。
三、對象
前面學習的幾個方法使用起來還是比較簡單的,但是會發現一個問題,以上學習的方法無法傳輸對象(如圖片)、對象的數組或集合,那就需要用到以下這些方法瞭。
intent還有以下這些重載方法:
putExtra(String name, Serializable value)
putExtra(String name, Parcelable value)
putExtra(String name, Parcelable[] value)
putParcelableArrayListExtra(String name, ArrayListvalue)
getSerializableExtra(String name)
getParcelableExtra(String name)
getParcelableArrayExtra(String name)
getParcelableArrayListExtra(String name)
可能你已經發現瞭,這裡提到的Serializable類型和Parcelable類型數據到底是什麼呢?接下來分別來學習。
1、序列化對象Serializable
Serializable接口是啟用其序列化功能的接口,實現java.io.Serializable 接口的類是可序列化的,沒有實現此接口的類將不能使它們的任一狀態被序列化或逆序列化(如果不懂序列化,建議復習鞏固Java部分的序列化知識模塊)。
Serializable實現序列化的方法也很簡單,將需要序列化的類實現Serializable接口,Serializable接口中沒有任何方法,隻需在類中指定serialVersionUID的值,該值可以任意指定一個值。可以理解為一個標記,即表明這個類可以序列化。
假如需要使用Intent傳遞一個Person對象,就先要將其序列化,如下示例代碼:
package com.jinyu.cqkxzsxy.android.activity.equipmentupdate.entity; import java.io.Serializable; /** * @創建者 鑫鱻 * @描述 Android零基礎入門到精通系列教程 * 首發微信公眾號分享達人秀(ShareExpert) */ public class Person implements Serializable { private static final long serialVersionUID = 1L; // 序列化ID private String name; // 姓名 private int age; // 年齡 public Person() { this.name = "未知"; this.age = 18; } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
然後即可調用前面的put和get方法來傳遞復雜對象數據瞭。
2、序列化對象Parcelable
由於Serializable在序列化的時候會產生大量的臨時變量,從而引起頻繁的GC,會影響持續性能。在使用內存的時候,Parcelable比Serializable性能高,所以推薦使用Parcelable。
實現Parcelable接口稍微復雜一些,但效率更高,推薦用這種方法提高性能。實現步驟如下:
將需要序列化的類實現Parcelable接口。
重寫writeToParcel方法,將對象序列化為一個Parcel對象。
重寫describeContents方法,描述內容接口,默認返回0。實例化靜態內部對象CREATOR實現接口Parcelable.Creator。其中public static final一個都不能少,內部對象CREATOR的名稱也不能改變,必須全部大寫。需重寫本接口中的兩個方法:
createFromParcel(Parcel in) 實現從Parcel容器中讀取傳遞數據值,封裝成Parcelable對象返回邏輯層。
newArray(int size) 創建一個類型為T,長度為size的數組,僅一句話即可(return new T[size]),供外部類反序列化本類數組使用。
接下來將上面的Person類進行改造,代碼如下:
package com.jinyu.cqkxzsxy.android.activity.equipmentupdate.entity; import android.os.Parcel; import android.os.Parcelable; /** * @創建者 鑫鱻 * @描述 Android零基礎入門到精通系列教程 * 首發微信公眾號分享達人秀(ShareExpert) */ public class Person implements Parcelable { private String name; // 姓名 private int age; // 年 protected Person(Parcel in) { // 在讀取Parcel容器裡的數據時,必須按成員變量聲明的順序讀取數據,不然會出現獲取數據出錯 name = in.readString(); age = in.readInt(); } public static final Creator CREATOR = new Creator() { // 再通過createFromParcel將Parcel對象映射成原對象 @Override public Person createFromParcel(Parcel in) { return new Person(in); } // 供外部類反序列化本類數組使用 @Override public Person[] newArray(int size) { return new Person[size]; } }; // 內容接口描述,默認返回0即可 @Override public int describeContents() { return 0; } // 按照聲明順序打包數據到Parcel對象中,既將數據打包到Parcel容器中 @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); dest.writeInt(age); } public Person() { this.name = "未知"; this.age = 18; } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
然後即可調用前面的put和get方法來傳遞復雜對象數據瞭,當然也可以是對象的數組或列表型數據。
在使用中需要註意的是,Parcelable不能使用在要將數據存儲在磁盤上的情況,因為Parcelable不能很好的保證數據的持續性在外界有變化的情況下。盡管Serializable效率低點,但此時還是建議使用Serializable。
四、全局Application
如果需要將一個對象在多個Activity之間傳遞,或者要連續傳遞好幾層,這種情況下如果使用以上方法就需要重復多次,使用起來就特別別扭,這種情況就可以考慮使用全局Application。
Android系統在每個程序運行的時候都會創建一個Application對象,而且隻會創建一個,所以Application 是單例(singleton)模式的一個類,而且Application對象的生命周期是整個程序中最長的,他的生命周期等於這個程序的生命周期。如果想存儲一些值,使用 Application就需要自定義類實現Application類,然後在AndroidManifest.xml中使用我們自定義的Application 而非系統默認的。
這裡簡單使用一個示例來學習,這裡簡化為全局保存一個狀態值,可以方便在各Activity中進行傳遞。首先自定義Application類,代碼如下:
package com.jinyu.cqkxzsxy.android.activity.equipmentupdate; import android.app.Application; /** * @創建者 鑫鱻 * @描述 Android零基礎入門到精通系列教程 * 首發微信公眾號分享達人秀(ShareExpert) */ public class MyApplication extends Application { private int state; public int getState() { return state; } public void setState(int state) { this.state = state; } }
然後在AndroidManifest.xml中聲明,為application標簽添加android:name屬性,代碼如下:
最後在需要使用定義的全局變量的地方即可調用,核心代碼如下:
package com.jinyu.cqkxzsxy.android.activity.equipmentupdate; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; public class TestActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_shop); // ... MyApplication app = (MyApplication) getApplicationContext(); // 保存數據 app.setState(1); // ... // 讀取數據 int state = app.getState(); // ... } }
這樣就非常方便的在各Activity之間進行數據傳遞瞭。如果想要在整個應用程序中任何位置都能使用,可以對MyApplication類進行適當的改造,這裡不做過多說明。
但是需要註意的是,當由於某些原因(比如系統內存不足),我們的app會被系統強制殺死,此時再次點擊進入應用時,系統會直接進入被殺死前的那個界面,制造一種從來沒有被殺死的假象。那麼問題來瞭,系統強制停止瞭應用,進程死瞭,那麼再次啟動時Application自然新的,那裡邊的數據自然木有啦,如果直接使用很可能報空指針或者其他錯誤。
所以在使用時一定要做好非空判斷,如果數據為空可以考慮邏輯上讓應用直接返回到最初的Activity。
五、單例模式
上面的Application就是基於單例的,單例模式的特點就是可以保證系統中一個類有且隻有一個實例。
這裡做一個簡單的示例,如定義一個數據持有者類,代碼如下:
package com.jinyu.cqkxzsxy.android.activity.equipmentupdate.entity; /** * @創建者 鑫鱻 * @描述 Android零基礎入門到精通系列教程 * 首發微信公眾號分享達人秀(ShareExpert) */ public class DataHolder { private String data; public String getData() { return data; } public void setData(String data) { this.data = data; } private static final DataHolder holder = new DataHolder(); public static DataHolder getInstance() { return holder; } }
然後在使用的地方即可直接調用,如下所示:
// 更新數據
DataHolder.getInstance().setData(data);
// 獲取數據
Stringdata=DataHolder.getInstance().getData();
這樣使用起來也非常簡單。
六、靜態變量
這個可以直接在Activity中完成單獨一個數據結構,和單例差不多。
這裡將上面的單例模式類簡單修改,代碼如下:
package com.jinyu.cqkxzsxy.android.activity.equipmentupdate.entity; /** * @創建者 鑫鱻 * @描述 Android零基礎入門到精通系列教程 * 首發微信公眾號分享達人秀(ShareExpert) */ public class DataHolder { private static String data; public static String getData() { return data; } public static void setData(String strData) { data = strData; } }
這樣就可以在啟動Activity之前設置數據,新的Activity中獲取數據。
// 更新數據
DataHolder.setData(data);
// 獲取數據
Stringdata=DataHolder.getData();
需要註意的是,如果數據很大很多(如bitmap),處理不當是很容易導致內存泄露或者內存溢出的,可以考慮使用WeakReferences 將數據包裝起來。
除瞭以上介紹的幾種方式,還可以使用持久化數據等方法,這裡先不做過多介紹,在後續的學習中會陸續接觸到。