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

}