最近要求做一个app内监听截屏,然后调用自己的分享功能。
网上有很多方案。这里我采用的是内容监听方案。
参考
https://www.jianshu.com/p/d7aba5a03b0f
结合我的业务实际,以下是完整代码。
0.局部变量
private String data;//截图图片路径
private Handler mUiHandler;
private MediaContentObserver mInternalObserver;
private MediaContentObserver mExternalObserver;
private static final String[] MEDIA_PROJECTIONS = {
// MediaStore.Images.ImageColumns.WIDTH,
// MediaStore.Images.ImageColumns.HEIGHT,
MediaStore.Images.ImageColumns.DATA,
MediaStore.Images.ImageColumns.DATE_TAKEN
};
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 class MediaContentObserver extends ContentObserver {
private Uri mediaContentUri; // 需要观察的Uri
public MediaContentObserver(Uri contentUri, Handler handler) {
super(handler);
mediaContentUri = contentUri;
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
// 处理媒体数据库反馈的数据变化
handleMediaContentChange(mediaContentUri);
}
}
2.注册监听
private void initMediaContentObserver() {
// 运行在 UI 线程的 Handler, 用于运行监听器回调
mUiHandler = new Handler(Looper.getMainLooper());
// 创建内容观察者,包括内部存储和外部存储
mInternalObserver = new MediaContentObserver(MediaStore.Images.Media.INTERNAL_CONTENT_URI, mUiHandler);
mExternalObserver = new MediaContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mUiHandler);
// 注册内容观察者
getContentResolver().registerContentObserver(
MediaStore.Images.Media.INTERNAL_CONTENT_URI, false, mInternalObserver);
getContentResolver().registerContentObserver(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, false, mExternalObserver);
}
3.处理数据
private void handleMediaContentChange(Uri contentUri) {
Cursor cursor = null;
try {
// 数据改变时查询数据库中最后加入的一条数据
cursor = getContentResolver().query(contentUri,
MEDIA_PROJECTIONS,
null, null, MediaStore.Images.ImageColumns.DATE_ADDED + " desc limit 1");
if (cursor == null) return;
if (!cursor.moveToFirst()) return;
// cursor.getColumnIndex获取数据库列索引
int dateTakenIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATE_TAKEN);
long dateTaken = cursor.getLong(dateTakenIndex); // 图片生成时间
//因为该监听也会监听到删除截图的操作,所以判断最后一张图的时间和现在时间,如果相差8s内,则认为是刚截图
//国内部分机器刚截的图并不是马上就写进去,而是放在内存了,8s是一个经验值
if (System.currentTimeMillis() - dateTaken > 8000) return;
int dataIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
data = cursor.getString(dataIndex); // 图片存储地址
// int width = 0;
// int height = 0;
// int widthIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.WIDTH);
// int heightIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.HEIGHT);
// width = cursor.getInt(widthIndex); // 获取图片高度
// height = cursor.getInt(heightIndex); // 获取图片宽度
// 处理获取到的第一行数据
handleMediaRowData(data);
} catch (AndroidRuntimeException e) {
//1.Android7.0以下机器要求用FLAG_ACTIVITY_NEW_TASK启动
//2.部分机器写入截图实在是太慢,给个800ms延迟
mUiHandler.postDelayed(() ->
startActivity(ShareCapturePicActivity.newIntent(this, data,
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP)), 800);
e.printStackTrace();
} finally {
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
}
}
//处理监听到的资源
private void handleMediaRowData(String data) {
if (checkScreenShot(data)) {
startActivity(ShareCapturePicActivity.newIntent(this, data, Intent.FLAG_ACTIVITY_SINGLE_TOP));
}
}
//判断是否是截屏
private boolean checkScreenShot(String data) {
data = data.toLowerCase();
// 判断图片路径是否含有指定的关键字之一, 如果有, 则认为当前截屏了
for (String keyWork : KEYWORDS) {
if (data.contains(keyWork)) {
return true;
}
}
return false;
}
至于FLAG_ACTIVITY_NEW_TASK的问题,我在这篇文章有说。
非activity环境启动activity还需要FLAG_ACTIVITY_NEW_TASK吗?
顺带提一下
onNewIntent的周期
第一次singleTop启动,正常onCreate--start--resume
后续就是 onNewIntent---start--resume
网友评论