歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Android WallpaperManager解析及BUG解決

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;
        }
    }

Copyright © Linux教程網 All Rights Reserved