cursor java_使用RxJava对Cursor的操作Sample

RxCursorSample是我在一个用Rx特性实现的图片多选/单选选择器

特性:

1.使用RxJava操作ContentProvider的Cursor,并且在图片详情页面,使用RxJava操作符实现了一个RecyclerView的简单动画,提升了用户体验

2.使用RxBus实现组件通信

3.适配了Android 6.0的权限请求

其他几个特性不分析了,主要分析下项目中的Rx:(源码在文章结尾)

** RxBus(这里是我的一篇关于RxBus的简书)负责了Fragment和Activity通信**

Activity接收RxBus的事件,然后通过FragmentManager切换Fragment。

下面是在ImagePickerActivity的主要代码:

private void initRxBus() {

// 接收 切换相册事件

rxSubscriptions.add(RxBus.getDefault().toObserverable(AddDetailEvent.class)

.map(addDetailEvent -> addDetailEvent.getBucketName())

.subscribe(bucketName -> {

// add DetailExploreFragment

addFragment(DetailExploreFragment.newInstance(bucketName, isMultiplePick));

}, throwable -> {

throwable.printStackTrace();

Toast.makeText(this, R.string.yo_switch_bucket_exception,Toast.LENGTH_SHORT).show();

}));

// 接收 切换预览事件

rxSubscriptions.add(RxBus.getDefault().toObserverable(AddPreviewEvent.class)

.map(addPreviewEvent -> addPreviewEvent.getImgs())

.subscribe(imgs -> {

toolbar.setTitle(R.string.yo_preview);

// add PreviewFragment

addFragment(PreviewFragment.newInstance(imgs, isMultiplePick));

}, throwable -> {

throwable.printStackTrace();

Toast.makeText(this, R.string.yo_switch_preview_exception, Toast.LENGTH_SHORT).show();

}));

}

上面的代码中,比如点击了“预览”,由DetailExploreFragment跳转至PreviewFragment,通过RxBus将事件传递到Activity,Activity再切换PreviewFragment

下面是DetailExploreFragment 中的代码,点击“预览”,发送事件

btnPreview.setOnClickListener((v) -> {

// addFragment

RxBus.getDefault().post(new AddPreviewEvent(imgs));

});

** RxJava操作ContentProvider的Cursor**

创建Observable的代码:

private Observable cursorObservable() {

return Observable.create(new Observable.OnSubscribe() {

@Override

public void call(Subscriber super Cursor> subscriber) {

if (!subscriber.isUnsubscribed()) {

try{

Cursor cursor = getCursor();

// 判断!subscriber.isUnsubscribed() 是为了在取消订阅时,保证cursor可以及时关闭

while (cursor.moveToNext() && !subscriber.isUnsubscribed()) {

subscriber.onNext(cursor);

}

subscriber.onCompleted();

}catch(Exeception e){

subscriber.onError(e);

}finally {

assert cursor != null;

cursor.close();

}

}

}

});

}

private Cursor getCursor() {

String[] mediaColumns = new String[]{

MediaStore.Images.Media.BUCKET_DISPLAY_NAME,

MediaStore.Images.Media.DATA,

"COUNT(*) AS " + COLUMN_NAME_COUNT

};

// SELECT _data, COUNT(*) AS v_count FROM video WHERE ( GROUP BY bucket_display_name)

String selection = " 1=1 ) GROUP BY (" + MediaStore.Images.Media.BUCKET_DISPLAY_NAME;

return _activity.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mediaColumns, selection, null, null);

}

Cursor是个比较特殊的数据库操作类,在该例中,在Cursor的游标移动到结尾之前,会一直发射Observable的数据源。

代码中while里有判断!subscriber.isUnsubscribed(),是为了在取消订阅时而cursor还没有执行完的情况下,保证cursor可以及时关闭。

下面在IO线程中将Observable转换成List,最终在主线程中的RecyclerView中绑定数据显示。

private void initData() {

subscription = cursorObservable()

.subscribeOn(Schedulers.io())

.filter(cursor -> {

String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));

return !path.endsWith(".gif");

})

.map(cursor -> {

String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));

String bucket_name = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_DISPLAY_NAME));

int count = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_COUNT));

return new BucketEntity(bucket_name, count, path);

})

.toList()

// 如果不用toList()转成List 一定要调用onBackpressureBuffer()方法,防止数据源发射过快,导致异常MissingBackpressureException

// .onBackpressureBuffer()

.observeOn(AndroidSchedulers.mainThread())

.subscribe(

bucketEntity -> {

adapter.setDatas(bucketEntity);

}, throwable -> {

throwable.printStackTrace();

Toast.makeText(_activity, R.string.yo_find_exception, Toast.LENGTH_SHORT).show();

}

);

}

filter操作符过滤掉的gif图片,map操作符将Observable对象转换成Obser,这里有2种绑定数据到RecyclerView的方法:

1. 数据源集合绑定:

使用toList操作符,它将一连串的Observable类型数据源集合转换成一个Observable>返回,最后一次性绑定数据到RecyclerView上

2. 单数据源依次绑定:

即通过Adapter.addData(bean),一个一个数据逐个绑定到RecyclerView上,但是如果直接这样操作,则会报预测MissingBackpressureException的异常,该异常是由于数据源发射速度过快导致的。

可以使用onBackpressureBuffer操作符解决,它可以缓冲发射速度过快的数据源,直到所有数据源全部发射出去。

这个例子中,使用的是第一种方法

下面这段代码使用了第二种,同时加入了一个操作符,视觉上形成一个逐条加入的动画

private void initData() {

subscription = cursorObservable()

.subscribeOn(Schedulers.io())

// 延迟60ms发射数据 形成动画, delay默认在computation线程 要主动切换到当前的线程

.delay(60, TimeUnit.MILLISECONDS, Schedulers.immediate())

.filter(cursor -> {

String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));

return !path.endsWith(".gif");

})

.map(cursor -> {

String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));

return new File(path);

})

// onBackpressureBuffer()方法,防止数据源发射过快引起的MissingBackpressureException

.onBackpressureBuffer()

.observeOn(AndroidSchedulers.mainThread())

.subscribe(

file -> {

adapter.addData(file);

}, throwable -> {

throwable.printStackTrace();

Toast.makeText(_activity, R.string.yo_find_exception, Toast.LENGTH_SHORT).show();

}

);

}

delay操作符可以延迟发射数据源。

因为该操作符默认在computation线程中运行,我们需要延迟的时间在数据源处理的IO线程上,所以主动指定在Schedules.immediate()上,即当前的IO线程上。(关于RxJava的线程高效使用,可以参考小鄧子的这篇译文)

这样的话,每隔60ms观察者就会收到一个经过加工的File数据源,然后将其绑定到RecyclerView上。

在视觉上,每个RecyclerView的Item都会在上个Item显示后的60ms后显示,仅仅通过一个操作符完成了一个Item显示动画 :)

更多详情可以查看源码,这里是完整的代码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值