android之壁紙機制

android之壁紙機制 
1.涉及核心類: 
1>ImageWallpaper.java(IW):繼承WallpaperService主要負責靜態壁紙的draw處理; 
2>WallpaperManager.java(WM):主要負責壁紙的存取方法管理(可能會多個實例); 
3>WallpaperManagerService(WMS).java:主要是對WalllpaperManager一些核心方法提供,及一些初始參數的保存(服務); 
4>iWallpaperManager.aidl(IWM):負責WallpaperManager與WallpaperManagerService之間的通信; 
5>IWallpaperManagerCallback(IMC).aidl:負責WallpaperManager與WallpaperManagerService之間的通信,這是一個回調機制與前面不同; 
6>WallpaperService.java(WS):設置壁紙的引擎機制(包括動態與靜態);//這類工作時沒有修改過,所以個人瞭解不是很清楚,希望有朋友補充. 
7>launcher.java(LC)設置壁紙初始化值,帶到壁紙機制的轉屏. 

2. 壁紙從設置存到取流程: 
1>首先WM.setBitmap(bitmap) 
public void setBitmap(Bitmap bitmap) throws IOException { 
        try { 
            ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null); 
            if (fd == null) { 
                return; 
            } 
            FileOutputStream fos = null; 
            try { 
                fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); 
                bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos); 
            } finally { 
                if (fos != null) { 
                    fos.close(); 
                } 
            } 
        } catch (RemoteException e) { 
        } 
    } 
2>然後WMS.setWallpaper(null),設置成功會寫入到壁紙相應文件裏,文件監聽類此時會觸發 
private final FileObserver mWallpaperObserver = new FileObserver( 
            WALLPAPER_DIR.getAbsolutePath(), CREATE | CLOSE_WRITE | DELETE | DELETE_SELF) { 
                @Override 
                public void onEvent(int event, String path) { 
                    if (path == null) { 
                        return; 
                    } 
                    synchronized (mLock) { 
                        // changing the wallpaper means we'll need to back up the new one 
                        long origId = Binder.clearCallingIdentity(); 
                        BackupManager bm = new BackupManager(mContext); 
                        bm.dataChanged(); 
                        Binder.restoreCallingIdentity(origId); 

                        File changedFile = new File(WALLPAPER_DIR, path); 
                        if (WALLPAPER_FILE.equals(changedFile)) { 
                            notifyCallbacksLocked();//會發出廣播與調用回調方法 
                        } 
                    } 
                } 
            }; 

//notifyCallbacksLocked()做兩件事情 
   private void notifyCallbacksLocked() { 
        final int n = mCallbacks.beginBroadcast(); 
        for (int i = 0; i < n; i++) { 
            try { 
                mCallbacks.getBroadcastItem(i).onWallpaperChanged();//回調機制在WM實現onWallpaperChanged() 
            } catch (RemoteException e) { 

                // The RemoteCallbackList will take care of removing 
                // the dead object for us. 
            } 
        } 
        mCallbacks.finishBroadcast(); 
        final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);//壁紙變化的廣播意圖 
        mContext.sendBroadcast(intent);//IW會接收到此意圖,IW.updateWallpaper()去獲取新壁紙. 
    } 

3>WM.onWallpaperChanged()通過handler機制清除壁紙緩存 
   private final Handler mHandler; 
        
        Globals(Looper looper) { 
            IBinder b = ServiceManager.getService(Context.WALLPAPER_SERVICE); 
            mService = IWallpaperManager.Stub.asInterface(b); 
            mHandler = new Handler(looper) { 
                @Override 
                public void handleMessage(Message msg) { 
                    switch (msg.what) { 
                        case MSG_CLEAR_WALLPAPER: 
                            synchronized (this) { 
                                mWallpaper = null;//用戶自定義壁紙 
                                mDefaultWallpaper = null;//系統默認壁紙 
                            } 
                            break; 
                    } 
                } 
            }; 
        } 
        
        public void onWallpaperChanged() { 
            /* The wallpaper has changed but we shouldn't eagerly load the 
             * wallpaper as that would be inefficient. Reset the cached wallpaper 
             * to null so if the user requests the wallpaper again then we'll 
             * fetch it. 
             */ 
            mHandler.sendEmptyMessage(MSG_CLEAR_WALLPAPER); 
        } 

//IW.updateWallpaper() 

  void updateWallpaper() { 
            synchronized (mLock) { 
                try { 
                    mBackground = mWallpaperManager.getFastDrawable();//WM去獲取壁紙 
                } catch (RuntimeException e) { 
                    Log.w("ImageWallpaper", "Unable to load wallpaper!", e); 
                } 
            } 
        } 
//收到壁紙變換廣播 
     class WallpaperObserver extends BroadcastReceiver { 
            public void onReceive(Context context, Intent intent) { 
                updateWallpaper();//調用 
                drawFrame(); 
                // Assume we are the only one using the wallpaper in this 
                // process, and force a GC now to release the old wallpaper. 
                System.gc(); 
            } 
        } 

        @Override 
        public void onCreate(SurfaceHolder surfaceHolder) { 
            super.onCreate(surfaceHolder); 
            IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);// 
            mReceiver = new WallpaperObserver(); 
            registerReceiver(mReceiver, filter);//註冊 
            updateWallpaper(); 
            surfaceHolder.setSizeFromLayout(); 
        } 

4>mWallpaperManager.getFastDrawable();//WM去獲取壁紙 
a.  public Drawable getFastDrawable() { 
        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true);//獲取壁紙總方法 
        if (bm != null) { 
            Drawable dr = new FastBitmapDrawable(bm); 
            return dr; 
        } 
        return null; 
    } 

b. public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault) { 
            synchronized (this) { 
                if (mWallpaper != null) { 
                    return mWallpaper; 
                } 
                if (mDefaultWallpaper != null) { 
                    return mDefaultWallpaper; 
                } 
                mWallpaper = null; 
                try { 
                    mWallpaper = getCurrentWallpaperLocked(context);//調用獲取用戶自定義壁紙方法 
                } catch (OutOfMemoryError e) { 
                    Log.w(TAG, "No memory load current wallpaper", e); 
                } 
                if (mWallpaper == null && returnDefault) { 
                    mDefaultWallpaper = getDefaultWallpaperLocked(context);調用默認壁紙方法 
                    return mDefaultWallpaper; 
                } 
                return mWallpaper; 
            } 
        } 

c. 兩方法分析 
private Bitmap getCurrentWallpaperLocked(Context context) { 
            try { 
                Bundle params = new Bundle(); 
                ParcelFileDescriptor fd = mService.getWallpaper(this, params);//WMS.getWallpaper(this, params),params是out型表示參數傳送是從WMS傳到WM,是與平時java編碼不合適習慣的這android一特性也是aidl機制的一部分;這裏留個問題就WMS是如何獲取到的params參數呢? 
                if (fd != null) { 
                    int width = params.getInt("width", 0); 
                    int height = params.getInt("height", 0); 
                    
                    if (width <= 0 || height <= 0) { 
                        // Degenerate case: no size requested, just load 
                        // bitmap as-is. 
                        Bitmap bm = null; 
                        try { 
                            bm = BitmapFactory.decodeFileDescriptor( 
                                   fd.getFileDescriptor(), null, null); 
                        } catch (OutOfMemoryError e) { 
                            Log.w(TAG, "Can't decode file", e); 
                        } 
                        try { 
                            fd.close(); 
                        } catch (IOException e) { 
                        } 
                        if (bm != null) { 
                            bm.setDensity(DisplayMetrics.DENSITY_DEVICE); 
                        } 
                        return bm; 
                    } 
                    
                    // Load the bitmap with full color depth, to preserve 
                    // quality for later processing. 
                    BitmapFactory.Options options = new BitmapFactory.Options(); 
                    options.inDither = false; 
                    options.inPreferredConfig = Bitmap.Config.ARGB_8888; 
                    Bitmap bm = BitmapFactory.decodeFileDescriptor( 
                            fd.getFileDescriptor(), null, options); 
                    try { 
                        fd.close(); 
                    } catch (IOException e) { 
                    } 
                    
                    return generateBitmap(context, bm, width, height); 
                } 
            } catch (RemoteException e) { 
            } 
            return null; 
        } 
        
        private Bitmap getDefaultWallpaperLocked(Context context) { 
            try { 
                InputStream is = context.getResources().openRawResource( 
                        com.android.internal.R.drawable.default_wallpaper); 
                if (is != null) { 
                    int width = mService.getWidthHint(); 
                    int height = mService.getHeightHint(); 
                    
                    if (width <= 0 || height <= 0) { 
                        // Degenerate case: no size requested, just load 
                        // bitmap as-is. 
                        Bitmap bm = null; 
                        try { 
                            bm = BitmapFactory.decodeStream(is, null, null); 
                        } catch (OutOfMemoryError e) { 
                            Log.w(TAG, "Can't decode stream", e); 
                        } 
                        try { 
                            is.close(); 
                        } catch (IOException e) { 
                        } 
                        if (bm != null) { 
                            bm.setDensity(DisplayMetrics.DENSITY_DEVICE); 
                        } 
                        return bm; 
                    } 
5>WMS.getWallpaper(this, params) 
    public ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb, 
            Bundle outParams) { 
        synchronized (mLock) { 
            try { 
                if (outParams != null) { 
                    outParams.putInt("width", mWidth); 
                    outParams.putInt("height", mHeight); 
                } 
                mCallbacks.register(cb); 
                File f = WALLPAPER_FILE; 
                if (!f.exists()) { 
                    return null; 
                } 
                return ParcelFileDescriptor.open(f, MODE_READ_ONLY);//ParcelFileDescriptor是google自定義的句柄具有安全性,對它所屬的流具體保護性,否會圖像丟失出現花屏情況.我對它瞭解也不深,不遇到類似的bug. 
            } catch (FileNotFoundException e) { 
                /* Shouldn't happen as we check to see if the file exists */ 
                Slog.w(TAG, "Error getting wallpaper", e); 
            } 
            return null; 
        } 
    } 

6>呵呵呵,該畫了IW.draw(),考慮有很多東西要跟大家分享下就把IW類粘貼出來 
package com.android.internal.service.wallpaper; 

import com.android.internal.view.WindowManagerPolicyThread; 

import android.app.WallpaperManager; 
import android.graphics.Canvas; 
import android.graphics.Rect; 
import android.graphics.Region.Op; 
import android.graphics.drawable.Drawable; 
import android.os.HandlerThread; 
import android.os.Looper; 
import android.os.Process; 
import android.service.wallpaper.WallpaperService; 
import android.util.Log; 
import android.view.MotionEvent; 
import android.view.SurfaceHolder; 
import android.content.Context; 
import android.content.IntentFilter; 
import android.content.Intent; 
import android.content.BroadcastReceiver; 

/** 
* Default built-in wallpaper that simply shows a static image. 
*/ 
public class ImageWallpaper extends WallpaperService { 
    WallpaperManager mWallpaperManager; 
    private HandlerThread mThread; 

    @Override 
    public void onCreate() { 
        super.onCreate(); 
        mWallpaperManager = (WallpaperManager) getSystemService(WALLPAPER_SERVICE); 
        Looper looper = WindowManagerPolicyThread.getLooper(); 
        if (looper != null) { 
            setCallbackLooper(looper); 
        } else { 
            mThread = new HandlerThread("Wallpaper", Process.THREAD_PRIORITY_FOREGROUND); 
            mThread.start(); 
            setCallbackLooper(mThread.getLooper()); 
        } 
    } 

    public Engine onCreateEngine() { 
        return new DrawableEngine(); 
    } 

    @Override 
    public void onDestroy() { 
        super.onDestroy(); 
        if (mThread != null) { 
            mThread.quit(); 
        } 
    } 

    class DrawableEngine extends Engine { 
        private final Object mLock = new Object(); 
        private WallpaperObserver mReceiver; 
        Drawable mBackground; 
        float mXOffset; 
        float mYOffset; 

        class WallpaperObserver extends BroadcastReceiver { 
            public void onReceive(Context context, Intent intent) { 
                updateWallpaper(); 
                drawFrame(); 
                // Assume we are the only one using the wallpaper in this 
                // process, and force a GC now to release the old wallpaper. 
                System.gc(); 
            } 
        } 

        @Override 
        public void onCreate(SurfaceHolder surfaceHolder) { 
            super.onCreate(surfaceHolder); 
            IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED); 
            mReceiver = new WallpaperObserver(); 
            registerReceiver(mReceiver, filter); 
            updateWallpaper(); 
            surfaceHolder.setSizeFromLayout(); 
        } 

        @Override 
        public void onDestroy() { 
            super.onDestroy(); 
            unregisterReceiver(mReceiver); 
        } 

        @Override 
        public void onVisibilityChanged(boolean visible) {//亮屏時會執行 
            drawFrame(); 
        } 
        
        @Override 
        public void onTouchEvent(MotionEvent event) { 
            super.onTouchEvent(event); 
        } 

        @Override 
        public void onOffsetsChanged(float xOffset, float yOffset, 
                float xOffsetStep, float yOffsetStep, 
                int xPixels, int yPixels) {//滑動壁紙時會執行 
            mXOffset = xOffset; 
            mYOffset = yOffset; 
            drawFrame(); 
        } 

        @Override 
        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {//開機和轉屏時會執行 
            super.onSurfaceChanged(holder, format, width, height); 
            drawFrame(); 
        } 

        @Override 
        public void onSurfaceCreated(SurfaceHolder holder) { 
            super.onSurfaceCreated(holder); 
        } 

        @Override 
        public void onSurfaceDestroyed(SurfaceHolder holder) { 
            super.onSurfaceDestroyed(holder); 
        } 
        
        void drawFrame() { 
            SurfaceHolder sh = getSurfaceHolder(); 
            Canvas c = sh.lockCanvas();//鎖住canvas 
            if (c != null) { 
                final Rect frame = sh.getSurfaceFrame(); 
                synchronized (mLock) { 
                    final Drawable background = mBackground; 
                    final int dw = frame.width(); 
                    final int dh = frame.height(); 
                    final int bw = background != null ? background.getIntrinsicWidth() : 0; 
                    final int bh = background != null ? background.getIntrinsicHeight() : 0; 
                    final int availw = dw-bw; 
                    final int availh = dh-bh; 
                    int xPixels = availw < 0 ? (int)(availw*mXOffset+.5f) : (availw/2); 
                    int yPixels = availh < 0 ? (int)(availh*mYOffset+.5f) : (availh/2); 

                    c.translate(xPixels, yPixels);//滑動後計算到壁紙畫起點位置 
                    if (availw<0 || availh<0) { 
                        c.save(Canvas.CLIP_SAVE_FLAG); 
                        c.clipRect(0, 0, bw, bh, Op.DIFFERENCE); 
                        c.drawColor(0xff000000);//出現壁紙尺寸異常或是轉屏延遲就會畫黑 
                        c.restore(); 
                    } 
                    if (background != null) { 
                        background.draw(c); 
                    } 
                } 
                sh.unlockCanvasAndPost(c);//解鎖canvas並提交 
            } 
        } 

        void updateWallpaper() { 
            synchronized (mLock) { 
                try { 
                    mBackground = mWallpaperManager.getFastDrawable(); 
                } catch (RuntimeException e) { 
                    Log.w("ImageWallpaper", "Unable to load wallpaper!", e); 
                } 
            } 
        } 
    } 


3.壁紙長寬初始化值及轉屏處理 
1>LC.setWallpaperDimension() 
private void setWallpaperDimension() { 
        WallpaperManager wpm = (WallpaperManager)getSystemService(WALLPAPER_SERVICE); 

        Display display = getWindowManager().getDefaultDisplay(); 
        boolean isPortrait = display.getWidth() < display.getHeight(); 

        final int width = isPortrait ? display.getWidth() : display.getHeight(); 
        final int height = isPortrait ? display.getHeight() : display.getWidth(); 
        wpm.suggestDesiredDimensions(width * WALLPAPER_SCREENS_SPAN, height);//WM.setsuggestDesiredDimensions(width,height) 設置長寬 
    } 
2> 
   public void suggestDesiredDimensions(int minimumWidth, int minimumHeight) { 
        try { 
            sGlobals.mService.setDimensionHints(minimumWidth, minimumHeight);//WMS.setsetDimensionHints(..) 
        } catch (RemoteException e) { 
        } 
    } 
3> 
    public void setDimensionHints(int width, int height) throws RemoteException { 
        checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS); 

        if (width <= 0 || height <= 0) { 
            throw new IllegalArgumentException("width and height must be > 0"); 
        } 

        synchronized (mLock) { 
            if (width != mWidth || height != mHeight) { 
                mWidth = width; 
                mHeight = height; 
                saveSettingsLocked();//將值以xml形式存儲,開機時候會調用loadSettingsLocked()讀取 
                if (mWallpaperConnection != null) { 
                    if (mWallpaperConnection.mEngine != null) { 
                        try { 
                            mWallpaperConnection.mEngine.setDesiredSize( 
                                    width, height); 
                        } catch (RemoteException e) { 
                        } 
                        notifyCallbacksLocked();//通知壁紙有變化(包括換壁紙與橫豎轉換). 
                    } 
                } 
            } 
        } 
    } 

4>android的壁紙機制用得IPC機制,aidl機制,廣播機制,這裏我們就不再介紹.不太清楚google吧.另外我將aidl內容貼出來,希望對大家的理解有幫助. 
/** @hide */ 
1>IWallpaperManager.aidl 
interface IWallpaperManager { 

    /** 
     * Set the wallpaper. 
     */ 
    ParcelFileDescriptor setWallpaper(String name); 
    
    /** 
     * Set the live wallpaper. 
     */ 
    void setWallpaperComponent(in ComponentName name); 
    
    /** 
     * Get the wallpaper. 
     */ 
    ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb, 
            out Bundle outParams); 
    
    /** 
     * Get information about a live wallpaper. 
     */ 
    WallpaperInfo getWallpaperInfo(); 
    
    /** 
     * Clear the wallpaper. 
     */ 
    void clearWallpaper(); 

    /** 
     * Sets the dimension hint for the wallpaper. These hints indicate the desired 
     * minimum width and height for the wallpaper. 
     */ 
    void setDimensionHints(in int width, in int height); 

    /** 
     * Returns the desired minimum width for the wallpaper. 
     */ 
    int getWidthHint(); 

    /** 
     * Returns the desired minimum height for the wallpaper. 
     */ 
    int getHeightHint(); 


2>IWallpaperManagerCallback.aidl 
oneway interface IWallpaperManagerCallback { 
    /** 
     * Called when the wallpaper has changed 
     */ 
    void onWallpaperChanged(); 

轉載自:http://ydyyes.iteye.com/blog/978745

發佈了32 篇原創文章 · 獲贊 1 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章