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

更多相关文章

  1. android --相机使用详解概述
  2. Android开发之InstanceState详解
  3. android对话框大全
  4. android中的dispatchTouchEvent、onInterceptTouchEvent和onTouc
  5. Android(安卓)ormlite 框架介绍
  6. android LinearLayout 动态添加子View
  7. Android(安卓)编程下 Touch 事件的分发和消费机制
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. Android(安卓)软键盘弹出,界面整体上移
  2. Android(安卓)数据存储与IO (一)
  3. Android(安卓)API 中文 (53) ―― BaseAdap
  4. 最强 Android(安卓)Studio 使用小技巧和
  5. 【流媒體】 Android(安卓)实时视频编码—
  6. android:targetSdkVersion问题。
  7. Coroutines in Android(安卓)- One Shot
  8. Netty多语言(Java、Android(安卓)、C#、We
  9. android MultiDex multiDex原理(一)
  10. 浅谈Android系统启动过程