1. 前言
Android系统没有直接对截屏事件监听的接口,也没有广播,只能自己动手来丰衣足食,一般有三种方法。
- 利用FileObserver监听某个目录中资源变化情况
- 利用ContentObserver监听全部资源的变化
- 监听截屏快捷按键
由于厂商自定义Android系统的多样性,再加上快捷键的不同以及第三方应用,监听截屏快捷键这事基本不靠谱,可以直接忽略。
本文使用的测试手机,一加2(One Plus 2)。
2. FileObserver
添加权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
代码示例:
public class ScreenshotActivity extends AppCompatActivity {
private final String TAG = "Screenshot";
private static final String PATH = Environment.getExternalStorageDirectory() + File.separator
+ Environment.DIRECTORY_PICTURES + File.separator + "Screenshots" + File.separator;
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_screenshot);
mFileObserver = new CustomFileObserver(PATH);
}
@Override
protected void onResume() {
super.onResume();
mFileObserver.startWatching();
Log.d(TAG, PATH);
}
@Override
protected void onStop() {
super.onStop();
mFileObserver.stopWatching();
}
/**
* 目录监听器
*/
private class CustomFileObserver extends FileObserver {
private String mPath;
public CustomFileObserver(String path) {
super(path);
this.mPath = path;
}
public CustomFileObserver(String path, int mask) {
super(path, mask);
this.mPath = path;
}
@Override
public void onEvent(int event, String path) {
Log.d(TAG, path + " " + event);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
打印的日志:
一加2
D/Screenshot: Screenshot_2016-12-16-17-49-18.png 256
D/Screenshot: Screenshot_2016-12-16-17-49-18.png 32
D/Screenshot: Screenshot_2016-12-16-17-49-18.png 2
D/Screenshot: Screenshot_2016-12-16-17-49-18.png 2
D/Screenshot: Screenshot_2016-12-16-17-49-18.png 2
D/Screenshot: Screenshot_2016-12-16-17-49-18.png 2
D/Screenshot: Screenshot_2016-12-16-17-49-18.png 2
D/Screenshot: Screenshot_2016-12-16-17-49-18.png 2
D/Screenshot: Screenshot_2016-12-16-17-49-18.png 2
D/Screenshot: Screenshot_2016-12-16-17-49-18.png 2
D/Screenshot: Screenshot_2016-12-16-17-49-18.png 2
D/Screenshot: Screenshot_2016-12-16-17-49-18.png 2
D/Screenshot: Screenshot_2016-12-16-17-49-18.png 2
D/Screenshot: Screenshot_2016-12-16-17-49-18.png 2
D/Screenshot: Screenshot_2016-12-16-17-49-18.png 2
D/Screenshot: Screenshot_2016-12-16-17-49-18.png 2
D/Screenshot: Screenshot_2016-12-16-17-49-18.png 2
D/Screenshot: Screenshot_2016-12-16-17-49-18.png 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
三星 S4
D/Screenshot: Screenshot_2016-12-16-19-01-08.png 256
D/Screenshot: Screenshot_2016-12-16-19-01-08.png 32
D/Screenshot: Screenshot_2016-12-16-19-01-08.png 2
D/Screenshot: Screenshot_2016-12-16-19-01-08.png 2
D/Screenshot: Screenshot_2016-12-16-19-01-08.png 2
D/Screenshot: Screenshot_2016-12-16-19-01-08.png 2
D/Screenshot: Screenshot_2016-12-16-19-01-08.png 2
D/Screenshot: Screenshot_2016-12-16-19-01-08.png 2
D/Screenshot: Screenshot_2016-12-16-19-01-08.png 2
D/Screenshot: Screenshot_2016-12-16-19-01-08.png 2
D/Screenshot: Screenshot_2016-12-16-19-01-08.png 2
D/Screenshot: Screenshot_2016-12-16-19-01-08.png 2
D/Screenshot: Screenshot_2016-12-16-19-01-08.png 2
D/Screenshot: Screenshot_2016-12-16-19-01-08.png 2
D/Screenshot: Screenshot_2016-12-16-19-01-08.png 2
D/Screenshot: Screenshot_2016-12-16-19-01-08.png 2
D/Screenshot: Screenshot_2016-12-16-19-01-08.png 2
D/Screenshot: Screenshot_2016-12-16-19-01-08.png 2
D/Screenshot: Screenshot_2016-12-16-19-01-08.png 2
D/Screenshot: Screenshot_2016-12-16-19-01-08.png 2
D/Screenshot: Screenshot_2016-12-16-19-01-08.png 2
D/Screenshot: Screenshot_2016-12-16-19-01-08.png 8
D/Screenshot: Screenshot_2016-12-16-19-01-08.png 32
D/Screenshot: Screenshot_2016-12-16-19-01-08.png 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
可以通过指定构造函数中的mask,监听某一个事件。
类型 | int值 | 说明 |
---|
FileObserver.ACCESS | 1 | 读取某个文件 |
FileObserver.MODIFY | 2 | 向某个文件写入数据 |
FileObserver.ATTRIB | 4 | 文件的属性被修改(权限/日期/拥有者) |
FileObserver.CLOSE_WRITE | 8 | 写入数据后关闭 |
FileObserver.CLOSE_NOWRITE | 16 | 只读模式打开文件后关闭 |
FileObserver.OPEN | 32 | 打开某个文件 |
FileObserver.MOVED_FROM | 64 | 有文件或者文件夹从被监听的文件夹中移走 |
FileObserver.MOVED_TO | 128 | 有文件或者文件夹移动到被监听的文件夹 |
FileObserver.CREATE | 256 | 文件或者文件夹被创建 |
FileObserver.DELETE | 512 | 文件被删除 |
FileObserver.DELETE_SELF | 1024 | 被监听的文件或者目录被删除 |
FileObserver.MOVE_SELF | 2048 | 被监听的文件或者目录被移走 |
几点注意事项:
- 每一次截屏,有多个事件回调
- 每一次截屏,不同的手机,事件回调可能有些不同,参考上述日志
- 不同的手机,默认截屏图片储存的文件夹可能不同
- FileObserver只能监听文件夹中子文件和子文件夹的变化情况,不能监听子文件夹内部的资源变化
- 需要
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
权限,否则可能收不到事件
基于第三点和第四点,这种方法并不能适用于所有的机型。
注意:如果自己写Demo没有收到事件,检查一下权限和监听的目录
3. ContentObserver
ContentObserver用来监听指定uri的所有资源变化,我们可以用它来监听图片资源变化情况,然后做过滤。
添加权限
<uses-permission android:name="MediaStore.Images.Media.INTERNAL_CONTENT_URI"/>
<uses-permission android:name="MediaStore.Images.Media.EXTERNAL_CONTENT_URI"/>
代码示例:
public class ScreenshotActivity extends AppCompatActivity {
private static final String[] KEYWORDS = {
"screenshot", "screen_shot", "screen-shot", "screen shot",
"screencapture", "screen_capture", "screen-capture", "screen capture",
"screencap", "screen_cap", "screen-cap", "screen cap"
};
/** 读取媒体数据库时需要读取的列 */
private static final String[] MEDIA_PROJECTIONS = {
MediaStore.Images.ImageColumns.DATA,
MediaStore.Images.ImageColumns.DATE_TAKEN,
};
/** 内部存储器内容观察者 */
private ContentObserver mInternalObserver;
/** 外部存储器内容观察者 */
private ContentObserver mExternalObserver;
private HandlerThread mHandlerThread;
private Handler mHandler;
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_screenshot);
mHandlerThread = new HandlerThread("Screenshot_Observer");
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
mInternalObserver = new MediaContentObserver(MediaStore.Images.Media.INTERNAL_CONTENT_URI, mHandler);
mExternalObserver = new MediaContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mHandler);
this.getContentResolver().registerContentObserver(
MediaStore.Images.Media.INTERNAL_CONTENT_URI,
false,
mInternalObserver
);
this.getContentResolver().registerContentObserver(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
false,
mExternalObserver
);
}
protected void onDestroy() {
super.onDestroy();
this.getContentResolver().unregisterContentObserver(mInternalObserver);
this.getContentResolver().unregisterContentObserver(mExternalObserver);
}
private void handleMediaContentChange(Uri contentUri) {
Cursor cursor = null;
try {
cursor = this.getContentResolver().query(
contentUri,
MEDIA_PROJECTIONS,
null,
null,
MediaStore.Images.ImageColumns.DATE_ADDED + " desc limit 1"
);
if (cursor == null) {
return;
}
if (!cursor.moveToFirst()) {
return;
}
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);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
}
}
/**
* 处理监听到的资源
*/
private void handleMediaRowData(String data, long dateTaken) {
if (checkScreenShot(data, dateTaken)) {
Log.d(TAG, data + " " + dateTaken);
} else {
Log.d(TAG, "Not screenshot event");
}
}
/**
* 判断是否是截屏
*/
private boolean checkScreenShot(String data, long dateTaken) {
data = data.toLowerCase();
for (String keyWork : KEYWORDS) {
if (data.contains(keyWork)) {
return true;
}
}
return false;
}
/**
* 媒体内容观察者(观察媒体数据库的改变)
*/
private class MediaContentObserver extends ContentObserver {
private Uri mContentUri;
public MediaContentObserver(Uri contentUri, Handler handler) {
super(handler);
mContentUri = contentUri;
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
Log.d(TAG, mContentUri.toString());
handleMediaContentChange(mContentUri);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
日志:
D/Screenshot: content://media/external/images/media
D/Screenshot: /storage/emulated/0/Pictures/Screenshots/Screenshot_2016-12-19-11-24-02.png 1482117842287
注意事项:
- ContentObserver会监听到所有图片资源的变化情况,要做好去重过滤工作
- 根据uri去读取ContentProvider内容时候,记得关闭cursor,防止内存泄漏
- 关键字可扩展,大大增加的监听的范围,比FileObserver好用多了,但是去重过滤会比FileObserver复杂一些。
4. 总结
目前这是在网上搜索到的关于截屏监听方法的总结,如果大家还有什么比较好的监听方法,欢迎分享。
参考: