乐者为王

Do one thing, and do it well.

在Application对象里存储数据的陷阱

看到Don't Store Data in the Application Object讲,在Application对象中存储共享数据会引起NullPointerException。顿时心里就咯噔了一下,用了四分之三秒,想起自己有个业余项目就干了这样的事。赶紧地测试看看。

打开应用,从MainActivity进入TxtViewerActivity界面(这里MainActivity主要是读取目录数据,然后保存在继承自Application的MainApp中,供TxtViewerActivity调用)。按手机Home键退出应用,这时你按菜单键可以看到该应用的缩图。然后在Eclipse中打开Window -> Show View -> Other -> Android -> Devices视图,双击窗口内的设备,然后点击设备下对应的进程,点击右上方红色的“Stop Process”图标。

重新按菜单键打开应用,然后……然后果然在LogCat中看到了有NullPointerException的大段红色警告文字。

为什么会Crash的?

根本原因在于:当应用被kill掉后,通过菜单键重新打开时,应用不会开始重新启动。Android系统会新建一个Application对象,然后启动上次离开时的TxtViewerActivity以造成这个应用从来没有被kill掉的假象。因为没有经过MainActivity的数据读取,所以在TxtViewerActivity中读取数据当然要抛出异常了。

有没有替代方法呢?

  • 直接将数据通过Intent传递给TxtViewerActivity?当然也会碰到上述同样的问题。
  • 使用SharedPreferences?可惜只能存储boolean、int、long、float和String五种数据类型,不支持List的存储;
  • 使用持久化存储?也不支持List的存储,而且太笨重了;
  • 使用Singleton对象保存共享数据,然后通过Intent传递呢?这个想法不错,还可以将读取assets资源等操作移到该对象中,做到单一职责原则,改善设计。不过这样一来Singleton对象会对MainActivity的context有长期引用,容易造成内存泄露。如果不把读取操作放进去……那根本就不可能,你能让一个追求完美的程序猿忍受糟糕的代码设计吗!

幸好早就有人总结出来经验了:使用Application的context代替Activity的context。

创建Singleton对象,在Application对该对象保持引用,把原来存储在Application中共享的数据全部移到Singleton对象中,将Activity中读取assets资源等操作也放入该对象,Activity中原来对Application对象的访问改成通过Application对象对Singleton对象的访问。

这样修改后,不光解决了应用的崩溃,还预防了内存泄漏,更改进代码的设计。

代码下载:https://github.com/dohkoos/txtReader

Comments