截屏事件监听实现

来源:互联网 发布:汽车行业的大数据应用 编辑:程序博客网 时间:2024/05/02 00:46

现在很多app都有监听截屏的功能,可以提示用户分享或者发送截屏图片,进一步提升app的用户体验。例如,微信在截屏后,点击输入框+号,会提示发送刚才的截屏图片,非常方便实用。其实,微信不光对截屏的图片进行了监听,你的手机sdcard里的大部分目录下新增图片时,都会被监听到。比如,你可以直接复制一张相册里的图片,微信也会提示。之前以为微信只是监听了系统截屏时发送的广播,然而查了文档并没有发现任何截屏类的广播。上述微信的例子也很好的解释了,其实对于截屏以及其他新增图片的监听是通过对sdcard文件内容变动的监听。
实现截屏监听主要有两种方式,使用FileObserver和ContentObserver,顾名思义,一个是基于文件或者目录的监听,一个是基于内容的监听。下面利用这两种方式简单实现了对于图片文件变动的监听。

FileObserver

FileObserver主要用于监听指定路径的目录或者文件的变动情况,包括监听目录下的子目录和文件以及目录本身,同时也可以指定监听文件变动的形式,例如创建、删除、移动、修改等,或者全部事件都监听。例如这里我们需要监听sdcard截屏目录,并且指定监听事件为FileObserver.CREATE,这样当screenshots目录下新增截屏图片时,onEvent事件就会触发,并且回调了截屏文件的相对路径,拿到路径我们就可以做想做的事情了。主要测试代码如下:

private void addFileObserver() {    String screenPath = getScreenshotPath();    fileObserver = new ScreenshotFileObserver(screenPath, FileObserver.CREATE);    fileObserver.startWatching(); // 开始监听}class ScreenshotFileObserver extends FileObserver {    public ScreenshotFileObserver(String path) {        super(path);    }    public ScreenshotFileObserver(String path, int mask) {        super(path, mask);    }    @Override    public void onEvent(int event, final String path) {        if (event == FileObserver.CREATE) {            Log.d("screenshot", path);            File file = new File(getScreenshotPath() + path);            // todo sth. with the screenshot picture            ...        }    }}

需要注意的是使用FileObserver可能需要声明WRITE_EXTERNAL_STORAGE和MOUNT_UNMOUNT_FILESYSTEMS权限,虽然官方文档并没有提到这点,但是不加权限可能导致FileObserver不能正常工作,但仅仅是监听不到文件变动而已,程序不会crash。
FileObserver用法非常简单,但是一个FileObserver只能监听一个目录。如果需要监听多个目录会比较麻烦,可能你会觉得直接监听sdcard根目录可以搞定,但是那样sdcard下任何一个文件的变动都会被监听,效率和性能可能是个问题。而且有时候我们其实并不清楚需要监听的目录的具体路径,比如不同厂商的手机保存截屏的目录可能不尽相同。
另外,FileObserver在android 6.0以上存在bug,监听不到目录的变动,只能监听指定的具体文件,具体讨论可见:https://code.google.com/p/android/issues/detail?id=189231。

ContentObserver

ContentObserver主要用于监听ContentProvider内容的变动,例如图片数据、音频数据、视频数据等。当有内容变动时,系统会相应修改其在ContentProvider中的记录。不同于FileObserver监听的是具体的文件或者目录路径,ContentObserver监听的是一个指定的Uri,通常可以理解为需要监听的数据类型。例如,这里我们需要监听截屏,可以向ContentResolver注册一个ContentObserver,并指定uri为系统媒体图片,那么当图片数据发生变化时,onChange方法就会被回调。根据回调返回的Uri,我们可以判断变动是否来自截屏图片,这样就可以实现对截屏事件的监听。

private void addContentObserver() {    contentObserver = new ScreenshotContentObserver(new Handler(getMainLooper()));    Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;    getContentResolver().registerContentObserver(uri, false, contentObserver);}class ScreenshotContentObserver extends ContentObserver {    public ScreenshotContentObserver(Handler handler) {        super(handler);    }    @Override    public void onChange(boolean selfChange, Uri uri) {        super.onChange(selfChange, uri);        // 处理uri获取真实的filepath,判断是否是截屏图片,通常filepath会包含screenshot关键字        ...    }}

ContentObserver监听的仅仅只是ContentProvider的记录变动,实际对应文件的变动可能还没有完成,两者可能是个异步的过程或者是操作顺序的问题。例如监听截屏事件,测试时发现onChange被回调了,但是立即操作回调的uri对应的文件却无法得到正确的结果。这是因为截屏文件还没有完全写入导致的,通常的解决方法是提供给用户一个交互界面来缓冲下。

1 0