【好文推荐】Activity之间的数据传递方法汇总
在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 Stringimplements java.io.Serializable, Comparable<String>, CharSequence
public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable
public 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>() {@Overridepublic Student createFromParcel(Parcel in) {return new Student(in);}@Overridepublic Student[] newArray(int size) {return new Student[size];}};@Overridepublic int describeContents() {return 0;}@Overridepublic 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 的操作,对其它既有复杂类型和自定义类型是不支持的。它所支持的类型只有:
boolean
float
int
long
String
Set<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是一个 interface,我们并不能直接实例化,但我们可以使用它的直接或间接实现类,比如:HashSet、TreeSet、LinkedHashSet等等。
我们查看这几个的实现,不难发现,它们也都是实现了 Serializable 接口,支持序列化操作的:
public class HashSet<E>extends AbstractSet<E>implements Set<E>, Cloneable, java.io.Serializable
public class TreeSet<E> extends AbstractSet<E>implements NavigableSet<E>, Cloneable, java.io.Serializable
public 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 之间的数据传递。
栗子煮太多,吃不动,不煮了,有兴趣的可以自己去查一下数据库的知识。
PS:实话实说吧,个人用的不多,忘了怎么玩了……
7、通过文件传递
前面提到的 SharedPreferences 也是基于文件实现的,只不过 SharedPreferences 是固定成 xml 格式的文件。我们也可以通过自定义文件操作方式去实现数据的存取,进而实现 Activity 之间的数据传递。
说了栗子不煮了,有兴趣自己去查一下吧。
PS:原因同上一条……
总结
其实 Activity 之间数据传递的方法还是很多的,也各有优缺点,但最最最最最常用的还是第一种—— Intent,其他方法都是理论可行,实际使用起来都会有点鸡肋,或者得不偿失。
因此要想掌握好 Activity 之间数据传递的技巧,个人觉得只需要掌握 Intent 的用法,能熟练使用,灵活处理就 OK 了。至于其它方法,能说得出来原理就可以了。
最后
针对Android程序员,我这边给大家整理了一些资料,包括不限于高级UI、性能优化、移动架构师、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!
- Android前沿技术大纲
- 全套体系化高级架构视频
资料领取方式: 加群免费获取 Android架构设计大群(185873940)
最后送福利了,现在关注我并且加入群聊可以获取包含源码解析,自定义View,动画实现,架构分享等。
内容难度适中,篇幅精炼,每天只需花上十几分钟阅读即可。
大家可以跟我一起探讨,欢迎加群探讨,有flutter—底层开发—性能优化—移动架构—资深UI工程师 —NDK相关专业人员和视频教学资料,还有更多面试题等你来拿
点击GitHub领取
自定义View,动画实现,架构分享等。
内容难度适中,篇幅精炼,每天只需花上十几分钟阅读即可。
大家可以跟我一起探讨,欢迎加群探讨,有flutter—底层开发—性能优化—移动架构—资深UI工程师 —NDK相关专业人员和视频教学资料,还有更多面试题等你来拿**
点击GitHub领取
[外链图片转存中…(img-1ZVXg49m-1623503402786)]
【好文推荐】Activity之间的数据传递方法汇总相关推荐
- Activity之间的数据传递方法汇总系列教学
近日一好友去阿里面试,面试失败了,分享了一个他最不擅长的算法面试题.题目是这样的. 题目:给定一个二叉搜索树(BST),找到树中第 K 小的节点. 出题人:阿里巴巴出题专家:文景/阿里云 CDN 资深 ...
- Android中Activity之间的数据传递(Intent和Bundle)
当一个Activity启动另一个Activity时,常常会有一些数据传过去,对于Activity之间的数据交换更简单,因为两个Activity之间进行数据传递交换更简单,因为两个Activity之间本 ...
- Android--Activity的跳转及Activity之间的数据传递
Activity的跳转及Activity之间的数据传递(作业) 首先呢,看到这个作业第一感觉并不难,因为只是涉及到多activity之间的跳转以及简单的数据传递而已.然后,做的时候也没花费多长的时间, ...
- Android零基础入门第83节:Activity间数据传递方法汇总
2019独角兽企业重金招聘Python工程师标准>>> 在Activity间传递的数据一般比较简单,但是有时候实际开发中也会传一些比较复杂的数据,本节一起来学习更多Activity间 ...
- Activity的创建步骤+Activity之间的数据传递+案例(人品测试器)
Android的四大组件: 1.activity(多层界面运用) 2.广播接收者 3.服务 4.内容提供者 此外,我这里还会说道 5.多媒体编程(图形.声音.视频) 6.Fragment+动画 7.S ...
- 【无标题】Android在activity之间发送数据(putExtra方法)
Activity 之间发送数据(putExtra方法详解) 当应用创建 Intent 对象以在 startActivity(android.content.Intent) 中用于启动新的 Activi ...
- android activity之间传递对象,Android Activity之间的数据传递
一.通过startActivity来进行Activity的传值 在Android中,如果我们要通过一个Activity来启动另一个Activity,可以使用 startActivity(Intent ...
- Activity之间的数据传递—实现Parcelable接口
一.单向传递数据和接收 1.可以传递数据类型 传递基本数据类型.字符串和对应的数组 传递Bundle(类似HashMap) 传递对象数据 传递Integer集合.String集合和parcelable ...
- 常见的Android数据传递方法汇总
一.Intent与Bundle 1.Activty与Activity 1.1.传递简单数据 (1) 传单个值(以String类型为例) 发送数据 Intent intent = new Intent( ...
最新文章
- 40 万年薪招应届生?OPPO 狂揽芯片人才,应届生招聘行情究竟如何?
- git 上传代码到指定仓库_初次使用git上传代码到github远程仓库
- 大约xib连接错误bug正确
- OpenGL在frag着色器中模拟手电筒效果
- android 微信缩小通话界面_安卓如何做出微信那样的界面仿微信“我”的界面2/5...
- 通过VisualSVN的POST-COMMIT钩子自动部署代码
- CDateTimeUI类源码分析
- dj电商-模型类设计-1.x-模型类抽象基类
- 最流畅的手机,性能、跑分却弱爆了?2019年上半年手机数据报告出炉
- 95-290-050-源码-内存管理-堆外内存与堆内内存概述
- oracle字段id加1,oracle 字段ID自动增1
- 空降到一个公司做高管,怎么打开局面?
- python星星闪烁_python实现while循环打印星星的四种形状
- html sql 编辑器,【web】CodeMirror打造SQL在线编辑器
- Date DateFormat SimpleDateFormat Calendar Joda-Time
- hdu 5755 Gambler Bo【gauss】
- 数据结构——前序线索二叉树及其前序遍历
- Sharepoint visio Web Access
- 【IoT】产品管理:产品部管理管理规章与制度
- 机器视觉、halcon visionpro 的使用感受
热门文章
- linux根文件系统配置,Linux学习笔记__ Linux根文件系统详解
- miRNA的特征、功能及识别方法等详解
- 人脸识别系统的实现与封装
- 数据结构——栈,队列,及其结构特点应用。
- 华清远见嵌入式培训_第一周回顾与反思
- 1--if中的return的作用/条件判断中如何退出函数
- 水晶报表填充.Net Objects数据源
- 【算法】一文详解贪心法
- discuz 版块导航function_forumlist.php,Discuz! X2扩建左侧版块导航 社区层次一目了然...
- k-means聚类算法——c语言