背景
媒体监听,是一个很常见的功能,例如手机自带的图库,监听到了拍照图片的生成,就会自动刷新一下图库,用于及时向用户展示最新的数据,还如聊天页面中,监听到相册有新的图片,则有个小弹窗询问用户是否发送该拍摄的图片等等。
!!!代码放文末!!!!
本文将会展示,如何对生成的图片和视频进行监听
环境
win10
as 4.0+
jdk 1.8
思路
其实实现监听的代码,无非来来去去都是那几种,一是查询对比,二是调用系统方法,而本文将会用ContentResolver进行监听。
实现
首先,监听一个图片的生成,可以通过ContentReslver的registerContentObserver方法进行监听,最后会回调到onChange()方法中。
核心代码如下:
getBaseContext().getContentResolver().registerContentObserver(
MediaStore.Images.Media.INTERNAL_CONTENT_URI,
true, this);
getBaseContext().getContentResolver().registerContentObserver(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
true, this);
可以看到,url区分了一个internal和external,实际的意思,就是监听机身存储以及外部存储,从兼容性角度考虑,两个都需要写。
在看源码:
/**
* Register an observer class that gets callbacks when data identified by a
* given content URI changes.
* <p>
* Starting in {@link android.os.Build.VERSION_CODES#O}, all content
* notifications must be backed by a valid {@link ContentProvider}.
*
* @param uri The URI to watch for changes. This can be a specific row URI,
* or a base URI for a whole class of content.
* @param notifyForDescendants When false, the observer will be notified
* whenever a change occurs to the exact URI specified by
* <code>uri</code> or to one of the URI's ancestors in the path
* hierarchy. When true, the observer will also be notified
* whenever a change occurs to the URI's descendants in the path
* hierarchy.
* @param observer The object that receives callbacks when changes occur.
* @see #unregisterContentObserver
*/
public final void registerContentObserver(@NonNull Uri uri,
boolean notifyForDescendants,
@NonNull ContentObserver observer) {
Objects.requireNonNull(uri, "uri");
Objects.requireNonNull(observer, "observer");
registerContentObserver(
ContentProvider.getUriWithoutUserId(uri),
notifyForDescendants,
observer,
ContentProvider.getUserIdFromUri(uri, mContext.getUserId()));
}
对于boolean参数notifyForDescendants,可以理解为,如果设置为true,则跟该媒体类型有关变动的监听都会回调,可以理解为是一个广泛设置,关联度很高的设置。如果设置为false,则认为是仅仅指定了这种类,设置可以理解为是指定的某个目录,关联度低,某些机型会失效有可能是这个参数导致的。
同样地,视频的监听,核心代码如下:
getBaseContext().getContentResolver().registerContentObserver(
MediaStore.Video.Media.INTERNAL_CONTENT_URI, true, this);
getBaseContext().getContentResolver().registerContentObserver(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true, this);
同样地,区别就是在于Uri的设置不一样而已。
注意
(1)注意有注册,就应该要有释放,否则则会内存泄露。不写出内存泄露,是一个安卓开发者的基本素养,了解如何修改内存泄露,是一个合格安卓开发者的门槛。
(2)注册的时候,handler可以自定义传入的,这关乎到回调时候的线程种类。回调源码如下:
/** @hide */
public final void dispatchChange(boolean selfChange, @NonNull Collection<Uri> uris,
@NotifyFlags int flags, @UserIdInt int userId) {
if (mHandler == null) {
onChange(selfChange, uris, flags, userId);
} else {
mHandler.post(() -> {
onChange(selfChange, uris, flags, userId);
});
}
}
可以看到,回调的时候,如果我们传入的不是null的handler,则会调用。
(3)回调延时,属于系统源码层级,如果不是定制的room,是无法修改的。这里只能建议说,不使用自己传入的handler,规避某些系统的延时。因为某些系统下,自己传入的handler,会有一个delay的操作的。
代码地址
搜索CtxObserverManager类
that's all----------------------------------------------------------------------------