欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

详解Android截屏事件监听

程序员文章站 2024-02-25 22:01:27
1. 前言 android系统没有直接对截屏事件监听的接口,也没有广播,只能自己动手来丰衣足食,一般有三种方法。 利用fileobserver监听某个目录中资源...

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);
      // 监听到事件,做一些过滤去重处理操作
    }
  }
}

打印的日志:

一加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

三星 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

可以通过指定构造函数中的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);
    }
  }
}

日志:

d/screenshot: content://media/external/images/media
d/screenshot: /storage/emulated/0/pictures/screenshots/screenshot_2016-12-19-11-24-02.png 1482117842287

注意事项:

  1. contentobserver会监听到所有图片资源的变化情况,要做好去重过滤工作
  2. 根据uri去读取contentprovider内容时候,记得关闭cursor,防止内存泄漏
  3. 关键字可扩展,大大增加的监听的范围,比fileobserver好用多了,但是去重过滤会比fileobserver复杂一些。

4. 总结

目前这是在网上搜索到的关于截屏监听方法的总结,如果大家还有什么比较好的监听方法,欢迎分享。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。