组件间的数据传输

在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对象传过去就行了.
*/

putExtra()官方文档

putExtras()官方文档

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开发精要》

Loading Disqus comments...
Table of Contents