日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Android截屏與WebView長圖分享經(jīng)驗(yàn)總結(jié)

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

一、概述

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

二、截圖分享

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

Android 系統(tǒng)有一個(gè)媒體數(shù)據(jù)庫,每拍一張照片,或使用系統(tǒng)截屏截取一張圖片,都會(huì)把這張圖片的詳細(xì)信息加入到這個(gè)媒體數(shù)據(jù)庫,并發(fā)出內(nèi)容改變通知,我們可以利用內(nèi)容觀察者(ContentObserver)監(jiān)聽媒體數(shù)據(jù)庫的變化,當(dāng)數(shù)據(jù)庫有變化時(shí),獲取***插入的一條圖片數(shù)據(jù),如果該圖片符合特定的規(guī)則,則認(rèn)為被截屏了。

考慮到手機(jī)存儲(chǔ)包括內(nèi)部存儲(chǔ)器和外部存儲(chǔ)器,為了增強(qiáng)兼容性,***同時(shí)監(jiān)聽兩種儲(chǔ)存空間的變化,以下是需要 ContentObserver 監(jiān)聽的資源 URI :

 
 
 
 
  1. MediaStore.Images.Media.INTERNAL_CONTENT_URI 
  2. MediaStore.Images.Media.EXTERNAL_CONTENT_URI

讀取外部存儲(chǔ)器資源,需要添加權(quán)限:

 
 
 
 
  1. android.permission.READ_EXTERNAL_STORAGE

注:在 Android 6.0 及以上版本需要?jiǎng)討B(tài)申請(qǐng)權(quán)限

1. 截屏判斷規(guī)則

當(dāng) ContentObserver 監(jiān)聽到媒體數(shù)據(jù)庫的數(shù)據(jù)改變, 在有數(shù)據(jù)改變時(shí)獲取***插入數(shù)據(jù)庫的一條圖片數(shù)據(jù), 如果符合以下規(guī)則, 則認(rèn)為截屏了:

  1. 時(shí)間判斷:通常截屏生成后會(huì)立馬存入系統(tǒng)多媒體數(shù)據(jù)庫,也就是說監(jiān)聽到數(shù)據(jù)庫變化的時(shí)間與截圖生成的時(shí)間不會(huì)相差太多,這里推薦以10秒作為閾值,當(dāng)然這個(gè)也是經(jīng)驗(yàn)值。
  2. 尺寸判斷:截屏顧名思義取得是當(dāng)前手機(jī)屏幕尺寸大小的圖片,所以圖片寬高大于屏幕寬高的肯定都不是截圖產(chǎn)生的。
  3. 路徑判斷:由于各手機(jī)廠家存放截圖的文件路徑都不太一樣,國內(nèi)情況可能會(huì)更嚴(yán)重,但是通常圖片保存路徑都會(huì)包含一些常見的關(guān)鍵詞,比如 “screenshot”、 “screencapture” 、 “screencap” 、 “截圖”、 “截屏”等,每次都檢查圖片路徑信息是否包含這些關(guān)鍵詞。

關(guān)于第3點(diǎn)需要補(bǔ)充說明一下,由于要判斷圖片文件路徑是否包含關(guān)鍵字,所以目前僅支持中英文環(huán)境,如果需要支持其他語言,需要手動(dòng)添加一些該語言的關(guān)鍵詞,否則有可能獲取不到圖片。

以上3點(diǎn)基本上可以保證截圖的正常監(jiān)聽,當(dāng)然在實(shí)際測試過程中,還會(huì)發(fā)現(xiàn)有些機(jī)型存在多報(bào)的情況,所以還需要做一些去重等工作,關(guān)于去重下面還會(huì)再提及。

2. 關(guān)鍵代碼

原理都了解清楚了,那么接下來就是如何實(shí)現(xiàn)的問題了。這里最關(guān)鍵是媒體內(nèi)容觀察者的設(shè)置,從數(shù)據(jù)庫中取出***條數(shù)據(jù)并解析圖片信息,然后再檢驗(yàn)圖片信息是否符合以上3條規(guī)則。

為了說清楚如何監(jiān)聽媒體數(shù)據(jù)庫改變,先要稍微講一下 ContentObserver 的原理。 ContentObserver ——內(nèi)容觀察者,目的是觀察(捕捉)特定 Uri 引起的數(shù)據(jù)庫的變化,繼而做一些相應(yīng)的處理,它類似于數(shù)據(jù)庫技術(shù)中的觸發(fā)器(Trigger),當(dāng) ContentObserver 所觀察的 Uri 發(fā)生變化時(shí),便會(huì)觸發(fā)它。當(dāng)然想要觀察就必須先要注冊(cè), Android 系統(tǒng)提供了 ContentResolver#registerContentObserver 方法用來注冊(cè)觀察器。此部分不熟悉的同學(xué)可以溫習(xí)一下 Android 的 ContentProvider 相關(guān)知識(shí)。

