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