您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關AndroidActivity之間的數據傳遞方法有哪些,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
前言
在Activity間傳遞的數據一般比較簡單,但是有時候實際開發中也會傳一些比較復雜的數據,一起來學習更多Activity間數據的傳遞方法。
1、通過 Intent 傳遞
我們在進行 Activity 跳轉時,是要有 Intent,此時 Intent 是可以攜帶數據的,我們可以利用它將數據傳遞給其它Activity。Intent 應該是系統提供的支持類型最廣,功能最全面的傳遞方式了。基本數據類型、復雜數據類型(如數組、集合)、自定義數據類型等等都能支持,而且使用起來也不復雜。下面將分別介紹一下這幾種方法。
1.1、基本數據類型傳遞
String 不是基本數據類型,Java 的基本數據類型有且僅有8種,Intent 都做了很好的支持。這8種基本類型都有自己的包裝類型(Wrap Class,復雜類型),而且包裝類型也實現了 Serializable 接口(后面再說),使得 Intent 也能很好的支持包裝類型。8種基本類型及其包裝類對應關系如下:
容我煮個栗子:
假設有 Activity1,Activity2 兩個 Activity;如果要在 Activity1 中啟動 Activity2,并傳過去幾個基本類型的數據,就可以這么寫:
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 中就可以通過如下方式接收:
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:上面發送和接收的時候,同一個字段必須使用相同的 name,比如:intent.putExtra("BOOLEAN", true);intent.getBooleanExtra("BOOLEAN", false);
1.2、復雜數據類型傳遞
Java 中也定義了一些常用的復雜類型,比如 String、基本數據類型的數組、ArrayList、HashMap 等等,Intent 也對它們做了支持,使得我們能很容易的通過 Intent 傳遞這些復雜類型。方法與上面基本類型類似,比如:
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);
接收方式也類似,這里就不再一一列舉了。
不過,像 ArrayList、HashMap 這種,本身還能存放復雜類型的數據結構,要想通過 Intent 傳遞,得確保它們內部存放的類型也是能支持序列化和反序列化的。
1.3、自定義數據類型傳遞
上面已經列舉了很多 Intent 支持的類型,但是默認提供的這些類型,總歸是不夠用的,很多時候我們會定義自己的數據類型,比如定義一個 Student:
public class Student{ public String name; public int age;}
那么這個時候我們應該如何通過Intent來傳遞呢?
1.3.1、實現 Serializable 接口
我們先看一下默認提供并被 Intent 支持的復雜數據類型的實現方式:
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
我們可以看到它們有一個共同的特點,都 implement 了 Serializable 接口。
Serializable 是一個空接口,它沒有定義任何方法,知識用來標記其實現類是支持序列化和反序列化的。
因此當我們想讓自定義的類型也能通過 Intent 傳遞時,只需要讓該類實現 Serializable 接口即可。
依舊用 Student 來煮個栗子:
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:關于 Serializable 還有一些知識點,比如:serialVersionUID、靜態變量序列化、transient 關鍵字、繼承問題等等,這里就不介紹了,有興趣的可以自行去查閱。
1.3.2、實現 Parcelable 接口
上面介紹了 Serializable 接口,但 Serializable 是 Java 的實現,Android 下能正常使用,沒毛病,但 Google 覺得 Serializable 在 Android 內存不大性能不強的情況下的效率不太夠,于是為 Android 量身定制了一個專用的接口——Parcelable。還是用 Student 來煮栗子:
要想實現 Parcelable 接口,只需要先寫好 Student 類和屬性,然后讓 Student 實現Parcelable,再然后根據 AS 的兩步提示:第一步重寫 describeContents 和 writeToParcel,第二步創建 CREATOR 就大功告成了。寫好的類如下:
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); }}
此時通過 Intent 去傳遞就可以使用如下方法:
intent.putExtra(String name, Parcelable value);intent.getParcelableExtra(String name);
這兩種實現序列化的方法的使用原則:
1)在使用內存的時候,Parcelable 比 Serializable 性能高,所以推薦使用 Parcelable。
2)Serializable 在序列化的時候會產生大量的臨時變量,從而引起頻繁的 GC。
3)Parcelable 不能使用在要將數據存儲在磁盤上的情況,因為 Parcelable 不能很好的保證數據的持續性在外界有變化的情況下。盡管 Serializable 效率低點,但此時還是建議使用 Serializable 。
PS:Intent 還支持通過 Bundle 封裝數據,然后傳遞 Bundle,但是查看 intent.putExtra 的實現,我們會發現,其實 intent.putExtra 的內部也是維護的一個 Bundle,因此,通過 putExtra 放入的數據,取出時也可以通過 Bundle 去取。
2、通過全局變量傳遞
顧名思義,就是借助一個全局變量做中轉,去傳遞數據。還是以前面的兩個 Activity 為例,傳遞不支持序列化的 Student 對象。我們可以先創建一個工具類,比如:
public class Transmitter { public static Student student;}
那么傳遞和接收時,就可以這么操作:
//傳遞Student stu = new Student();Transmitter.student = stu;Intent intent = new Intent(this, Activity2);startActivity(intent);//接收onCreate(...){ Student stu = Transmitter.student;}
可以看到使用起來非常的方便快捷。
但是,全局變量在 APP 運行期間一直存在,如果通過全局變量存放的數據量比較大,變量個數多;并且在不需要使用后,沒有及時的將全局變量置為 null,好讓 GC 去回收,那么是有可能會引發 OOM 問題的。
因此,如果要使用全局變量來作為數據傳遞方法,那么就一定要注意維護好這些全局變量的狀態。
3、通過 SharedPreferences 傳遞
SharedPreferences 是 Android 提供的一種實現數據存儲的方式,它可以將數據以 xml 格式存儲在機器中,通常用來存儲 APP 的設置信息,我們也可以用它來實現 Activity 間的數據傳遞。
但是,SharedPreferences 因其特殊的工作方式,只提供了對部分基本類型和 String 的操作,對其它既有復雜類型和自定義類型是不支持的。它所支持的類型只有:
booleanfloatintlongStringSet<String>
仍舊拿前面的兩個 Activity 煮栗子,要實現它們之間的數據傳遞,只需要現在 Activity1 中,將數據放入 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 中通過 SharedPreferences 將數據取出來,如下:
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);
關于 SharedPreferences 有幾點需要注意:
1、getSharedPreferences("FILENAME", MODE_PRIVATE) 是通過 Context 調用的,發送和接收的 FILENAME、MODE_PRIVATE 都要一致。
2、發送時,往 SharedPreferences 存入數據后,需要提交,提交的方式有兩種:commit、apply,這兩個的區別如下:commit:同步操作,立即將修改寫到 Storage,有 boolean 類型返回值。
apply:立即刷新 In-memory 中的數據,然后啟動異步任務將修改寫到 Storage,無返回值。
當兩個 apply 同時操作時,后調用 apply 的將會被保存到 Storage 中;當有 apply正在執行時,調用 commit,commit 將被阻塞,直到 apply 執行完。
因 Android framework 已經做好所有的事情,所以當我們不需要關注提交操作的返回值時,可以將 commit 無條件替換 apply 使用,而且 AS 也會建議將 commit 替換成 apply。
3、SharedPreferences 支持的數據類型都必須是支持序列化操作的,上面提到的 Set<String>是一個 interface,我們并不能直接實例化,但我們可以使用它的直接或間接實現類,比如:HashSet、TreeSet、LinkedHashSet等等。
我們查看這幾個的實現,不難發現,它們也都是實現了 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、通過 SystemProperties 傳遞
這個類可以看做一個維護全局變量的類,只不過這里的全局變量是系統的,它們的值是 build.prop 文件里面的內容。我們先看一下它的定義:
/** * Gives access to the system properties store. The system properties * store contains a list of string key-value pairs. * * {@hide} */public class SystemProperties
沒錯,這玩意是個 hide 的類,那就意味著正常情況下 SDK 里面是沒有的,AS 里面也是訪問不到的。不過我們還是可以通過一些手段去訪問到它,比如反射、將源碼的庫導出到 AS 使用、將 APP 放在源碼中編譯等等。
這里我們就不關注用什么手段去訪問它了,我們重點還是在利用它進行 Activity 之間的數據傳遞。
假設我們是在源碼中編譯,還是用一開始的兩個 Activity 來煮栗子,發送數據時可以這么操作:
SystemProperties.set("NAME", "Shawn.XiaFei");startActivity(...);
接收時就可以這么寫:
SystemProperties.get("NAME");//或者SystemProperties.get("NAME", "defValue");
是不是很方便呢,不過別激動,我們看下 set 的實現:
/** * 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);}
看注釋,沒錯,key 和 val 都限制了長度的!!!當然,32和92字符,在一般情況下也還是夠用的。但是下面就要說一般 APP 開發可能無法完成的事了。
前面說了,這玩意是 SDK 不可見的,而且它維護的是系統的屬性值,系統屬性值 APP 可以讀,但不能輕易修改。因此上面 set 的時候,如果權限不夠就會報如下錯誤:
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
這個錯誤在 Rom 開發中比較常見,解決辦法就是配置相應的 avc 權限,這一操作是一般 APP 開發者無法進行的。有興趣的可以自己去查資料,這里不做介紹。
5、通過 SettingsProvider 傳遞
愛折騰的人可能注意到了 Android 設備上一般都會有這么一個應用,它的作用是通過數據庫去維護一些系統配置信息。在 Rom 開發中,通常借助它設置首次開機的默認行為。
通過它傳遞數據的關鍵在 android.provider.Settings 類,這個類里面有 3 個常用的靜態內部類,分別是:Global、System、Secure,它們分別對應不同的權限等級。
煮栗子了:
發送時,這么寫就可以了:
/*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(...);
接收時,就這么寫:
String name = Settings.Global.getString(getContentResolver(), "NAME");
使用起來也是很簡單滴!不過,使用起來雖然簡單,但也并不是那么容易的。它也是要權限的!!!
如果權限不夠,運行的時候就會報如下錯誤:
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 的權限,我們試著在 Manifest 里面添加一下,結果 AS 標紅了,提示如下:
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.
意思就是說,這個權限只有系統 APP 才能獲得,三方 APP 沒戲。
6、通過數據庫傳遞
其實上面介紹的 SettingsProvider 方法,也是通過數據庫實現的,只不過它對數據庫的操作做了封裝,我們感覺不到而已。既然如此,我們也可以在自己 APP 中創建數據庫,然后通過數據庫來實現 Activity 之間的數據傳遞。栗子煮太多,吃不動,不煮了,有興趣的可以自己去查一下數據庫的知識。
7、通過文件傳遞
前面提到的 SharedPreferences 也是基于文件實現的,只不過 SharedPreferences 是固定成 xml 格式的文件。我們也可以通過自定義文件操作方式去實現數據的存取,進而實現 Activity 之間的數據傳遞。說了栗子不煮了,有興趣自己去查一下吧。
其實 Activity 之間數據傳遞的方法還是很多的,也各有優缺點,但最最最最最常用的還是第一種—— Intent,其他方法都是理論可行,實際使用起來都會有點雞肋,或者得不償失。
因此要想掌握好 Activity 之間數據傳遞的技巧,個人覺得只需要掌握 Intent 的用法,能熟練使用,靈活處理就 OK 了。至于其它方法,能說得出來原理就可以了。
關于AndroidActivity之間的數據傳遞方法有哪些就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。