Android监听系统截屏的坑

最近有作在监听系统截屏操做,而后对截屏的图片获取到,再在其底部加入二维码或者其余信息生成分享海报。这里面最很差作的就是监听系统截屏的操做了,系统没有提供相关api,因此得靠“骚操做”,目前业内主流的操做,像这篇博文写的那样:http://www.noobyard.com/article/p-tckdjgqz-mt.html
经过 自定义媒体内容观察者内部类,去观察媒体数据库的改变,当改变的时候发送通知。然而存在必定的问题:部分机型(或者说一些奇怪的操做会致使读出来的照片并非最新的一张照片,而是一张很老的照片,从而致使了监听截屏操做失败),接下来咱们一块儿来看看这个问题。web

其中ContentObserver基本代码以下:数据库

/**
 * 媒体内容观察者(观察媒体数据库的改变)
 */
private class MediaContentObserver extends ContentObserver {
    private Uri mContentUri;
    public MediaContentObserver(Uri contentUri, Handler handler) {
        super(handler);
        mContentUri = contentUri;
    }
    public void onChange(boolean selfChange) {
        super.onChange(selfChange);
        handleMediaContentChange(mContentUri);
    }
}

其中获取最后一次更新的媒体文件时的代码(为便于查看 删除了判空处理代码):api

/**
 * 处理媒体数据库的内容改变
 */
private void handleMediaContentChange(Uri contentUri) {
    Cursor cursor = null;
    /** 读取媒体数据库时须要读取的列 */
    private static final String[] MEDIA_PROJECTIONS =  {
        MediaStore.Images.ImageColumns.DATA,
        MediaStore.Images.ImageColumns.DATE_TAKEN };
    try {
        // 数据改变时查询数据库中最后加入的一条数据
        cursor = mContext.getContentResolver().query(
                contentUri,
                 MEDIA_PROJECTIONS,
                null,
                null,
                MediaStore.Images.ImageColumns.DATE_ADDED + " desc limit 1"
        );
        // 获取各列的索引
        int dataIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
        int dateTakenIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATE_TAKEN);

        // 获取行数据
        String data = cursor.getString(dataIndex);
        long dateTaken = cursor.getLong(dateTakenIndex);

        // 处理获取到的第一行数据
        handleMediaRowData(data, dateTaken);
}

这里彷佛看不出来问题,流程很清晰:svg

一、监听媒体库的数据变化,变化后回调通知
二、经过查询语句:.net

mContext.getContentResolver().query(
                contentUri,
                MEDIA_PROJECTIONS,
                null, null,
                MediaStore.Images.ImageColumns.DATE_ADDED + " desc limit 1" )

按DATE_ADDED(上文Android监听截屏事件之媒体读取的探索有讲过,DATE_ADDED的意思是“添加的到数据的时间,单位秒),那么这里的意图就是–>“获取到最新添加到数据库的文件”
然而事实呢?
出现的问题就是,有一部分手机监听获取图片的时候,会有拿到一张明明是好久以前的图片,而咱们这里的代码认为那是最新的照片,因而打个断点去看了看为何。3d

咱们对代码进行改造,去打印contentUri里面全部的数据code

cursor = mContext.getContentResolver().query(
                    contentUri,
                    null,
                    null,
                    null,
                    MediaStore.Images.ImageColumns.DATE_ADDED + " desc"
            );
            
            while (cursor.moveToNext()) {
                // 获取各列的索引
                int dataIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
                int dateTakenIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATE_TAKEN);
                int dateAddedIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATE_ADDED);
                int dateModifiedIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATE_MODIFIED);

                // 获取行数据
                String data = cursor.getString(dataIndex);
                String dateTaken = TimeUtils.getFormateTime(cursor.getLong(dateTakenIndex));
                String dateAdded = TimeUtils.getFormateTime(cursor.getLong(dateAddedIndex));
                String dateModified = TimeUtils.getFormateTime(cursor.getLong(dateModifiedIndex));

                Log.e("julis_data","文件名:"+data+" 添加时间:"+" "+dateAdded+" 修改时间:"+dateModified+" dateTaken时间:"+dateTaken);

            }

其中TimeUtils.getFormateTime的做用是将时间戳转换为“yyyy-MM-dd HH:mm”的形式orm

打印出来全部图片的信息以下图所示:
这是排在最前面的图片的信息在这里插入图片描述
找到关于截屏图片的信息在这里插入图片描述
看到打印出来的数据后一上来就傻眼了,dateAdded的时间竟然是在 51436年…这明显有问题嘛。不急,咱们断点去看看各个点的原有数据。server

这是获取到第一张图片时候的断点信息:在这里插入图片描述这是一直向下打断点直到获取到的图片为截屏图片信息这里能够到 data_added的时间是正常的
从两个断点信息能够看到 截图图片的dataAdded的时间是正常的,而咱们看到咱们默认代码按date_added排序获取到最新一张照片的,时间戳为1561025865348,而截屏图片的时间戳为1562747517,明显前者更大,因此致使咱们作监听截屏的时候获取到“最新”的照片并非咱们想要的那张截图照片。其中 1561025865348 很明显是一个以ms毫秒为单位的一个时间戳,因此使用date_added排序的话有一些问题,而咱们打印date_modified获取到的时间都是正确的,而咱们也能够看到获取的 date_taken都是比较正确的数据,都是以秒为单位,因此这里我对查询代码作了下修改:xml

MediaStore.Images.ImageColumns.DATE_ADDED改成MediaStore.Images.ImageColumns.DATE_MODIFIED亲测原来有问题的手机都没有这个问题了。

mContext.getContentResolver().query(
          contentUri,
          MEDIA_PROJECTIONS,
          null, null,
          MediaStore.Images.ImageColumns.DATE_MODIFIED + " desc limit 1" )

总结:
一、将DATE_ADDED改成DATE_MODIFIED这样改动的方式也许不能保证所有手机能监听到截屏,可是本身测出来的原先有问题的手机都没有问题了。
二、经过打印数据发现,DATE_TAKEN也都是统一保持的ms毫秒为单位,讲道理DATE_ADDED改成DATE_TAKEN也应该能解决部分手机没法监听到的问题。
三、我的猜想 部分手机厂商在对图片存储的时候对于添加的时间戳可能存在相关的差别致使了这样的威问题。
四、在58同城一样有相似的问题:在进入聊天界面后,截个屏(至关于当前相册最新图片为那张截屏的图),但是点“+”操做弹出来推荐的照片的确是 MediaStore.Images.ImageColumns.DATE_ADDED desc limit 1最新的那张图片。

在这里插入图片描述