Android截屏與WebView長圖分享經驗總結

2021-02-22 架構師必備

一、概述

最近在做新業務需求的同時,我們在 Android 上遇到了一些之前沒有碰到過的問題,截屏分享、 WebView 生成長圖以及長圖在各個分享渠道分享時圖片模糊甚至分享失敗等問題,在這過程中踩了很多坑,到目前為止絕大部分的問題都還算是有了比較滿意的解決方案。以下就從三個方面來總結一下過程中遇到的挑戰和最後的解決方案。

二、截圖分享

在 Android 原生系統中是沒有提供截圖的廣播或者監聽事件的,也就是說代碼層面無法獲知用戶的截屏操作,這樣就無法滿足用戶截屏後跳出分享提示的需求。既然無法從根本上解決截屏監聽的問題,那麼就要考慮通過其他方式間接實現,目前比較成熟穩定的方案是監聽系統媒體資料庫資源的變化,具體方案原理如下:

Android 系統有一個媒體資料庫,每拍一張照片,或使用系統截屏截取一張圖片,都會把這張圖片的詳細信息加入到這個媒體資料庫,並發出內容改變通知,我們可以利用內容觀察者(ContentObserver)監聽媒體資料庫的變化,當資料庫有變化時,獲取最後插入的一條圖片數據,如果該圖片符合特定的規則,則認為被截屏了。

考慮到手機存儲包括內部存儲器和外部存儲器,為了增強兼容性,最好同時監聽兩種儲存空間的變化,以下是需要 ContentObserver 監聽的資源 URI :

讀取外部存儲器資源,需要添加權限:

註:在 Android 6.0 及以上版本需要動態申請權限

1. 截屏判斷規則

當 ContentObserver 監聽到媒體資料庫的數據改變, 在有數據改變時獲取最後插入資料庫的一條圖片數據, 如果符合以下規則, 則認為截屏了:

時間判斷:通常截屏生成後會立馬存入系統多媒體資料庫,也就是說監聽到資料庫變化的時間與截圖生成的時間不會相差太多,這裡推薦以10秒作為閾值,當然這個也是經驗值。

尺寸判斷:截屏顧名思義取得是當前手機屏幕尺寸大小的圖片,所以圖片寬高大於屏幕寬高的肯定都不是截圖產生的。

路徑判斷:由於各手機廠家存放截圖的文件路徑都不太一樣,國內情況可能會更嚴重,但是通常圖片保存路徑都會包含一些常見的關鍵詞,比如 「screenshot」、 「screencapture」 、 「screencap」 、 「截圖」、 「截屏」等,每次都檢查圖片路徑信息是否包含這些關鍵詞。

關於第3點需要補充說明一下,由於要判斷圖片文件路徑是否包含關鍵字,所以目前僅支持中英文環境,如果需要支持其他語言,需要手動添加一些該語言的關鍵詞,否則有可能獲取不到圖片。

以上3點基本上可以保證截圖的正常監聽,當然在實際測試過程中,還會發現有些機型存在多報的情況,所以還需要做一些去重等工作,關於去重下面還會再提及。

2. 關鍵代碼

原理都了解清楚了,那麼接下來就是如何實現的問題了。這裡最關鍵是媒體內容觀察者的設置,從資料庫中取出第一條數據並解析圖片信息,然後再檢驗圖片信息是否符合以上3條規則。

為了說清楚如何監聽媒體資料庫改變,先要稍微講一下 ContentObserver 的原理。 ContentObserver ——內容觀察者,目的是觀察(捕捉)特定 Uri 引起的資料庫的變化,繼而做一些相應的處理,它類似於資料庫技術中的觸發器(Trigger),當 ContentObserver 所觀察的 Uri 發生變化時,便會觸發它。當然想要觀察就必須先要註冊, Android 系統提供了 ContentResolver#registerContentObserver 方法用來註冊觀察器。此部分不熟悉的同學可以溫習一下 Android 的 ContentProvider 相關知識。

接下來直接用代碼說明整個註冊和觸發流程,代碼如下:

有註冊就需要在 Activity 銷毀時取消註冊,所以還需要封裝一個解除註冊的方法供外部調用, Android 系統提供 ContentResolver#unregisterContentObserver 方法來取消註冊,代碼比較簡單,這裡就不再展示了。

