引言
在Android应用开发:Fragment的非中断保存setRetaineInstance一文中已经介绍过了如何让Fragment不随着Activity销毁从而保存数据的方法。在移动应用程序的架构设计中,界面与数据即不可分割又不可混淆。在绝大部分的开发经历中,我们都是使用Fragment来进行界面编程,即使保存数据基本上也只是界面相关控件的数据,很少做其他的数据保存,毕竟这样与开发原则相背,而今天这一篇博客就要来介绍一下Fragment的另类用法,只是用来保存数据而没有任何界面元素。
实现背景
对于Fragment的数据保存方法,不难想到还是与setRetainInstance有关系的。这样一来所处的背景也是在屏幕旋转或其他配置改变时需要用到。无论在开发中我们的界面是用Activity还是Fragment生成的,在屏幕发生旋转时,都会在生命周期onSaveInstanceState中做控件状态和必要数据的缓存工作。通常情况下,会用到Bundle来存储数据。如Bundle的官方介绍所说,Bundle是一个用来存储String及其他序列化数据类型的map。同样Android中也存在着这样的一个异常:http://developer.android.com/intl/zh-cn/reference/android/os/TransactionTooLargeException.html
这个异常从字面上看不难理解,是传输数据过大异常。在描述中可知,现行Android系统中对于应用程序的传输数据大小限制在1Mb以内。所以如果在屏幕旋转过程中使用Bundle缓存大数据并不是十分安全的。这样的大数据在Android中很经典的代表之一就是Bitmap,即使Bitmap已经是序列化数据,能够方便的使用Bundle作为缓存媒介,但是笔者还是强烈不建议这样做。下边,就提供一个简单的解决途径。
实现过程
首先,创建一个用来保存数据的Fragment:
public class BitmapDataFragment extends Fragment { public static final String TAG = "bitmapsaver"; private Bitmap bitmap; private BitmapDataFragment(Bitmap bitmap) { this.bitmap = bitmap; } public static BitmapDataFragment newInstance(Bitmap bitmap) { return new BitmapDataFragment(bitmap); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); } public Bitmap getData() { return bitmap; } }
这个Fragment没有任何界面,在onCreate生命周期中使用setRetainInstance(true)确保不会随载体销毁,从而确保数据的安全性。
创建完成后,实践一下使用过程,假设其使用者是Activity:
@Override protected void onSaveInstanceState(Bundle outState) { if (mBitmap != null) { getSupportFragmentManager().beginTransaction() .add(BitmapDataFragment.newInstance(mBitmap), BitmapDataFragment.TAG) .commit(); outState.putBoolean(SENSE_IMAGE_KEY, true); } else { outState.putBoolean(SENSE_IMAGE_KEY, false); } super.onSaveInstanceState(outState); }
在设备发生旋转时,检测当前界面中显示的某个Bitmap,如果确实有数据,则new出一个我们刚刚创建的Fragment,将Bitmap数据放置进去,然后将这个Fragment添加到FragmentManager中并指定tag,这样我们在恢复状态后就可以方便的找到它。
在恢复时候,Activity的生命周期走到了onCreate()中,在这里我们可以通过检测Bundle参数来确定是否有Bitmap数据待取:
if (savedInstanceState.getBoolean(SENSE_IMAGE_KEY)) { BitmapDataFragment fragment = (BitmapDataFragment) getSupportFragmentManager() .findFragmentByTag(BitmapDataFragment.TAG); bitmap = fragment.getData(); getSupportFragmentManager().beginTransaction().remove(fragment).commit(); }
PS:在取出我们所需的Bitmap数据后不要忘记把作为数据容器的这个Fragment从FragmentManager中移除掉,释放其占用的系统内存。
总结
很简单的Fragment非主流用法,相比直接使用Bundle保存数据确实是复杂了些,但是能够更安全的进行数据转移对应用来说还是很好的一件事。推荐指数五颗星★★★★★!