0
点赞
收藏
分享

微信扫一扫

Activity之间的数据传递方法汇总,2021年字节跳动+京东+美团面试总结

穆熙沐 2022-01-30 阅读 74

intent.getParcelableExtra(String name);

这两种实现序列化的方法的使用原则:

1)在使用内存的时候,Parcelable 比 Serializable 性能高,所以推荐使用 Parcelable。

2)Serializable 在序列化的时候会产生大量的临时变量,从而引起频繁的 GC。

3)Parcelable 不能使用在要将数据存储在磁盘上的情况,因为 Parcelable 不能很好的保证数据的持续性在外界有变化的情况下。尽管 Serializable 效率低点,但此时还是建议使用 Serializable 。

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

仍旧拿前面的两个 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 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 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 执行完。

因 And
roid framework 已经做好所有的事情,所以当我们不需要关注提交操作的返回值时,可以将 commit 无条件替换 apply 使用,而且 AS 也会建议将 commit 替换成 apply。

**3、**SharedPreferences 支持的数据类型都必须是支持序列化操作的,上面提到的 Set是一个 interface,我们并不能直接实例化,但我们可以使用它的直接或间接实现类,比如:HashSet、TreeSet、LinkedHashSet等等。

我们查看这几个的实现,不难发现,它们也都是实现了 Serializable 接口,支持序列化操作的:

public class HashSet
extends AbstractSet
implements Set, Cloneable, java.io.Serializable
public class TreeSet extends AbstractSet
implements NavigableSet, Cloneable, java.io.Serializable
public class LinkedHashSet
extends HashSet
implements Set, 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_SETTINGS
at 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 . h a n d l e M e s s a g e ( A c t i v i t y T h r e a d . j a v a : 1613 ) a t a n d r o i d . o s . H a n d l e r . d i s p a t c h M e s s a g e ( H a n d l e r . j a v a : 106 ) a t a n d r o i d . o s . L o o p e r . l o o p ( L o o p e r . j a v a : 164 ) a t a n d r o i d . a p p . A c t i v i t y T h r e a d . m a i n ( A c t i v i t y T h r e a d . j a v a : 6523 ) a t j a v a . l a n g . r e f l e c t . M e t h o d . i n v o k e ( N a t i v e M e t h o d ) a t c o m . a n d r o i d . i n t e r n a l . o s . R u n t i m e I n i t 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 H.handleMessage(ActivityThread.java:1613)atandroid.os.Handler.dispatchMessage(Handler.java:106)atandroid.os.Looper.loop(Looper.java:164)atandroid.app.ActivityThread.main(ActivityThread.java:6523)atjava.lang.reflect.Method.invoke(NativeMethod)atcom.android.internal.os.RuntimeInitMethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:857)

意思很明了,得给它 WRITE_SECURE_SETTINGS 的权限,我们试着在 Manifest 里面添加一下,结果 AS 标红了,提示如下:

意思就是说,这个权限只有系统 APP 才能获得,三方 APP 没戏。

6、通过数据库传递

其实上面介绍的 SettingsProvider 方法,也是通过数据库实现的,只不过它对数据库的操作做了封装,我们感觉不到而已。既然如此,我们也可以在自己 APP 中创建数据库,然后通过数据库来实现 Activity 之间的数据传递。

栗子煮太多,吃不动,不煮了,有兴趣的可以自己去查一下数据库的知识。

7、通过文件传递

前面提到的 SharedPreferences 也是基于文件实现的,只不过 SharedPreferences 是固定成 xml 格式的文件。我们也可以通过自定义文件操作方式去实现数据的存取,进而实现 Activity 之间的数据传递。

说了栗子不煮了,有兴趣自己去查一下吧。

总结

其实 Activity 之间数据传递的方法还是很多的,也各有优缺点,但最最最最最常用的还是第一种—— Intent,其他方法都是理论可行,实际使用起来都会有点鸡肋,或者得不偿失。

因此要想掌握好 Activity 之间数据传递的技巧,个人觉得只需要掌握 Intent 的用法,能熟练使用,灵活处理就 OK 了。至于其它方法,能说得出来原理就可以了。

最后

针对Android程序员,我这边给大家整理了一些资料,包括不限于高级UI、性能优化、移动架构师、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

  • Android前沿技术大纲

  • 全套体系化高级架构视频

资料领取方式: 加群免费获取 Android架构设计大群(185873940)

到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

  • Android前沿技术大纲

[外链图片转存中…(img-829k7KuB-1643431366882)]

  • 全套体系化高级架构视频

[外链图片转存中…(img-D2ARnQ2j-1643431366882)]

资料领取方式: 加群免费获取 Android架构设计大群(185873940)
举报

相关推荐

0 条评论