溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

AndroidActivity之間的數(shù)據(jù)傳遞方法有哪些

發(fā)布時(shí)間:2021-11-26 15:21:43 來(lái)源:億速云 閱讀:121 作者:柒染 欄目:編程語(yǔ)言

這篇文章將為大家詳細(xì)講解有關(guān)AndroidActivity之間的數(shù)據(jù)傳遞方法有哪些,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。

前言

在Activity間傳遞的數(shù)據(jù)一般比較簡(jiǎn)單,但是有時(shí)候?qū)嶋H開(kāi)發(fā)中也會(huì)傳一些比較復(fù)雜的數(shù)據(jù),一起來(lái)學(xué)習(xí)更多Activity間數(shù)據(jù)的傳遞方法。

1、通過(guò) Intent 傳遞

我們?cè)谶M(jìn)行 Activity 跳轉(zhuǎn)時(shí),是要有 Intent,此時(shí) Intent 是可以攜帶數(shù)據(jù)的,我們可以利用它將數(shù)據(jù)傳遞給其它Activity。Intent 應(yīng)該是系統(tǒng)提供的支持類(lèi)型最廣,功能最全面的傳遞方式了?;緮?shù)據(jù)類(lèi)型、復(fù)雜數(shù)據(jù)類(lèi)型(如數(shù)組、集合)、自定義數(shù)據(jù)類(lèi)型等等都能支持,而且使用起來(lái)也不復(fù)雜。下面將分別介紹一下這幾種方法。

1.1、基本數(shù)據(jù)類(lèi)型傳遞

String 不是基本數(shù)據(jù)類(lèi)型,Java 的基本數(shù)據(jù)類(lèi)型有且僅有8種,Intent 都做了很好的支持。這8種基本類(lèi)型都有自己的包裝類(lèi)型(Wrap Class,復(fù)雜類(lèi)型),而且包裝類(lèi)型也實(shí)現(xiàn)了 Serializable 接口(后面再說(shuō)),使得 Intent 也能很好的支持包裝類(lèi)型。8種基本類(lèi)型及其包裝類(lèi)對(duì)應(yīng)關(guān)系如下:

容我煮個(gè)栗子:

假設(shè)有 Activity1,Activity2 兩個(gè) Activity;如果要在 Activity1 中啟動(dòng) Activity2,并傳過(guò)去幾個(gè)基本類(lèi)型的數(shù)據(jù),就可以這么寫(xiě):

Intent intent = new Intent(this, Activity2.class);intent.putExtra(String name, boolean value);intent.putExtra(String name, byte value);intent.putExtra(String name, char value);intent.putExtra(String name, short value);intent.putExtra(String name, int value);intent.putExtra(String name, float value);intent.putExtra(String name, long value);intent.putExtra(String name, double value);startActivity(intent);

在 Activity2 的 onCreate 中就可以通過(guò)如下方式接收:

Intent intent = getIntent();boolean bool = intent.getBooleanExtra(String name, boolean defaultValue);byte bt = intent.getByteExtra(String name, byte defaultValue);char ch = intent.getCharExtra(String name, char defaultValue);short sh = intent.getShortExtra(String name, short defaultValue);int i = intent.getIntExtra(String name, int defaultValue);float fl = intent.getFloatExtra(String name, float defaultValue);long lg = intent.getLongExtra(String name, long defaultValue);double db = intent.getDoubleExtra(String name, double defaultValue);

PS:上面發(fā)送和接收的時(shí)候,同一個(gè)字段必須使用相同的 name,比如:intent.putExtra("BOOLEAN", true);intent.getBooleanExtra("BOOLEAN", false);

1.2、復(fù)雜數(shù)據(jù)類(lèi)型傳遞

Java 中也定義了一些常用的復(fù)雜類(lèi)型,比如 String、基本數(shù)據(jù)類(lèi)型的數(shù)組、ArrayList、HashMap 等等,Intent 也對(duì)它們做了支持,使得我們能很容易的通過(guò) Intent 傳遞這些復(fù)雜類(lèi)型。方法與上面基本類(lèi)型類(lèi)似,比如:

intent.putExtra(String name, String value);intent.putExtra(String name, int[] value);intent.putExtra(String name, Parcelable value);intent.putExtra(String name, Serializable value);intent.putExtra(String name, CharSequence value);intent.putStringArrayListExtra(String name, ArrayList<String> value);

接收方式也類(lèi)似,這里就不再一一列舉了。

