`
ydyyes
  • 浏览: 25341 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

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




分享到:
评论

相关推荐

    Android 动态壁纸源码.zip

    对于初学者来说,这是一次了解Android服务和动画机制的好机会;对于有经验的开发者,这则可以作为一个起点,启发更多创新的动态壁纸设计。记得在研究源码时,结合Android官方文档和其他教程,以加深理解并提升你的...

    android动态壁纸

    在Android操作系统中,动态壁纸(Dynamic Wallpapers)是一种超越传统静态图像的个性化设置方式,它为用户提供了丰富的视觉体验和互动功能。动态壁纸允许背景随着用户的操作或时间变化而变化,创造出更为生动的桌面...

    2011.09.21——— android 动态壁纸开发

    开发动态壁纸涉及到OpenGL ES、动画框架、多线程以及Android系统服务等多个方面,深入学习可以提升对Android系统底层机制的理解。推荐阅读Android官方文档,以及相关开源项目的源码,如Android开放源代码项目(AOSP...

    android mtk锁屏壁纸功能

    通过对这套代码的学习和研究,开发者不仅可以实现个性化的锁屏壁纸功能,还能深入理解Android系统的壁纸服务机制,提高自己的Android开发技能。对于希望在MTK平台上开发特色锁屏壁纸的应用开发者来说,这是一个非常...

    Android源码——动态壁纸源码.7z

    1. **Android壁纸服务**:Android系统提供了壁纸服务框架,允许开发者创建自定义壁纸应用。动态壁纸服务继承自`WallpaperService`类,通过重写其提供的方法来实现动画效果和用户交互。 2. **SurfaceView**:动态...

    Android壁纸源码Galaxy4(android-5.1.1_r1)

    "Android壁纸源码Galaxy4(android-5.1.1_r1)"是一个专门研究Android 5.1.1版本中Galaxy4壁纸应用的源代码库,它揭示了Android壁纸应用开发的内部机制,对于想要深入了解Android系统或者进行壁纸应用开发的开发者来...

    Android动态壁纸源码

    1. **Android壁纸服务**:Android系统提供了壁纸服务框架,开发者可以通过实现`WallpaperService`类来创建自己的动态壁纸。这个服务会在用户切换到桌面时启动,并在后台运行,处理壁纸的动画和交互。 2. ** ...

    Android-一个设置壁纸的简单程序

    综上所述,开发一个简单的Android壁纸设置程序涉及Android SDK的使用、Activity生命周期管理、Intent机制、壁纸服务的运用、图像处理技术、文件管理和界面设计等多个方面。通过深入理解和实践这些知识点,你可以创建...

    Android动态壁纸

    创建Android动态壁纸需要对Android系统服务、绘图机制以及触摸事件处理有深入理解。通过`WallpaperService`和其子类`Engine`,我们可以实现丰富多样的动态效果。例如,`TimeWall`就是一个很好的实例,它展示了如何...

    Android 动态壁纸源码.rar

    【Android动态壁纸源码解析】 Android动态壁纸是Android操作系统中的一种特色功能,允许用户自定义主屏幕背景,使其具有交互性和动画效果。本资源提供的"Android动态壁纸源码"可以帮助开发者深入了解如何创建自定义...

    Android动态壁纸源码(入门级demo)

    1. **Android壁纸服务(Wallpaper Service)**: 动态壁纸是通过继承`android.service.wallpaper.WallpaperService`类来创建的。你需要重写`Engine`类,并在其中实现动态效果的逻辑。`Engine`类是壁纸服务的核心,...

    安卓锁屏九宫格锁屏解锁壁纸相关-拯救电源键最详尽的android开发一键锁屏实例源码.rar

    在Android平台上,开发者可以利用系统提供的API来实现各种定制化的功能,其中之一就是创建自定义的锁屏界面。这个"安卓锁屏九宫格锁屏解锁壁纸相关-拯救电源键最详尽的android开发一键锁屏实例源码"的资源包,提供了...

    android 壁纸自动切换

    这个项目对于初学者来说是一个很好的实践,因为可以深入理解Android的消息处理机制——`Handler`,以及如何在Android应用中实现定时任务。 `Handler`是Android中用于线程间通信的关键组件,特别是在主线程和工作...

    Android2.2动态壁纸源码

    - **性能优化**:利用硬件加速和缓存机制可以提高动态壁纸的渲染效率。 - **适配不同分辨率**:为不同屏幕尺寸和分辨率设计适应性布局和绘制策略。 - **主题定制**:提供配置选项让用户可以自定义颜色和样式等。 ...

    安卓锁屏九宫格锁屏解锁壁纸相关-android一键锁屏源代码android4.2工作正常.zip

    【标题】"安卓锁屏九宫格锁屏解锁壁纸相关-android一键锁屏源代码android4.2工作正常.zip" 涉及的核心知识点是Android系统的锁屏机制,特别是九宫格锁屏和壁纸的实现。九宫格锁屏是Android系统中一种常见的用户安全...

Global site tag (gtag.js) - Google Analytics