在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对象,就先要将其序列化,如下示例代码:
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<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; } }
然后即可调用前面的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属性,代码如下:
<?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>
最后在需要使用定义的全局变量的地方即可调用,核心代码如下:
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 将数据包装起来。
除了以上介绍的几种方式,还可以使用持久化数据等方法,这里先不做过多介绍,在后续的学习中会陆续接触到。
今天就先到这里,如果有问题欢迎留言一起探讨,也欢迎加入Android零基础入门技术讨论微信群,共同成长!
如果该系列分享对你有帮助,就动动手指关注、点赞、留言吧,你的互动就是对我最大的鼓励!
此文章版权为微信公众号分享达人秀(ShareExpert)——鑫鱻所有,若需转载请联系作者授权,特此声明!
往期总结回顾:
Android零基础入门第1节:Android的前世今生
Android零基础入门第2节:Android 系统架构和应用组件那些事
Android零基础入门第3节:带你一起来聊一聊Android开发环境
Android零基础入门第4节:正确安装和配置JDK, 高富帅养成第一招
Android零基础入门第5节:善用ADT Bundle, 轻松邂逅女神
Android零基础入门第6节:配置优化SDK Manager, 正式约会女神
Android零基础入门第7节:搞定Android模拟器,开启甜蜜之旅
Android零基础入门第8节:HelloWorld,我的第一趟旅程出发点
Android零基础入门第9节:Android应用实战,不懂代码也可以开发
Android零基础入门第10节:开发IDE大升级,终于迎来了Android Studio
Android零基础入门第11节:简单几步带你飞,运行Android Studio工程
Android零基础入门第12节:熟悉Android Studio界面,开始装逼卖萌
Android零基础入门第13节:Android Studio个性化配置,打造开发利器
Android零基础入门第14节:使用高速Genymotion,跨入火箭时代
Android零基础入门第15节:掌握Android Studio项目结构,扬帆起航
Android零基础入门第16节:Android用户界面开发概述
Android零基础入门第17节:文本框TextView
Android零基础入门第18节:输入框EditText
Android零基础入门第19节:按钮Button
Android零基础入门第20节:复选框CheckBox和单选按钮RadioButton
Android零基础入门第21节:开关组件ToggleButton和Switch
Android零基础入门第22节:图像视图ImageView
Android零基础入门第23节:图像按钮ImageButton和缩放按钮ZoomButton
Android零基础入门第24节:自定义View简单使用,打造属于你的控件
Android零基础入门第25节:简单且最常用的LinearLayout线性布局
Android零基础入门第26节:两种对齐方式,layout_gravity和gravity大不同
Android零基础入门第27节:正确使用padding和margin
Android零基础入门第28节:轻松掌握RelativeLayout相对布局
Android零基础入门第29节:善用TableLayout表格布局
Android零基础入门第30节:两分钟掌握FrameLayout帧布局
Android零基础入门第31节:少用的AbsoluteLayout绝对布局
Android零基础入门第32节:新推出的GridLayout网格布局
Android零基础入门第33节:Android事件处理概述
Android零基础入门第34节:Android中基于监听的事件处理
Android零基础入门第35节:Android中基于回调的事件处理
Android零基础入门第36节:Android系统事件的处理
Android零基础入门第37节:初识ListView
Android零基础入门第38节:初识Adapter
Android零基础入门第39节:ListActivity和自定义列表项
Android零基础入门第40节:自定义ArrayAdapter
Android零基础入门第41节:使用SimpleAdapter
Android零基础入门第42节:自定义BaseAdapter
Android零基础入门第43节:ListView优化和列表首尾使用
Android零基础入门第44节:ListView数据动态更新
Android零基础入门第45节:网格视图GridView
Android零基础入门第46节:列表选项框Spinner
Android零基础入门第47节:自动完成文本框AutoCompleteTextView
Android零基础入门第48节:可折叠列表ExpandableListView
Android零基础入门第49节:AdapterViewFlipper图片轮播
Android零基础入门第50节:StackView卡片堆叠
Android零基础入门第51节:进度条ProgressBar
Android零基础入门第52节:自定义ProgressBar炫酷进度条
Android零基础入门第53节:拖动条SeekBar和星级评分条RatingBar
Android零基础入门第54节:视图切换组件ViewSwitcher
Android零基础入门第55节:ImageSwitcher和TextSwitcher
Android零基础入门第56节:翻转视图ViewFlipper
Android零基础入门第57节:DatePicker和TimePicker选择器
Android零基础入门第58节:数值选择器NumberPicker
Android零基础入门第59节:常用三大Clock时钟组件
Android零基础入门第60节:日历视图CalendarView和定时器Chronometer
Android零基础入门第61节:滚动视图ScrollView
Android零基础入门第62节:搜索框组件SearchView
Android零基础入门第63节:值得借鉴学习的选项卡TabHost
Android零基础入门第64节:揭开RecyclerView庐山真面目
Android零基础入门第65节:RecyclerView分割线开发技巧
Android零基础入门第66节:RecyclerView点击事件处理
Android零基础入门第67节:RecyclerView数据动态更新
Android零基础入门第68节:RecyclerView添加首尾视图
Android零基础入门第69节:ViewPager快速实现引导页
Android零基础入门第70节:ViewPager打造TabHost效果
Android零基础入门第71节:CardView简单实现卡片式布局
Android零基础入门第72节:SwipeRefreshLayout下拉刷新
Android零基础入门第73节:Activity创建和配置
Android零基础入门第74节:Activity启动和关闭
Android零基础入门第75节:Activity状态和生命周期
Android零基础入门第76节:Activity数据保存和横竖屏切换
Android零基础入门第77节:Activity任务栈和启动模式
Android零基础入门第78节:四大组件的纽带——Intent
Android零基础入门第79节:Intent 属性详解(上)
Android零基础入门第80节:Intent 属性详解(下)
Android零基础入门第81节:Activity数据传递
Android零基础入门第82节:Activity数据回传
来源:oschina
链接:https://my.oschina.net/u/3598984/blog/1555970