不過(guò),像 ArrayList、HashMap 這種,本身還能存放復(fù)雜類(lèi)型的數(shù)據(jù)結(jié)構(gòu),要想通過(guò) Intent 傳遞,得確保它們內(nèi)部存放的類(lèi)型也是能支持序列化和反序列化的。

1.3、自定義數(shù)據(jù)類(lèi)型傳遞

上面已經(jīng)列舉了很多 Intent 支持的類(lèi)型,但是默認(rèn)提供的這些類(lèi)型,總歸是不夠用的,很多時(shí)候我們會(huì)定義自己的數(shù)據(jù)類(lèi)型,比如定義一個(gè) Student:

public class Student{ public String name; public int age;}

那么這個(gè)時(shí)候我們應(yīng)該如何通過(guò)Intent來(lái)傳遞呢?

1.3.1、實(shí)現(xiàn) Serializable 接口

我們先看一下默認(rèn)提供并被 Intent 支持的復(fù)雜數(shù)據(jù)類(lèi)型的實(shí)現(xiàn)方式:

public final class String implements java.io.Serializable, Comparable<String>, CharSequencepublic class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializablepublic class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable

我們可以看到它們有一個(gè)共同的特點(diǎn),都 implement 了 Serializable 接口。

Serializable 是一個(gè)空接口,它沒(méi)有定義任何方法,知識(shí)用來(lái)標(biāo)記其實(shí)現(xiàn)類(lèi)是支持序列化和反序列化的。

因此當(dāng)我們想讓自定義的類(lèi)型也能通過(guò) Intent 傳遞時(shí),只需要讓該類(lèi)實(shí)現(xiàn) Serializable 接口即可。

依舊用 Student 來(lái)煮個(gè)栗子:

public class Student implements Serializable{ private static final long serialVersionUID = 1L; public String name; public int age;}

傳遞方法就是:

intent.putExtra(String name, Serializable value);intent.getSerializableExtra(String name);

PS:關(guān)于 Serializable 還有一些知識(shí)點(diǎn),比如:serialVersionUID、靜態(tài)變量序列化、transient 關(guān)鍵字、繼承問(wèn)題等等,這里就不介紹了,有興趣的可以自行去查閱。

1.3.2、實(shí)現(xiàn) Parcelable 接口

上面介紹了 Serializable 接口,但 Serializable 是 Java 的實(shí)現(xiàn),Android 下能正常使用,沒(méi)毛病,但 Google 覺(jué)得 Serializable 在 Android 內(nèi)存不大性能不強(qiáng)的情況下的效率不太夠,于是為 Android 量身定制了一個(gè)專(zhuān)用的接口——Parcelable。還是用 Student 來(lái)煮栗子:

要想實(shí)現(xiàn) Parcelable 接口,只需要先寫(xiě)好 Student 類(lèi)和屬性,然后讓 Student 實(shí)現(xiàn)Parcelable,再然后根據(jù) AS 的兩步提示:第一步重寫(xiě) describeContents 和 writeToParcel,第二步創(chuàng)建 CREATOR 就大功告成了。寫(xiě)好的類(lèi)如下:

public class Student implements Parcelable{ public String name; public int age; protected Student(Parcel in) { name = in.readString(); age = in.readInt(); } public static final Creator<Student> CREATOR = new Creator<Student>() { @Override public Student createFromParcel(Parcel in) { return new Student(in); } @Override public Student[] newArray(int size) { return new Student[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); dest.writeInt(age); }}

此時(shí)通過(guò) Intent 去傳遞就可以使用如下方法:

intent.putExtra(String name, Parcelable value);intent.getParcelableExtra(String name);

這兩種實(shí)現(xiàn)序列化的方法的使用原則:

1)在使用內(nèi)存的時(shí)候,Parcelable 比 Serializable 性能高,所以推薦使用 Parcelable。

2)Serializable 在序列化的時(shí)候會(huì)產(chǎn)生大量的臨時(shí)變量,從而引起頻繁的 GC。

3)Parcelable 不能使用在要將數(shù)據(jù)存儲(chǔ)在磁盤(pán)上的情況,因?yàn)?Parcelable 不能很好的保證數(shù)據(jù)的持續(xù)性在外界有變化的情況下。盡管 Serializable 效率低點(diǎn),但此時(shí)還是建議使用 Serializable 。

