组件间的数据传输
在Android里,每个组件都相对独立,它们是不能互相访问对方的数据.因此得通过一些特殊的方式在组件间传递和共享数据.
以前我仅会使用Intent传输,文件和数据库共享数据.在深入了解更多组件数据传输方法后,深刻体会到在某些环境下,那些方法显得如此笨拙和低效.
这里主要介绍四种传递和共享数据的方法:
- 通过Intent对象进行传输
- 通过文件系统进行数据共享
- 通过Application环境对象进行数据共享
- 通过组件(Content Provider & Service)进行数据共享
Intent对象进行传输
Intent对象是组件间通信协议的拟定者和消息数据的传递者,它是组件间交互数据的重要载体.Google官方对Intent的文档.
- 对于数据量小的数据,则直接通过Intent对象的
Extra
域,以键值对形式组织,并一同打包传到目标组件. - 对于数据量大的数据,常用Intent的
Data
域进行存储,以URI形式保存.目标组件通过URI读取具体数据内容.
Extra域
//对于Extra域传值
Intent intent = new Intent();
intent.setClass(AActivity.class, BActivity.class);
//可以直接在putExtra函数里面设置key-value.
intent.putExtra("name", "luckyshq");
intent.putExtra("age", 21);
//或者用putExtras放入Bundle对象.
Bundle bundle = new Bundle();
bundle.putString("name", "luckyshq");
bundle.putInt("age", 21);
/*
个人建议在数据量很小的时候而且`不是多组件传递`时用第一种写法,其他情况都用第二种(即用Bundle对象).
虽说第二种写法在某些方面相对第一种要写更多代码,而且创建了一个新对象.但使用Bundle对象来组织数据更加清晰.
如果不想创建过多的对象的话,可以自己封装一个单例模式的Bundle类,每次用时都直接getInstance就行(不过还是得考虑使用环境,数据实时性等).
同时如果出现了数据要从`A -> B -> C`多个组件传递时,这种写法的优势就出现了.
如果用第一种的话,在每次都得从intent中把数据取出来,再put进去.但使用Bundle的话,就直接使用bundle对象传过去就行了.
*/
Data域
//对于大量的数据,直接putExtra进行序列化和目标组件反序列化的开销就太大了.
//因此直接将地址放入Data域,让目标组件自己去获取吧.
Intent intent = new Intent();
intent.setAction(android.content.Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "image/*");
//关于Intent组件里的各种项及作用将在后续文章中进行介绍.
使用Intent的优缺点
优点
开发便捷
Intent机制提供了完善的接口,直接使用即可,不需要多余的序列化和反序列化的处理.跨应用,跨进程
Intent机制是通过序列化和反序列化处理数据的,因此里面附加的数据可以在进程间和应用间传递.
缺点
传输开销大
因为Intent机制传递时会通过组件管理服务,所以会有两次序列化和反序列化的过程,使得传输效率变低.不适合多组件间共享数据
Intent传递是线性的,所以如果要实现多组件间共享,开发复杂度和开销都会大幅加大.
使用文件进行数据共享
有时多个组件需要对同一份数据进行操作,这样如果使用Intent的话就不能很方便的满足需求. 这时我们就能通过文件来对数据进行共享.(包括SharedPreferences等)
//从文件里读取数据
FileInputStream inputFile = openFileInput("a.txt");
String[] content = readFile(inputFile); //readFile是自定义函数
//写数据到文件
FileOutputStream outputFile = openFileOutput("a.txt");
writeFile(content, outputFile);
关于数据一致性的处理
使用文件来传递,共享数据的时候.确保数据的一致性是十分重要的. 如果多个组件同时读写同一个文件的话就会出现各种脏数据等问题.
这里我们可以利用Android处于前台状态的组件对象只有一个这个特性.
同一个应用中,所有对文件的读取操作都在onResume()
里完成,而对所有文件的写入操作都在onPause()
里完成.
这样就能保证在使用一个应用时,数据仅被当前应用的前台组件对象所操作.
- 当将文件写在SD卡中,给多个不同应用来公用时,就得考虑到多线程读写的问题.那样就得加入对应的加锁等操作来避免多应用读写造成的问题.
使用文件共享的优缺点
优点
可以实现多组件对象间数据共享
.
缺点
读写操作开销大
总所周知,外存储设别读写速度与内存相差好几个数量级,若数据较大时会明显阻塞线程,影响用户体验.开发成本高
要自行处理序列化和反序列化,加大了开发成本.有一定局限性
有些应用不存在外存,同时放在外存的数据基本上没有安全性保护.
使用应用环境的全局数据共享
由于使用外部存储的开销较大,使用全局共享的内存区域就能很大程度上的减少该类开销. 这里就会使用到应用环境组件Application,它在应用进程被构造时创建出来,直至这个进程被最终销毁.它是作为全局共享内存区域的最佳选择.
//在实现的时候需要自行派生Application类来进行实现.
public class MyApplication extends Application {
private int attr;
public void onCreate(){
//在onCreate里进行初始化.
attr = getDefaultAttr();
}
//读数据接口
public int getAttr(){
return attr;
}
//写入接口
public void updateAttr(int attr){
this.attr = attr;
saveAttrAsDefault(this.attr);
}
}
- 如果数据修改不是很频繁,就可以直接在写入接口里修改后立即写入.
- 如果修改比较频繁,通常会将写入数据的职责交给修改数据的组件对象,让它们选择合适的时机来写入,批量进行修改.
使用Application全局数据共享的优缺点
优点
效率高
可以有效降低数据读取和写入次数,数据只会在进程构造之初读取一次.可实现不需要持久化存储的数据共享
有些数据是不需要写入文件持久化保存的,所以Application在这点上优于文件数据共享.
缺点
不支持跨进程,跨应用共享
Application是对应特定的应用进程的,所以无法做到跨进程共享.同时若某个应用有多个进程的话,就不能用Application来共享,会出现数据不一致的情况.内存占用较大
由于Application的生命周期跨越了整个进程周期,其中的数据会一致占据者进程内存空间.若数据规模较大,则需优化读写,来降低内存开销.
使用组件共享数据
Application不能在不同进程组件对象间传递数据.而Android组件最擅长的就是跨进程和跨引用. 因此可以使用组件来进行跨进程和应用的数据共享.
使用数据源组件共享数据
数据源组件的设计目标,是为各个应用的组件对象提供数据服务. 相比与一般的文件封装,它有以下3个好处:
更安全地共享数据
通过文件分享数据时,需要将数据写到扩展的外存中,可靠性低.而使用数据源组件,可以将私有数据进行封装再传递,提高了可靠性.开发更为便捷
Android为数据源组件提供了很多的支持,使得对基于数据源组件的数据进行读写和展示都十分简单.更适合处理关系型数据
Android为数据源组件设计的基于REST+SQL的概念,使得数据源组件更适合关系型数据的增删改查.
数据源组件适合在不同组件对象间共享关系型和结构化的数据,不过由于组件连接和传输方式比较复杂.因此不适合大数据文件处理.
使用服务组件共享数据
服务组件可以与调用组件建立双向通讯连接,利用进程间通信机制在组件对象间传输数据.它重视的是对数据的操作和处理,它提供的机制保证它可以安全有效地处理操作请求.
参考文献
[1] 《Android开发精要》