引言


在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保存数据确实是复杂了些,但是能够更安全的进行数据转移对应用来说还是很好的一件事。推荐指数五颗星★★★★★!