接下來直接用代碼說明整個(gè)注冊(cè)和觸發(fā)流程,代碼如下:

 
 
 
 
  1. private void initMediaContentObserver() {
  2.     // 運(yùn)行在 UI 線程的 Handler, 用于運(yùn)行監(jiān)聽器回調(diào) 
  3.     private final Handler mUiHandler = new Handler(Looper.getMainLooper());
  4.     // 創(chuàng)建內(nèi)容觀察者,包括內(nèi)部存儲(chǔ)和外部存儲(chǔ)
  5.     mInternalObserver = new MediaContentObserver(MediaStore.Images.Media.INTERNAL_CONTENT_URI, mUiHandler);
  6.     mExternalObserver = new MediaContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mUiHandler);
  7.     // 注冊(cè)內(nèi)容觀察者
  8.     mContext.getContentResolver().registerContentObserver(
  9.             MediaStore.Images.Media.INTERNAL_CONTENT_URI, false, mInternalObserver);
  10.     mContext.getContentResolver().registerContentObserver(
  11.             MediaStore.Images.Media.EXTERNAL_CONTENT_URI, false, mExternalObserver);
  12. }
  13. /**
  14.  * 自定義媒體內(nèi)容觀察者類(觀察媒體數(shù)據(jù)庫的改變)
  15.  */
  16. private class MediaContentObserver extends ContentObserver {
  17.     private Uri mediaContentUri;       // 需要觀察的Uri
  18.     public MediaContentObserver(Uri contentUri, Handler handler) {
  19.         super(handler);
  20.         mediaContentUri = contentUri;
  21.     }
  22.     @Override
  23.     public void onChange(boolean selfChange) {
  24.         super.onChange(selfChange);
  25.         // 處理媒體數(shù)據(jù)庫反饋的數(shù)據(jù)變化
  26.         handleMediaContentChange(mediaContentUri);
  27.     }
  28. }

有注冊(cè)就需要在 Activity 銷毀時(shí)取消注冊(cè),所以還需要封裝一個(gè)解除注冊(cè)的方法供外部調(diào)用, Android 系統(tǒng)提供 ContentResolver#unregisterContentObserver 方法來取消注冊(cè),代碼比較簡單,這里就不再展示了。

監(jiān)聽器設(shè)置和注冊(cè)完成后,一旦用戶操作了截屏動(dòng)作,系統(tǒng)就會(huì)執(zhí)行 ContentObserver#onChange 回調(diào)方法,在這個(gè)方法中我們可以根據(jù) Uri 獲取并解析數(shù)據(jù)。這里展示一下具體的數(shù)據(jù)解析過程,上述提到的規(guī)則判斷比較簡單,就不再展示了。

  1. private void handleMediaContentChange(Uri contentUri) {
  2.     Cursor cursor = null;
  3.         try {
  4.             // 數(shù)據(jù)改變時(shí)查詢數(shù)據(jù)庫中***加入的一條數(shù)據(jù)
  5.             cursor = mContext.getContentResolver().query(contentUri,
  6.                     Build.VERSION.SDK_INT < 16 ? MEDIA_PROJECTIONS : MEDIA_PROJECTIONS_API_16,
  7.                     null, null, MediaStore.Images.ImageColumns.DATE_ADDED + " desc limit 1");
  8.             if (cursor == null)  return;
  9.             if (!cursor.moveToFirst()) return;       
  10.             // cursor.getColumnIndex獲取數(shù)據(jù)庫列索引
  11.             int dataIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
  12.             String data = cursor.getString(dataIndex);        // 圖片存儲(chǔ)地址
  13.             int dateTakenIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATE_TAKEN);
  14.             long dateTaken = cursor.getLong(dateTakenIndex);  // 圖片生成時(shí)間
  15.             int width = 0;
  16.             int height = 0;
  17.             if (Build.VERSION.SDK_INT >= 16) {
  18.                 int widthIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.WIDTH);
  19.                 int heightIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.HEIGHT);
  20.                 width = cursor.getInt(widthIndex);    // 獲取圖片高度
  21.                 height = cursor.getInt(heightIndex);  // 獲取圖片寬度
  22.             } else {
  23.                 Point size = getImageSize(data);     // 根據(jù)路徑獲取圖片寬和高
  24.                 width = size.x;
  25.                 height = size.y;
  26.             }
  27.             // 處理獲取到的***行數(shù)據(jù),分別判斷路徑是否包含關(guān)鍵詞、時(shí)間差以及圖片寬高和屏幕寬高的大小關(guān)系
  28.             handleMediaRowData(data, dateTaken, width, height);
  29.         } catch (Exception e) {
  30.             e.printStackTrace();
  31.         } finally {
  32.             if (cursor != null && !cursor.isClosed())

    新聞名稱:Android截屏與WebView長圖分享經(jīng)驗(yàn)總結(jié)
    URL地址:http://www.5511xx.com/article/coiedpj.html