監聽器設置和註冊完成後,一旦用戶操作了截屏動作,系統就會執行 ContentObserver#onChange 回調方法,在這個方法中我們可以根據 Uri 獲取並解析數據。這裡展示一下具體的數據解析過程,上述提到的規則判斷比較簡單,就不再展示了。

有些手機 ROM 截屏一次會發出多次內容改變的通知,因此需要做去重操作,去重也不複雜,可以用列表緩存最近十幾條圖片地址數據,每次獲取到新的圖片地址,都會先判斷緩存中是否存在相同的圖片地址,如果當前的圖片地址已經存在列表中,則直接過濾掉即可,否則添加到緩存中。如此就可以保證截屏監聽事件既不遺漏也不重複。

以上就是手機截屏的核心原理和關鍵代碼,如果需要分享截屏圖片也很簡單, data 即為圖片的存儲地址,轉換成 Bitmap 即可完成分享。

二、WebView 生成長圖

介紹 web 長圖之前,先來說一下單屏圖片的生成方案,和手機截圖不同的是生成的圖片不會顯示頂部的狀態欄、標題欄以及底部的菜單欄,可以滿足不同的業務需求。

有的時候我們需要將一個長 Web 網頁生成圖片分享出去,相似的例子就是手機端的各種便籤應用,當便籤內容超出一屏時,就需要將所有的內容生成一張長圖對外分享出去。

WebView 和其他 View 一樣,系統都提供了 draw 方法,可以直接將 View 的內容渲染到畫布上,有了畫布我們就可以在上面繪製其他各種各種的內容,比如底部添加 Logo 圖片,畫紅線框等等。關於 WebView 生成長圖網上已經有很多現成的方案和代碼,以下代碼是經測試過的穩定版本,供參考。

Android 為了提高滾動等各方面的繪製速度,可以為每一個 View 建立一個緩存,使用View#buildDrawingCache 為自己的 View 建立相應的緩存, 這個 cache 就是一個 bitmap 對象。利用這個功能可以對整個屏幕視圖進行截屏並生成 Bitmap ,也可以獲得指定的 View 的 Bitmap 對象。這裡由於還要在原有的圖片上繪製 Logo ,所以直接使用了 WebView 的 draw 方法了。

由於我們的 H5 頁面大部分都是運行在微信的 X5 瀏覽器中,所以為了減少前端的適配工作,我們將騰訊的 X5 瀏覽器內核引入了 Android 工程中,代替系統原生的 WebView 內核,關於 X5 內核的引入後續還會有專門的文章介紹,敬請期待。

這裡需要說明一下如何在 X5 內核下生成 Web 長圖,上面代碼展示的系統原生 WebView 生成圖片的方案,但是在 X5 環境下上述代碼就失效了,經過踩坑以及查看 X5 內核原始碼,最終我們找到了解決該問題的方法,下面用關鍵代碼來說明一下具體的實現方式。

註:X5 內核生成的長圖清晰度比原生 WebView 要差一些,目前還沒有太好的解決方案。

三、長圖分享

一般我們向各個社交平臺上發送的圖片都比較小,最大也就是手機屏幕大小的圖片,再大的就不多見了。但是也有例外,比如微博的長圖、錘子便籤的長圖等等,如果直接將這些圖片通過微信分享 SDK 或者微博分享 SDK 分享出去,就會發現圖片基本上都是模糊的,但是將圖片發送給 iPhone 手機就可以正常查看,我們只能哀嘆 Android 版微信不給力。

微信 SDK 不給力,但是產品體驗還是不能丟,怎麼辦呢?辦法還是有的,我們都知道除了各個社交平臺自己的分享 SDK ,系統提供了原生分享方案,本質上就是社交平臺把目標 Activity 對外暴露了出來,然後第三方 App 就可以根據事先定義好的 Intent 跳轉規則喚起社交平臺,同時完成數據傳輸和展示。

好像問題可以完美解決了,但是還是有坑需要接著踩。在 Android 7.0 及以上的版本系統限制了 Intent 傳輸 file:// 開頭的數據,這也就限制了系統原生分享單圖,怎麼辦呢?兩種方案,一種是在 7.0 及以上版本上使用微信等分享 SDK ,接受分享圖片模糊的現狀,另一種是通過反射跳過系統對以 file:// 開頭文件在 Intent 中傳輸的限制,但是這種方式會有風險,畢竟我們不知道未來 Android 會做出什麼調整。以下是跳過系統限制的代碼片段,供參考。

至此基本上可以滿足任意圖片大小的分享了。此外經過驗證還發現微信分享 Android 版 SDK 對縮略圖和分享圖的大小都有限制,官方給的指導意見是縮略圖小於 32K ,分享圖片小於 10M 即可正常分享,但是試驗下來這兩個值都是理論上限,不要太接近這個上限,如果圖片太大,縮略圖和分享圖都會出現模糊的情況,甚至無法正常分享,當然對於通過系統分享的話就不存在這個限制,圖片也比較清晰。

除了圖片大小有限制,縮略圖的尺寸也是有限制的,這一點官方文檔並沒有給出,試驗結果顯示圖片尺寸小於等於120x120是比較安全的範圍,分享都沒有問題。

四、小結

截屏監聽、 WebView 生成長圖以及長圖分享都是我們團隊之前未曾遇到過的業務需求,在滿足產品業務需求的同時,也踩了很多坑,積累了一些經驗,特此總結。

相關焦點

  • Android如何獲取WebView內容高度
    ="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.trs.studyview.view.TRSWebView android:id="@+id/webview
  • Android WebView和JS交互詳細教程
    Android webview和JS的交互已經是老生常談了,坑很多、問題也很多。
  • Flutter加載網頁之官方webview_flutter講解
    對於加載網頁這個功能來說,flutter並沒有組件,反而是第三方開發了一些組件,目前比較常用的有二種: flutter_webview_plugin webview_flutterflutter_webview_plugin是目前用得最火的一種,也是比較好用的,但是它有個致命的缺點,它不支持「進入某個URL之前攔截」,這也是我放棄它的原因。
  • 5步搞定android混淆
    serialPersistentFields;    private void writeObject(java.io.ObjectOutputStream);    private void readObject(java.io.ObjectInputStream);    java.lang.Object writeReplace();    java.lang.Object readResolve();}-keep
  • 華為p40怎麼截屏長圖 華為p40截長圖教程
    很多華為p40的用戶都不清楚怎麼才能截長圖,所以今天就來為大家分享一下華為p40截長圖教程,幫助大家更快的完成截長圖的操作。1、常規截屏後,左下角會出現縮略圖, 向下滑動縮略圖,可以繼續滾動截長屏。2、單指指關節敲擊屏幕並保持指關節不離開屏幕,稍微用力畫「S」,屏幕將自動向下滾動截屏。 滾動過程中,點擊滾動區域可停止截屏。
  • 三年經驗 Android 開發面經總結
    在下2017年畢業,目前從事android開發工作已經3年啦,前段時間剛完成一次跳槽,面試了幾家公司,將一些面試經驗分享給大家,希望對大家有所幫助。簡歷首先是簡歷,一般找一個模板,填寫掌握的技能和項目經歷即可。
  • 來開發一個wanandroid快應用吧
    而hap update —force會重新複製hap-tools文件夾到node_modules中三、調試1.安裝調試器調試器是一個android應用,直接下載安裝這裡通過wanandroid開發api來開發wanandroid應用修改manifest.json配置信息{  "package": "cn.codebear.wanandroid",  "name": "wanandroid",  "versionName": "1.0.0",  "versionCode": "1",  "minPlatformVersion
  • 玩Android 快應用已經開源啦~
    這裡通過wanandroid開發api來開發wanandroid應用http://www.wanandroid.com/blog/show/21.修改manifest.json配置信息{  "package": "cn.codebear.wanandroid",  "name": "wanandroid
  • 開發總結:Android反編譯方法的總結
    總結反編譯主要的目的在於學習。利用反編譯進行相關的漢化或修改,還是儘量不要吧,畢竟人家寫個程序不容易啊!  打開main.txt代碼如下:<?>   <LinearLayout       xmlns:android="http://schemas.android.com/apk/res/android"
  • 安卓手機怎麼截屏啊
    智慧型手機已經成為我們日常生活中不可缺少的隨身物品,使用過程中,經常會遇到想要截屏跟朋友分享的情況,根據經驗,給廣大安卓機友們分享幾種截屏方式!第一種方式:使用手機系統自帶截屏快捷鍵。1)電源鍵 + 音量減小鍵 (同時按下,長按3s鍾即可)(目前安卓系統4.0以上一般都是這個組合)2)上面的組合鍵不行的話,試試:電源鍵+home鍵(房子鍵),同時按下。3)小米手機是 電源鍵+菜單鍵 ,同時按下。第二種方式:借用第三方軟體在電腦端對手機進行截屏。   安裝2345手機助手電腦版,用數據線將手機與電腦端成功連接,電腦端會同步演示手機端畫面,並且可以隨時截屏。
  • 蘋果手機怎麼截圖 iPhone XR/XS截屏/長截圖教程
    今天小編主要為果粉朋友詳細介紹下蘋果iPhone XR截屏方法,包括普通的截屏、長截屏等操作,希望可以幫助到大家。iPhone XR截屏圖文教程iPhone XR怎麼截圖?本文主要分享2種方法,另外還會簡單介紹下如何長截屏。
  • Android WebView 與 JS 的交互方式最全面匯總
    交互方式總結Android與JS通過WebView互相調用方法,實際上是:Android去調用JS的代碼JS去調用Android的代碼二者溝通的橋梁是WebView對於當該JS通過Android的mWebView.loadUrl(「file:///android_asset/javascript.html」)加載後,就會回調shouldOverrideUrlLoading (),接下來繼續看步驟
  • 小米手機如何截屏長圖
    本辦法只限小米用,其他手機未嘗試首先我們先來找到我們準備截屏的頁面,找到頁面後,開始截屏,首先在屏幕上下拉即可找到任務欄,在任務裡選擇截屏,屏幕既會截取當前這個界面,還有一個快速截屏方式,同時按下音量減和菜單鍵即可快速截屏截屏成功後,圖片會在右上角停留一會,這時我們點擊懸浮的截圖,就看到了截取長圖選項接下來選擇此功能
  • 【Android基礎學習一】Android 常用 adb 命令總結
    input tap , 對屏幕發送一個觸摸事件adb shell input tap 500 500點擊屏幕上坐標為 500 500 的位置input swipe , 滑動事件adb shell input swipe 900 500 100 500從右往左滑動屏幕如果版本不低於 4.4 , 可以模擬長按事件
  • Android代碼混淆使用手冊
    '), 'proguard-rules.pro'   }}因為開啟混淆會使編譯時間變長,且不利於斷點調試,所以debug模式下不開啟**擴展:**release下還有一些配置是可以加上的zipAlignEnabled
  • Android測試 常用adb 命令總結
    點擊上方「雲測學院」可以訂閱哦技術文章:3640字 | 5分鐘閱讀親好文記得要收藏的,同時也要分享
  • 榮耀暢玩5A如何截屏,滾動截屏,屏幕錄製
    榮耀暢玩5A,截屏,滾動截屏,屏幕錄製,錄屏#資料課代表 | 講竅門# 榮耀暢玩5A是榮耀近期發布的高性價比手機,它不只是用1300萬像素攝像頭+麒麟8核處理器的配置來體現手機性價比,還通過手機搭載EMUI4.1系統,設置更多功能細節,讓用戶獲得更加合理化的手機體驗。
  • 《Android經驗分享》周刊第6期
    《Android經驗分享》收錄國內外最近一周熱門的技術博客以及優秀的類庫,星期一傍晚前將準時更新給大家微信公眾號:Android
  • 百度推出iOS截長圖App 可自動截屏+裁剪+拼接
    首頁 > 見聞 > 關鍵詞 > 百度最新資訊 > 正文 百度推出iOS截長圖App 可自動截屏+裁剪+拼接
  • 滾動截屏怎麼操作
    滾動截屏操作方法很簡單,只要在截屏後選擇「長截圖」即可了,當然,如果你是用華為手機的話,那就更加方法,開啟指關節截屏後用指關節畫個「S」即可自動滾動截屏。如果你用的是華為手機,且系統比較新的話,可以在手機的設置開啟「指關節滾動截屏」功能,具體開啟步驟:在桌面找到「設置」點擊並進入,在「設置」界面中找到「輔助功能」選項點擊進入,找到「快捷啟動及手勢」選項點擊進入,然後就可以看到「截屏」選項了,同樣點擊進入,開啟「指關節截屏功能」即可。