記一次安卓手機水印顯示問題的排查歷程

近期在風控部門的要求下,咱們在APP的一些關鍵頁面上添加了水印,技術方案也比較簡單,上線一切正常,不過大概一週以後,陸陸續續開始收到有花屏的反饋,具體截圖以下相似: android

頁面截圖.png

最開始考慮的多是手機有自定義字體的緣故,後面偶然得知是由於安卓系統中輔助功能裏能夠設置高對比字體致使的,高對比文字會在文字的周圍添加描邊,加強視覺效果。因而推薦用戶關掉了這個設置,隨着反饋開始增多,開始考慮若是完全的解決這一問題了。canvas

最開始想到的方案是能不能拿到是否開啓了這個高對比的設置,而後提示給用戶。查了一下還真的能夠經過 AccessibilityManager 的一個屬性拿到,代碼以下:緩存

AccessibilityManager am = (AccessibilityManager) this.getSystemService(Context.ACCESSIBILITY_SERVICE);
boolean isHighTextContrastEnabled = am.isHighTextContrastEnabled();
複製代碼

不過拿到是否開關了這個選項以後提示用戶關閉的體驗總歸仍是不太好,部分用戶確實有開啓這個高對比的需求,那隻能再想辦法能在開啓了高對比度文字的狀況下如何去掉水印的這個高對比的描邊了。bash

Google搜索HighTextContrast這個關鍵字的時候意外發現Canvas有一個setHighContrastText方法,簡直看到了曙光,大概也瞭解了這個是否高亮是跟每一個Canvas繪製的時候綁定的。那就在繪製這個水印的地方調用這個API不就萬事ok了嗎?app

不幸的是Android很任性的把這個方法定義成了一個hide方法:ide

/** @hide */
    public void setHighContrastText(boolean highContrastText) {
        nSetHighContrastText(mNativeCanvasWrapper, highContrastText);
    }
複製代碼

不過不要緊,根據以前收藏已久的偏方,大概是能夠經過ClassLoader的雙親委派機制完美的繞過hide方法的,具體作法是:測試

  1. 在src Java文件夾下建立一個同包名的類,如:android.graphics.Canvas
  2. 定義Canvas中全部用到的方法和屬性,方法只須要定義,能夠給空實現。

這樣AS在編譯的時候優先是引用本地代碼,能夠經過JAVA訪問權限的校驗,運行時因爲ClassLoader的雙親委派機制,加載到JVM中的只會是Android的Canvas類,這樣就不會影響到Canvas類的行爲。字體

根據這個思路實現了以後,完美解決問題,一行代碼解決困擾了幾天的問題,感受倍爽。在準備提交這個commit回家的時候又開始猶豫了,真的要這麼黑科技嗎,對於這個另類的Canvas,之後會不會增長維護成本,還有這個hide掉的API調用會不會命中某個機型隱藏的坑?畢竟是Canvas類啊!ui

仍是犯慫了,再想一想,有沒有更優雅的方式。深刻的瞭解了一下高對比度文字的實現過程,發現它只做用於文字,並且和背景有很大的關係。那天然就會想到若是把每一個水印文字先繪製到一張Bitmap上呢,而後再把Bitmap平鋪到整個頁面,是否是就能夠經過繪製前給定一個不會產生高對比的背景,繪製完成以後再對Bitmap作轉換呢?this

按照這個思路,首先把水印繪製到透明的Bitmap上,而後再直接繪製到Drawable上,實驗了以後發現完美解決問題,並且不須要再對背景作特殊的處理。考慮到Bitmap的複用性,對Bitmap作了必定的緩存,代碼以下:

private static HashMap<String, Bitmap> waterBitmaps = new HashMap<>();

    private static Bitmap getWaterBitmap(String label,int color, int fontSize) {
        String key = generateKey(label,color, fontSize);
        if (!waterBitmaps.containsKey(key)) {
            Paint paint = new Paint();
            paint.setColor(color);
            paint.setAntiAlias(true);
            paint.setTextSize(fontSize);
            Rect rect = new Rect();
            paint.getTextBounds(label, 0, label.length(), rect);

            int width = rect.width();
            int height = rect.height();

            Bitmap waterBitmap = Bitmap.createBitmap(width + 10, height + 10, Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(waterBitmap);
            canvas.drawText(label, 10, height, paint);
            waterBitmaps.put(key, waterBitmap);

            return waterBitmap;
        } else {
            return waterBitmaps.get(key);
        }
    }

    private static String generateKey(String label, int color, int fontSize) {
        return label + "_" + color+ "_" + fontSize;
    }
複製代碼

而後把這個Bitmap平鋪到須要的頁面,通過多個頁面的測試,完美解決問題。經過繪製到Bitmap的方式,不只解決了花屏的問題,也經過直接drawBitmap提升了繪製的效率,一箭雙鵰,perfect!

相關文章
相關標籤/搜索