PS:Intent 還支持通過(guò) Bundle 封裝數(shù)據(jù),然后傳遞 Bundle,但是查看 intent.putExtra 的實(shí)現(xiàn),我們會(huì)發(fā)現(xiàn),其實(shí) intent.putExtra 的內(nèi)部也是維護(hù)的一個(gè) Bundle,因此,通過(guò) putExtra 放入的數(shù)據(jù),取出時(shí)也可以通過(guò) Bundle 去取。

2、通過(guò)全局變量傳遞

顧名思義,就是借助一個(gè)全局變量做中轉(zhuǎn),去傳遞數(shù)據(jù)。還是以前面的兩個(gè) Activity 為例,傳遞不支持序列化的 Student 對(duì)象。我們可以先創(chuàng)建一個(gè)工具類(lèi),比如:

public class Transmitter { public static Student student;}

那么傳遞和接收時(shí),就可以這么操作:

//傳遞Student stu = new Student();Transmitter.student = stu;Intent intent = new Intent(this, Activity2);startActivity(intent);//接收onCreate(...){ Student stu = Transmitter.student;}

可以看到使用起來(lái)非常的方便快捷。

但是,全局變量在 APP 運(yùn)行期間一直存在,如果通過(guò)全局變量存放的數(shù)據(jù)量比較大,變量個(gè)數(shù)多;并且在不需要使用后,沒(méi)有及時(shí)的將全局變量置為 null,好讓 GC 去回收,那么是有可能會(huì)引發(fā) OOM 問(wèn)題的。

因此,如果要使用全局變量來(lái)作為數(shù)據(jù)傳遞方法,那么就一定要注意維護(hù)好這些全局變量的狀態(tài)。

3、通過(guò) SharedPreferences 傳遞

SharedPreferences 是 Android 提供的一種實(shí)現(xiàn)數(shù)據(jù)存儲(chǔ)的方式,它可以將數(shù)據(jù)以 xml 格式存儲(chǔ)在機(jī)器中,通常用來(lái)存儲(chǔ) APP 的設(shè)置信息,我們也可以用它來(lái)實(shí)現(xiàn) Activity 間的數(shù)據(jù)傳遞。

但是,SharedPreferences 因其特殊的工作方式,只提供了對(duì)部分基本類(lèi)型和 String 的操作,對(duì)其它既有復(fù)雜類(lèi)型和自定義類(lèi)型是不支持的。它所支持的類(lèi)型只有:

booleanfloatintlongStringSet<String>

仍舊拿前面的兩個(gè) Activity 煮栗子,要實(shí)現(xiàn)它們之間的數(shù)據(jù)傳遞,只需要現(xiàn)在 Activity1 中,將數(shù)據(jù)放入 SharedPreferences,如下:

SharedPreferences sp = getSharedPreferences("FILENAME", MODE_PRIVATE);SharedPreferences.Editor editor = sp.edit();editor.putBoolean(String key, boolean value);editor.putFloat(String key, float value);editor.putInt(String key, int value);editor.putLong(String key, long value);editor.putString(String key, String value);editor.putStringSet(String key, Set<String> values);//editor.commit();editor.apply();startActivity(...);

然后在 Activity2 中通過(guò) SharedPreferences 將數(shù)據(jù)取出來(lái),如下:

SharedPreferences sp = getSharedPreferences("FILENAME", MODE_PRIVATE);sp.getBoolean(String key, boolean defValue);sp.getFloat(String key, float defValue);sp.getInt(String key, int defValue);sp.getLong(String key, long defValue);sp.getString(String key, String defValue);sp.getStringSet(String key, Set<String> defValue);

關(guān)于 SharedPreferences 有幾點(diǎn)需要注意:

1、getSharedPreferences("FILENAME", MODE_PRIVATE) 是通過(guò) Context 調(diào)用的,發(fā)送和接收的 FILENAME、MODE_PRIVATE 都要一致。

2、發(fā)送時(shí),往 SharedPreferences 存入數(shù)據(jù)后,需要提交,提交的方式有兩種:commit、apply,這兩個(gè)的區(qū)別如下:commit:同步操作,立即將修改寫(xiě)到 Storage,有 boolean 類(lèi)型返回值。

apply:立即刷新 In-memory 中的數(shù)據(jù),然后啟動(dòng)異步任務(wù)將修改寫(xiě)到 Storage,無(wú)返回值。

當(dāng)兩個(gè) apply 同時(shí)操作時(shí),后調(diào)用 apply 的將會(huì)被保存到 Storage 中;當(dāng)有 apply正在執(zhí)行時(shí),調(diào)用 commit,commit 將被阻塞,直到 apply 執(zhí)行完。

因 Android framework 已經(jīng)做好所有的事情,所以當(dāng)我們不需要關(guān)注提交操作的返回值時(shí),可以將 commit 無(wú)條件替換 apply 使用,而且 AS 也會(huì)建議將 commit 替換成 apply。

3、SharedPreferences 支持的數(shù)據(jù)類(lèi)型都必須是支持序列化操作的,上面提到的 Set<String>是一個(gè) interface,我們并不能直接實(shí)例化,但我們可以使用它的直接或間接實(shí)現(xiàn)類(lèi),比如:HashSet、TreeSet、LinkedHashSet等等。

我們查看這幾個(gè)的實(shí)現(xiàn),不難發(fā)現(xiàn),它們也都是實(shí)現(xiàn)了 Serializable 接口,支持序列化操作的:

public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializablepublic class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializablepublic class LinkedHashSet<E> extends HashSet<E> implements Set<E>, Cloneable, java.io.Serializable {

4、通過(guò) SystemProperties 傳遞

這個(gè)類(lèi)可以看做一個(gè)維護(hù)全局變量的類(lèi),只不過(guò)這里的全局變量是系統(tǒng)的,它們的值是 build.prop 文件里面的內(nèi)容。我們先看一下它的定義:

/** * Gives access to the system properties store. The system properties * store contains a list of string key-value pairs. * * {@hide} */public class SystemProperties

沒(méi)錯(cuò),這玩意是個(gè) hide 的類(lèi),那就意味著正常情況下 SDK 里面是沒(méi)有的,AS 里面也是訪問(wèn)不到的。不過(guò)我們還是可以通過(guò)一些手段去訪問(wèn)到它,比如反射、將源碼的庫(kù)導(dǎo)出到 AS 使用、將 APP 放在源碼中編譯等等。

這里我們就不關(guān)注用什么手段去訪問(wèn)它了,我們重點(diǎn)還是在利用它進(jìn)行 Activity 之間的數(shù)據(jù)傳遞。

假設(shè)我們是在源碼中編譯,還是用一開(kāi)始的兩個(gè) Activity 來(lái)煮栗子,發(fā)送數(shù)據(jù)時(shí)可以這么操作:

SystemProperties.set("NAME", "Shawn.XiaFei");startActivity(...);

接收時(shí)就可以這么寫(xiě):

SystemProperties.get("NAME");//或者SystemProperties.get("NAME", "defValue");

是不是很方便呢,不過(guò)別激動(dòng),我們看下 set 的實(shí)現(xiàn):

/** * Set the value for the given key. * @throws IllegalArgumentException if the key exceeds 32 characters * @throws IllegalArgumentException if the value exceeds 92 characters */public static void set(String key, String val) { if (key.length() > PROP_NAME_MAX) { throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); } if (val != null && val.length() > PROP_VALUE_MAX) { throw new IllegalArgumentException("val.length > " + PROP_VALUE_MAX); } native_set(key, val);}

看注釋?zhuān)瑳](méi)錯(cuò),key 和 val 都限制了長(zhǎng)度的?。?!當(dāng)然,32和92字符,在一般情況下也還是夠用的。但是下面就要說(shuō)一般 APP 開(kāi)發(fā)可能無(wú)法完成的事了。

前面說(shuō)了,這玩意是 SDK 不可見(jiàn)的,而且它維護(hù)的是系統(tǒng)的屬性值,系統(tǒng)屬性值 APP 可以讀,但不能輕易修改。因此上面 set 的時(shí)候,如果權(quán)限不夠就會(huì)報(bào)如下錯(cuò)誤:

Unable to set property "NAME" to "Shawn.XiaFei": connection failed; errno=13 (Permission denied)type=1400 audit(0.0:167): avc: denied { write } for name="property_service" dev="tmpfs" ino=10696 scontext=u:r:untrusted_app_25:s0:c512,c768 tcontext=u:object_r:property_socket:s0 tclass=sock_file permissive=0

這個(gè)錯(cuò)誤在 Rom 開(kāi)發(fā)中比較常見(jiàn),解決辦法就是配置相應(yīng)的 avc 權(quán)限,這一操作是一般 APP 開(kāi)發(fā)者無(wú)法進(jìn)行的。有興趣的可以自己去查資料,這里不做介紹。

5、通過(guò) SettingsProvider 傳遞

