android截圖事件監(jiān)聽(tīng)的原理與實(shí)現(xiàn)
Android系統(tǒng)沒(méi)有對(duì)用戶截屏行為提供回調(diào)的api,所以我們只能走野路子來(lái)獲取用戶是否截屏了。一般大家都會(huì)采用如下兩種方法
1.監(jiān)聽(tīng)截屏圖片所在目錄變化(FileObserver)
2.監(jiān)聽(tīng)媒體庫(kù)的變化(ContentObserver)
上面兩種方法均不是萬(wàn)能的,需要結(jié)合使用才能達(dá)到良好的效果,首先看看如何監(jiān)控目錄
在android中,我們可以通過(guò)FileObserver來(lái)監(jiān)聽(tīng)目錄變化,先來(lái)看看如何使用
private static final File DIRECTORY_PICTURES = new File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_PICTURES); private static final File DIRECTORY_DCIM = new File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_DCIM); if (manufacturer.equalsIgnoreCase("xiaomi")) { DIRECTORY_SCREENSHOT = new File(DIRECTORY_DCIM, "Screenshots"); } else { DIRECTORY_SCREENSHOT = new File(DIRECTORY_PICTURES, "Screenshots"); } FILE_OBSERVER = new FileObserver(DIRECTORY_SCREENSHOT.getPath(), FileObserver.ALL_EVENTS) { @Override public void onEvent(int event, String path) { if (event == FileObserver.CREATE) { String newPath = new File(DIRECTORY_SCREENSHOT, path).getAbsolutePath(); Log.d(TAG, "path: " + newPath); } } };
我們對(duì)指定目錄的指定事件監(jiān)聽(tīng)即可,當(dāng)事件被觸發(fā)時(shí)onEvent會(huì)回調(diào)。這里我們只關(guān)心目錄中有沒(méi)有新的文件生成。
坑1:在實(shí)踐中發(fā)現(xiàn),并不是所有手機(jī)都允許如此監(jiān)聽(tīng)或者說(shuō)都能收到回調(diào)。有的手機(jī)上面無(wú)法收到CREATE事件,但是可以收到其他事件。
我還發(fā)現(xiàn),有的時(shí)候收到的事件并沒(méi)有在FileObserver中定義,比如32768!下面是Linux中相應(yīng)event對(duì)應(yīng)的含義,32768=IN_IGNORED,但是為什么會(huì)ignore,并不清楚。
http://rswiki.csie.org/lxr/http/source/include/linux/inotify.h?a=m68k#L45
還遇到過(guò)1073741856(1073741856 = 0x40000000 | 0x20,即IN_OPEN | IN_ISDIR)和1073741840(1073741840 = 0x40000000 | 0x10,即IN_CLOSE_NOWRITE | IN_ISDIR)。
坑2:不同手機(jī),監(jiān)聽(tīng)的目錄并不一致。小米需要監(jiān)聽(tīng)Environment.DIRECTORY_DCIM,其他監(jiān)聽(tīng)Environment.DIRECTORY_PICTURES即可。
關(guān)于FileObserver這里再多說(shuō)兩句,F(xiàn)ileObserver無(wú)法進(jìn)行遞歸監(jiān)聽(tīng),也就是說(shuō),我們監(jiān)聽(tīng)的文件夾中如果有子文件夾,并且我們想知道其中變化,這種方式是不可行的。需要手動(dòng)對(duì)子文件進(jìn)行操作。
另外,當(dāng)我們監(jiān)聽(tīng)的目錄/文件被刪除后又重新建立了一個(gè)同名的目錄/文件,之前的FileObserver不會(huì)繼續(xù)工作,需要重新設(shè)置監(jiān)聽(tīng)才行。
還要注意,F(xiàn)ileObserver回調(diào)并不在主線程中,而是在FileObserver線程中。
鑒于上述原因,我們還要使用方法2,監(jiān)聽(tīng)媒體庫(kù)變化。這個(gè)方法使用ContentObserver即可。
private static final ContentObserver CONTENT_OBSERVER = new ContentObserver(HANDLER) { @Override public void onChange(boolean selfChange, Uri uri) { //記得先檢查讀文件的權(quán)限 ContentResolver resolver = GeneralInfoHelper.getContext().getContentResolver(); if (uri.toString().matches(MediaStore.Images.Media.EXTERNAL_CONTENT_URI + "(/\\d+)?")) { Cursor cursor = resolver.query(uri, PROJECTION, null, null, MediaStore.MediaColumns.DATE_ADDED + " DESC"); if (cursor != null && cursor.moveToFirst()) { //完整路徑 String newPath = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.DATA)); File file = new File(newPath); //file.exists() 判斷文件是否存在 } if (cursor != null) { cursor.close(); } } } }; getContentResolver().registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, CONTENT_OBSERVER);
坑3:實(shí)踐中發(fā)現(xiàn),并不是所有手機(jī)都是監(jiān)聽(tīng)相同的Uri,有的帶數(shù)字,有的不帶。
坑4:查詢數(shù)據(jù)庫(kù)時(shí)記得按MediaStore.MediaColumns.DATE_ADDED字段排序,注意,這個(gè)時(shí)間單位是秒,不是毫秒
坑5:即使排了序,你拿到的仍然有可能不是正確的,在魅族E2上面出現(xiàn)了這個(gè)問(wèn)題。但是當(dāng)我刪除了魅族E2截圖文件夾之后,一切又恢復(fù)正常了……這里我做了一個(gè)簡(jiǎn)單的判斷,如何DATE_ADDED和當(dāng)前時(shí)間相差兩秒以內(nèi),那么從數(shù)據(jù)庫(kù)查出的這條數(shù)據(jù)我視為有效
坑6:當(dāng)用戶刪除了截圖文件夾的時(shí)候,媒體庫(kù)此時(shí)會(huì)更新,所以此時(shí)onChange會(huì)收到大量回調(diào),所以這里需要判斷判斷文件是否存在。
可能有人會(huì)問(wèn),為什么不直接用第二種方法?
原因有2,首先從坑5可以看出第二種方法也并非100%有效,其次,這種方法速度很慢,通常會(huì)有2-3秒的延遲。而第一種方法如果有效,通常都會(huì)比后者快很多。
好了,障礙基本掃清,下面開(kāi)始融合兩種方法
首先使用成員變量記錄截圖文件路徑
private static String sScreenshotPath;
當(dāng)方法1或者方法2收到結(jié)果時(shí),用收到的結(jié)果與sScreenshotPath對(duì)比,如果是同一個(gè)文件,那么就無(wú)需再次通知了,否則則進(jìn)行通知。
邏輯太簡(jiǎn)單,代碼就不寫了。但是實(shí)際情況是不會(huì)這么樂(lè)觀的。
坑7:在實(shí)踐中發(fā)現(xiàn),有的系統(tǒng)不直接保存截圖,而是先生成一個(gè)隱藏文件,比如叫.截圖.jpg,然后再修改文件名(去掉“.”)。這種情況下,我們可能就會(huì)收到兩次用戶截圖事件的回調(diào)(方法1和方法2都可能收到回調(diào)),但實(shí)際用戶只截了一次。
這里我做了一個(gè)特殊處理,在判斷是否是同一個(gè)文件時(shí),只判斷文件名,而不去管文件的完整路徑也不管文件是否隱藏(也就是不比較文件名前面的“.”)
//僅靠文件名而不是全路徑判斷是否為同一個(gè)截圖文件,因?yàn)橛行┫到y(tǒng)對(duì)截圖有move操作 private static boolean isSameFile(String newPath) { if (TextUtils.isEmpty(sScreenshotPath)) { return false; } return TextUtils.equals(removePrefixDot(new File(sScreenshotPath).getName()), removePrefixDot(new File(newPath).getName())); } private static String removePrefixDot(@NonNull String filename) { if (filename.startsWith(".")) { return filename.substring(1); } return filename; }
至此,android截圖事件監(jiān)聽(tīng)基本結(jié)束,由于測(cè)試機(jī)器有限,故無(wú)法保證上述方法萬(wàn)無(wú)一失。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Native.js獲取監(jiān)聽(tīng)開(kāi)關(guān)等操作Android藍(lán)牙設(shè)備實(shí)例代碼
- native.js獲取手機(jī)硬件基本信息實(shí)例代碼android版
- Dcloud的native.js直接撥打電話Android實(shí)例代碼
- DCloud的native.js調(diào)用系統(tǒng)分享實(shí)例Android版代碼
- Android中通過(guò)view方式獲取當(dāng)前Activity的屏幕截圖實(shí)現(xiàn)方法
- Android中如何獲取視頻文件的截圖、縮略圖
- Android模擬器中窗口截圖存成文件實(shí)現(xiàn)思路及代碼
- 詳解有關(guān)Android截圖與錄屏功能的學(xué)習(xí)
- Android實(shí)現(xiàn)截圖和分享功能的代碼
- Android獲取常用輔助方法(獲取屏幕高度、寬度、密度、通知欄高度、截圖)
- Android實(shí)現(xiàn)拍照截圖功能
- Android屏幕及view的截圖實(shí)例詳解
- Android截屏截圖的幾種方法總結(jié)
- Android實(shí)現(xiàn)截圖分享qq 微信功能
- Android實(shí)現(xiàn)從相冊(cè)截圖的功能
- Android 中WebView 截圖的實(shí)現(xiàn)方式
- Android App內(nèi)監(jiān)聽(tīng)截圖加二維碼功能代碼
- Android 5.0及以上編程實(shí)現(xiàn)屏幕截圖功能的方法
- Android仿銀行客戶簽名并且保存簽名的截圖文件并命名為本地時(shí)間
- Android 截圖功能源碼的分析
- Android使用WebView實(shí)現(xiàn)截圖分享功能
- Native.js屏幕截圖實(shí)例代碼
相關(guān)文章
Android之Notification的多種用法實(shí)例
本篇文章主要介紹了Android之Notification的多種用法實(shí)例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-12-12Android ActionBar完全解析使用官方推薦的最佳導(dǎo)航欄(上)
Action Bar是一種新増的導(dǎo)航欄功能,在Android 3.0之后加入到系統(tǒng)的API當(dāng)中,它標(biāo)識(shí)了用戶當(dāng)前操作界面的位置,并提供了額外的用戶動(dòng)作、界面導(dǎo)航等功能2017-04-04Android編程實(shí)現(xiàn)webview執(zhí)行l(wèi)oadUrl時(shí)隱藏鍵盤的workround效果
這篇文章主要介紹了Android編程實(shí)現(xiàn)webview執(zhí)行l(wèi)oadUrl時(shí)隱藏鍵盤的workround效果,較為詳細(xì)的分析了執(zhí)行l(wèi)oadUrl時(shí)隱藏鍵盤的workround具體步驟與兩種實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10Android 基于百度語(yǔ)音的語(yǔ)音交互功能(推薦)
最近在開(kāi)發(fā)android的項(xiàng)目,在項(xiàng)目需求中要用到語(yǔ)音喚醒功能。之前都沒(méi)接觸過(guò),今天小編就給大家分享android基于百度語(yǔ)音的語(yǔ)音交互功能,非常不錯(cuò),感興趣的朋友一起看看吧2016-11-11RecyclerView實(shí)現(xiàn)仿支付寶應(yīng)用管理
這篇文章主要為大家詳細(xì)介紹了RecyclerView實(shí)現(xiàn)仿支付寶應(yīng)用管理的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-04-04Android使用PowerImageView實(shí)現(xiàn)播放強(qiáng)大的ImageView動(dòng)畫效果
今天我們就來(lái)編寫一個(gè)PowerImageView控件,讓它既能支持ImageView控件原生的所有功能,同時(shí)還可以播放GIF圖片2018-05-05Android開(kāi)發(fā)之無(wú)痕過(guò)渡下拉刷新控件的實(shí)現(xiàn)思路詳解
下拉刷新效果功能在程序開(kāi)發(fā)中經(jīng)常會(huì)見(jiàn)到,今天小編抽時(shí)間給大家分享Android開(kāi)發(fā)之無(wú)痕過(guò)渡下拉刷新控件的實(shí)現(xiàn)思路詳解,需要的朋友參考下吧2016-11-11