在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<Integer> value)
putStringArrayListExtra(String name, ArrayList<String> value)
putCharSequenceArrayListExtra(String name, ArrayList<CharSequence> 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, ArrayList<? extends Parcelable> value)
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对象,就先要将其序列化,如下示例代码:
[代码]java代码:
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类进行改造,代码如下:
[代码]java代码:
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<person> CREATOR = new Creator<person>() { // 再通过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; } } </person></person> |
然后即可调用前面的put和get方法来传递复杂对象数据了,当然也可以是对象的数组或列表型数据。
在使用中需要注意的是,Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的持续性在外界有变化的情况下。尽管Serializable效率低点,但此时还是建议使用Serializable。
四、全局Application
如果需要将一个对象在多个Activity之间传递,或者要连续传递好几层,这种情况下如果使用以上方法就需要重复多次,使用起来就特别别扭,这种情况就可以考虑使用全局Application。
Android系统在每个程序运行的时候都会创建一个Application对象,而且只会创建一个,所以Application 是单例(singleton)模式的一个类,而且Application对象的生命周期是整个程序中最长的,他的生命周期等于这个程序的生命周期。如果想存储一些值,使用 Application就需要自定义类实现Application类,然后在AndroidManifest.xml中使用我们自定义的Application 而非系统默认的。
这里简单使用一个示例来学习,这里简化为全局保存一个状态值,可以方便在各Activity中进行传递。首先自定义Application类,代码如下:
[代码]java代码:
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属性,代码如下:
[代码]xml代码:
<? xml version = "1.0" encoding = "utf-8" ?> < manifest xmlns:android = "http://schemas.android.com/apk/res/android" package = "com.jinyu.cqkxzsxy.android.activity.equipmentupdate" > < application android:name = ".MyApplication" android:allowBackup = "true" android:icon = "@mipmap/ic_launcher" android:label = "@string/app_name" android:roundIcon = "@mipmap/ic_launcher_round" android:supportsRtl = "true" android:theme = "@style/AppTheme" > < activity android:name = ".MainActivity" > < intent-filter > < action android:name = "android.intent.action.MAIN" /> < category android:name = "android.intent.category.LAUNCHER" /> </ intent-filter > </ activity > </ application > </ manifest > |
最后在需要使用定义的全局变量的地方即可调用,核心代码如下:
[代码]java代码:
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就是基于单例的,单例模式的特点就是可以保证系统中一个类有且只有一个实例。
这里做一个简单的示例,如定义一个数据持有者类,代码如下:
[代码]java代码:
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中完成单独一个数据结构,和单例差不多。
这里将上面的单例模式类简单修改,代码如下:
[代码]java代码:
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 将数据包装起来。
除了以上介绍的几种方式,还可以使用持久化数据等方法,这里先不做过多介绍,在后续的学习中会陆续接触到。
今天就先到这里,如果有问题欢迎留言一起探讨,也欢迎加入Android零基础入门技术讨论微信群,共同成长!
共同学习,写下你的评论
评论加载中...
作者其他优质文章