愛(ài)折騰的人可能注意到了 Android 設(shè)備上一般都會(huì)有這么一個(gè)應(yīng)用,它的作用是通過(guò)數(shù)據(jù)庫(kù)去維護(hù)一些系統(tǒng)配置信息。在 Rom 開(kāi)發(fā)中,通常借助它設(shè)置首次開(kāi)機(jī)的默認(rèn)行為。

通過(guò)它傳遞數(shù)據(jù)的關(guān)鍵在 android.provider.Settings 類(lèi),這個(gè)類(lèi)里面有 3 個(gè)常用的靜態(tài)內(nèi)部類(lèi),分別是:Global、System、Secure,它們分別對(duì)應(yīng)不同的權(quán)限等級(jí)。

煮栗子了:

發(fā)送時(shí),這么寫(xiě)就可以了:

/*Settings.System.putInt(ContentResolver cr, String name, int value);Settings.System.putString(ContentResolver cr, String name, String value);Settings.System.putFloat(ContentResolver cr, String name, float value);Settings.System.putLong(ContentResolver cr, String name, long value);*/Settings.Global.putString(getContentResolver(), "NAME", "Shawn.XiaFei");startActivity(...);

接收時(shí),就這么寫(xiě):

String name = Settings.Global.getString(getContentResolver(), "NAME");

使用起來(lái)也是很簡(jiǎn)單滴!不過(guò),使用起來(lái)雖然簡(jiǎn)單,但也并不是那么容易的。它也是要權(quán)限的!??!

如果權(quán)限不夠,運(yùn)行的時(shí)候就會(huì)報(bào)如下錯(cuò)誤:

java.lang.RuntimeException: Unable to start activity ComponentInfo{xxx.xxx/xxx.xxx.Activity1}: java.lang.SecurityException: Permission denial: writing to settings requires:android.permission.WRITE_SECURE_SETTINGSat android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2805)at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2883)at android.app.ActivityThread.-wrap11(Unknown Source:0)at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1613)at android.os.Handler.dispatchMessage(Handler.java:106)at android.os.Looper.loop(Looper.java:164)at android.app.ActivityThread.main(ActivityThread.java:6523)at java.lang.reflect.Method.invoke(Native Method)at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:857)

意思很明了,得給它 WRITE_SECURE_SETTINGS 的權(quán)限,我們?cè)囍?Manifest 里面添加一下,結(jié)果 AS 標(biāo)紅了,提示如下:

Permissions with the protection level signature or signatureOrSystem are only granted to system apps. If an app is a regular non-system app, it will never be able to use these permissions.

意思就是說(shuō),這個(gè)權(quán)限只有系統(tǒng) APP 才能獲得,三方 APP 沒(méi)戲。

6、通過(guò)數(shù)據(jù)庫(kù)傳遞

其實(shí)上面介紹的 SettingsProvider 方法,也是通過(guò)數(shù)據(jù)庫(kù)實(shí)現(xiàn)的,只不過(guò)它對(duì)數(shù)據(jù)庫(kù)的操作做了封裝,我們感覺(jué)不到而已。既然如此,我們也可以在自己 APP 中創(chuàng)建數(shù)據(jù)庫(kù),然后通過(guò)數(shù)據(jù)庫(kù)來(lái)實(shí)現(xiàn) Activity 之間的數(shù)據(jù)傳遞。栗子煮太多,吃不動(dòng),不煮了,有興趣的可以自己去查一下數(shù)據(jù)庫(kù)的知識(shí)。


7、通過(guò)文件傳遞

前面提到的 SharedPreferences 也是基于文件實(shí)現(xiàn)的,只不過(guò) SharedPreferences 是固定成 xml 格式的文件。我們也可以通過(guò)自定義文件操作方式去實(shí)現(xiàn)數(shù)據(jù)的存取,進(jìn)而實(shí)現(xiàn) Activity 之間的數(shù)據(jù)傳遞。說(shuō)了栗子不煮了,有興趣自己去查一下吧。



其實(shí) Activity 之間數(shù)據(jù)傳遞的方法還是很多的,也各有優(yōu)缺點(diǎn),但最最最最最常用的還是第一種—— Intent,其他方法都是理論可行,實(shí)際使用起來(lái)都會(huì)有點(diǎn)雞肋,或者得不償失。

因此要想掌握好 Activity 之間數(shù)據(jù)傳遞的技巧,個(gè)人覺(jué)得只需要掌握 Intent 的用法,能熟練使用,靈活處理就 OK 了。至于其它方法,能說(shuō)得出來(lái)原理就可以了。

關(guān)于AndroidActivity之間的數(shù)據(jù)傳遞方法有哪些就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI