您的位置:首页 > 移动开发 > Android开发

android之壁纸机制

2015-09-11 22:13 549 查看


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)设置壁纸初始化值,带到壁纸机制的转屏.

流程一: 壁纸设置的流程:

会用到WallpaperManager.java 里面的如下函数

    public void setResource(int resid) throws IOException {
        try {
            Resources resources = mContext.getResources();
            /* Set the wallpaper to the default values */
            ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(
                    "res:" + resources.getResourceName(resid));
            if (fd != null) {
                FileOutputStream fos = null;
                try {
                    fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
                    setWallpaper(resources.openRawResource(resid), fos);
                } finally {
                    if (fos != null) {
                        fos.close();
                    }
                }
            }
        } catch (RemoteException e) {
            // Ignore
        }
    }

流程二: 然后WallpaperManagerService 中setWallpaper()函数,

    private void setWallpaper(InputStream data, FileOutputStream fos)

            throws IOException {
        byte[] buffer = new byte[32768];
        int amt;
        while ((amt=data.read(buffer)) > 0) {
            fos.write(buffer, 0, amt);
        }
    }

设置成功会写入到壁纸相应文件里,WallpaperManagerService 中文件监听类此时会触发

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();

                        }

                    }

                }

            };

   该函数的具体实现如下:

   private void notifyCallbacksLocked() {

        final int n = mCallbacks.beginBroadcast();

        for (int i = 0; i < n; i++) {

            try {

                //回调机制在WallpaperManager.java 实现onWallpaperChanged()

                mCallbacks.getBroadcastItem(i).onWallpaperChanged();

            } catch (RemoteException e) {

                // The RemoteCallbackList will take care of removing

                // the dead object for us.

            }

        }

        mCallbacks.finishBroadcast();

       //壁纸变化的广播,ImageWallpaper.java 回接受该广播,调用updateWallpaper()去获取新壁纸.

        final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);

        mContext.sendBroadcast(intent);

    }

3 回到WallpaperManager.java 中onWallpaperChanged(),

        public void onWallpaperChanged() {

            mHandler.sendEmptyMessage(MSG_CLEAR_WALLPAPER);

        }

上面代码可以知道通过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;

                    }

                }

            };

        }

       

    ImageWallpaper.java 中updateWallpaper() 函数实现如下

  void updateWallpaper() {

            synchronized (mLock) {

                try {

                     //WallpaperManager去获取壁纸

                    mBackground = mWallpaperManager.getFastDrawable();

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

}

9474
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: