Android WallpaperManager解析及BUG解決

來源:互聯網
上載者:User

Android系統的壁紙是其核心模組之一,但是一直以來壁紙Android的壁紙又有其一直的BUG。例如使用單屏的圖片作為壁紙,在手機重啟後,會自動展開圖片變為隨案頭一起滑動的案頭。還有就是在這種情況下使用案頭,壁紙後面會有惱人的黑色,在壁紙的開始、結束部分會有一部分黑屏,再次啟動後黑條會消失,但壁紙還是處於展開狀態。

近期對該問題通過學習WallpaperManager的相關機制,解決了上述問題,先特分享出來。

1.WallpaperManager的使用,WallpaperManager在使用時通過Context的getSystemService來擷取,通過WallpaperManager我們可以實現設定壁紙,擷取壁紙的寬度、高度、設定所需壁紙的寬度高度。這裡需要說明的是,對於類似於Launcher一類的案頭應用來說,一般對壁紙的要求都是高度全屏,寬度為螢幕寬度的兩倍。這個數值基本是系統的標準數值。從一定程度上來說也是標準。我們知道在Android手機中,案頭滑動時,每次壁紙進行的滑動與案頭實際滑動距離不一樣,相差很多的,會根據案頭的寬度,滑動響應的距離。正因為如此,對壁紙的實際寬度的指定就變得沒有那麼重要了,因為本來案頭的滑動跟後面壁紙的滑動二者沒有數值上的對應關係,所以各家案頭基本都遵守Android標準的規定,使用壁紙時指定壁紙寬度為螢幕寬度的兩倍。同樣的,案頭也可以不指定,在滑動時壁紙由系統自動繪製。

WallpaperManager採用的標準的AIDL服務的形式實現,其實現代碼

通過閱讀WallpaperManagerService會發現WallpaperManager中進行壁紙切換的方式,簡單來說在我們設定壁紙時,WallpaperManager把壁紙以檔案的形式儲存在/data/data/com.android.settings/files/WallpaperManager。同時還有一個關鍵的檔案,/data/system/wallpaper_info.xml,發現該設定檔中儲存有壁紙的期望寬度與高度。但是閱讀WallpaperManager並沒有發現壁紙真正繪製的地方,仔細閱讀後,發現Android中的壁紙管理與壁紙繪製同樣通過ServiceBind的方式來通知繪製壁紙,在Android中預設的繪製方式SystemUI的ImageWallpaper,通過閱讀該部分代碼,可以發現壁紙的真正繪製通過Surfice上使用Canvas,或硬體加速的方式實現繪製。瞭解到這裡基本就可以解釋、解決WallpaperManager的問題了。

1.WallpaperManager的問題一:

指定豎屏、單屏壁紙後,開機壁紙被展開。這個主要是在WallpaperManagerService中load設定檔的問題,在Android中,設定壁紙後,直接會將壁紙的寬度、高度儲存在設定檔中,在啟動時,讀取設定檔,指導繪製方進行繪製。問題就出在讀取設定檔的時候,在Android中,不允許壁紙的寬度比高度小,具體的原因沒有看明白,系統中在發現儲存的寬度比高度小後,會對其寬度進行展開,拉到與高度一樣高,這就是為什麼原來豎屏、單屏的壁紙重啟後變成可以滑動的了。主要問題代碼如下:

    private void loadSettingsLocked() {        if (DEBUG) Slog.v(TAG, "loadSettingsLocked");                JournaledFile journal = makeJournaledFile();        FileInputStream stream = null;        File file = journal.chooseForRead();        boolean success = false;        try {            stream = new FileInputStream(file);            XmlPullParser parser = Xml.newPullParser();            parser.setInput(stream, null);            int type;            do {                type = parser.next();                if (type == XmlPullParser.START_TAG) {                    String tag = parser.getName();                    if ("wp".equals(tag)) {                        mWidth = Integer.parseInt(parser.getAttributeValue(null, "width"));                        mHeight = Integer.parseInt(parser.getAttributeValue(null, "height"));                        mName = parser.getAttributeValue(null, "name");                        String comp = parser.getAttributeValue(null, "component");                        mNextWallpaperComponent = comp != null                                ? ComponentName.unflattenFromString(comp)                                : null;                        if (mNextWallpaperComponent == null ||                                "android".equals(mNextWallpaperComponent.getPackageName())) {                            mNextWallpaperComponent = mImageWallpaperComponent;                        }                                                  if (DEBUG) {                            Slog.v(TAG, "mWidth:" + mWidth);                            Slog.v(TAG, "mHeight:" + mHeight);                            Slog.v(TAG, "mName:" + mName);                            Slog.v(TAG, "mNextWallpaperComponent:" + mNextWallpaperComponent);                        }                    }                }            } while (type != XmlPullParser.END_DOCUMENT);            success = true;        } catch (NullPointerException e) {            Slog.w(TAG, "failed parsing " + file + " " + e);        } catch (NumberFormatException e) {            Slog.w(TAG, "failed parsing " + file + " " + e);        } catch (XmlPullParserException e) {            Slog.w(TAG, "failed parsing " + file + " " + e);        } catch (IOException e) {            Slog.w(TAG, "failed parsing " + file + " " + e);        } catch (IndexOutOfBoundsException e) {            Slog.w(TAG, "failed parsing " + file + " " + e);        }        try {            if (stream != null) {                stream.close();            }        } catch (IOException e) {            // Ignore        }        if (!success) {            mWidth = -1;            mHeight = -1;            mName = "";        }        // We always want to have some reasonable width hint.        WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);        Display d = wm.getDefaultDisplay();        int baseSize = d.getMaximumSizeDimension();        if (mWidth < baseSize) {            mWidth = baseSize;        }        if (mHeight < baseSize) {            mHeight = baseSize;        }    }

 

WallpaperManager的問題二,豎屏壁紙兩側會有黑邊。這個問題就是具體的壁紙繪製的問題了,簡單來講就是壁紙本身沒有被展開到案頭所需要的寬度,在ImageWallpaper中進行繪製時,會根據Wallpaper的需要寬度、壁紙的寬度來指定壁紙在什麼位置來進行繪製,黑邊就是由於畫布太寬,留出黑邊繪製壁紙,造成這個問題。

        void drawFrameLocked() {            if (!mVisible) {                if (DEBUG) {                    Log.d(TAG, "Suppressed drawFrame since wallpaper is not visible.");                }                return;            }            if (!mRedrawNeeded && !mOffsetsChanged) {                if (DEBUG) {                    Log.d(TAG, "Suppressed drawFrame since redraw is not needed "                            + "and offsets have not changed.");                }                return;            }            if (mBackgroundWidth < 0 || mBackgroundHeight < 0) {                // If we don't yet know the size of the wallpaper bitmap,                // we need to get it now.                updateWallpaperLocked();            }            SurfaceHolder sh = getSurfaceHolder();            final Rect frame = sh.getSurfaceFrame();            final int dw = frame.width();            final int dh = frame.height();            final int availw = dw - mBackgroundWidth;            final int availh = dh - mBackgroundHeight;            int xPixels = availw < 0 ? (int)(availw * mXOffset + .5f) : (availw / 2);            int yPixels = availh < 0 ? (int)(availh * mYOffset + .5f) : (availh / 2);            mOffsetsChanged = false;            if (!mRedrawNeeded && xPixels == mLastXTranslation && yPixels == mLastYTranslation) {                if (DEBUG) {                    Log.d(TAG, "Suppressed drawFrame since the image has not "                            + "actually moved an integral number of pixels.");                }                return;            }            mRedrawNeeded = false;            mLastXTranslation = xPixels;            mLastYTranslation = yPixels;            if (mBackground == null) {                // If we somehow got to this point after we have last flushed                // the wallpaper, well we really need it to draw again.  So                // seems like we need to reload it.  Ouch.                updateWallpaperLocked();            }            if (mIsHwAccelerated) {                if (!drawWallpaperWithOpenGL(sh, availw, availh, xPixels, yPixels)) {                    drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);                }            } else {                drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);            }            if (FIXED_SIZED_SURFACE) {                // If the surface is fixed-size, we should only need to                // draw it once and then we'll let the window manager                // position it appropriately.  As such, we no longer needed                // the loaded bitmap.  Yay!                mBackground = null;                mWallpaperManager.forgetLoadedWallpaper();            }        }

 

最後一個思考就是為什麼有黑邊後,重啟後就沒有了那?

這個就需要把整個壁紙管理、繪製的方式綜合來看了。

1.首先,設定一張壁紙後,/data/system/wallpaper_info.xml儲存的是該壁紙的真是寬高。

2.開機後,load壁紙資訊時,發現寬度比高度小,所以對壁紙的寬高進行修改,返回的寬度與高度相等。舉例同為800,返回給ImageWallpaper,ImageWallpaper據此展開壁紙圖片為800x800。

3.在標準案頭中,指定寬度為480*2=960,要求ImageWallpaper進行展開繪製,ImageWallpaper通過計算,留出足夠黑邊後繪製壁紙,此時出現黑邊。同時WallpaperManager儲存寬度到wallpaper_info.xml中,也就是wallpaper_info中的寬度變為960。

4.再次啟動後,返回的壁紙寬度變為960,在ImageWallpaper中按照960進行展開圖片,所以再次開機後,已經沒有黑邊了,可以全螢幕顯示。

 

——歡迎轉載,請註明出處http://blog.csdn.net/zyplus——

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.