在Android中,壁纸分为静态与动态两种。静态壁纸是一张图片,而动态壁纸则以动画为表现形式,或者可以对用户的操作作出反应。这两种形式看似差异很大,其实二者的本质是统一的。它们都以一个Service的形式运行在系统后台,并在一个类型为TYPE_WALLPAPER的窗口上绘制内容。进一步讲,静态壁纸是一种特殊的动态壁纸,它仅在窗口上渲染一张图片,并且不会对用户的操作作出反应。
一、 初识Android壁纸
在Android中,壁纸分为静态与动态两种。静态壁纸是一张图片,而动态壁纸则以动画为表现形式,或者可以对用户的操作作出反应。这两种形式看似差异很大,其实二者的本质是统一的。它们都以一个Service的形式运行在系统后台,并在一个类型为TYPE_WALLPAPER的窗口上绘制内容。进一步讲,静态壁纸是一种特殊的动态壁纸,它仅在窗口上渲染一张图片,并且不会对用户的操作作出反应。因此本章将首先通过动态壁纸的实现讨论Android壁纸的实现与管理原理,然后在对静态壁纸的实现做介绍。
Android壁纸的实现与管理分为三个层次:
1、**WallpaperService和Engine(壁纸的实现原理):**同SystemUI一样,壁纸运行在一个Android服务之中,这个服务的名字叫做WallpaperService。当用户选择了一个壁纸之后,此壁纸所对应的WallpaperService便会启动并开始进行壁纸的绘制工作,因此继承并定制WallpaperService是开发者进行壁纸开发的第一步。Engine是WallpaperService中的一个内部类,实现了壁纸窗口的创建以及Surface的维护工作。另外,Engine提供了可供子类重写的一系列回调,用于通知壁纸开发者关于壁纸的生命周期、Surface状态的变化以及对用户的输入事件进行响应。可以说,Engine类是壁纸实现的核心所在。壁纸开发者需要继承Engine类,并重写其提供的回调以完成壁纸的开发。这一层次的内容主要体现了壁纸的实现原理。
2、**WallpaperManagerService(对壁纸的管理方式):**这个系统服务用于管理壁纸的运行与切换,并通过WallpaperManager类向外界提供操作壁纸的接口。当通过WallpaperManagaer的接口进行壁纸的切换时,WallpaperManagerService会取消当前壁纸的WallpaperService的绑定,并启动新壁纸的WallpaperService。另外,Engine类进行窗口创建时所使用的窗口令牌也是由WallpaperManagerService提供的。这一层次主要体现了Android对壁纸的管理方式。
3、**WindowManagerService(对壁纸窗口的管理):**用于计算壁纸窗口的Z序、可见性以及为壁纸应用窗口动画。壁纸窗口(TYPE_WALLPAPER)的Z序计算不同于其他类型的窗口。其他窗口依照其类型会有固定的mBaseLayer以及mSubLayer,并结合它们所属的Activity的顺序或创建顺序进行Z序的计算,因此这些窗口的Z序相对固定。而壁纸窗口则不然,它的Z序会根据FLAG_SHOW_WALLPAPER标记在其它窗口的LayoutParams.flags中的存在情况而不断地被调整。这一层次主要体现了Android对壁纸窗口的管理方式。
二、动态壁纸的设置流程
1、通过调用WallpaperManager的setWallpaperComponent方法可以帮我们开启自定义动态壁纸。
frameworks/base/core/java/android/app/WallpaperManager.java
public class WallpaperManager {
@SystemApi
@RequiresPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT)
public boolean setWallpaperComponent(ComponentName name) {
return setWallpaperComponent(name, mContext.getUserId());
}
@RequiresPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT)
@UnsupportedAppUsage
public boolean setWallpaperComponent(ComponentName name, int userId) {
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
throw new RuntimeException(new DeadSystemException());
}
try {
//通过binder调用WallpaperManagerService的setWallpaperComponentChecked方法
sGlobals.mService.setWallpaperComponentChecked(name, mContext.getOpPackageName(),
userId);
return true;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
private static class Globals extends IWallpaperManagerCallback.Stub {
private final IWallpaperManager mService;//WallpaperManagerService的binder代理对象
}
}
类型为Globals的sGlobals对象,内部属性mService是一个代理者,setWallpaperComponent最终会调用WallpaperManagerService的setWallpaperComponentChecked方法。
2、WallpaperManagerService的setWallpaperComponentChecked方法如下所示。
frameworks/base/core/java/android/app/WallpaperManager.java
public class WallpaperManagerService extends IWallpaperManager.Stub
implements IWallpaperManagerService {
@Override
public void setWallpaperComponentChecked(ComponentName name, String callingPackage,
int userId) {
//权限验证
if (isWallpaperSupported(callingPackage) && isSetWallpaperAllowed(callingPackage)) {
setWallpaperComponent(name, userId);
}
}
//设置动态壁纸
private void setWallpaperComponent(ComponentName name, int userId) {
userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
false /* all */, true /* full */, "changing live wallpaper", null /* pkg */);
//权限检测
checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
int which = FLAG_SYSTEM;
boolean shouldNotifyColors = false;
WallpaperData wallpaper;//壁纸数据
synchronized (mLock) {
Slog.v(TAG, "setWallpaperComponent name=" + name);
wallpaper = mWallpaperMap.get(userId);
if (wallpaper == null) {
throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
}
final long ident = Binder.clearCallingIdentity();
// Live wallpapers can't be specified for keyguard. If we're using a static
// system+lock image currently, migrate the system wallpaper to be a lock-only
// image as part of making a different live component active as the system
// wallpaper.
if (mImageWallpaper.equals(wallpaper.wallpaperComponent)) {
if (mLockWallpaperMap.get(userId) == null) {
// We're using the static imagery and there is no lock-specific image in place,
// therefore it's a shared system+lock image that we need to migrate.
Slog.i(TAG, "Migrating current wallpaper to be lock-only before"
+ "updating system wallpaper");
migrateSystemToLockWallpaperLocked(userId);
}
}
// New live wallpaper is also a lock wallpaper if nothing is set
if (mLockWallpaperMap.get(userId) == null) {
which |= FLAG_LOCK;
}
try {
wallpaper.imageWallpaperPending = false;
boolean same = changingToSame(name, wallpaper);
//启动新壁纸的WallpaperService
if (bindWallpaperComponentLocked(name, false, true, wallpaper, null)) {
if (!same) {
wallpaper.primaryColors = null;
} else {
if (wallpaper.connection != null) {
wallpaper.connection.forEachDisplayConnector(displayConnector -> {
try {
if (displayConnector.mEngine != null) {
displayConnector.mEngine.dispatchWallpaperCommand(
COMMAND_REAPPLY, 0, 0, 0, null);
}
} catch (RemoteException e) {
Slog.w(TAG, "Error sending apply message to wallpaper", e);
}
});
}
}
wallpaper.wallpaperId = makeWallpaperIdLocked();
notifyCallbacksLocked(wallpaper);
shouldNotifyColors = true;
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
if (shouldNotifyColors) {
notifyWallpaperColorsChanged(wallpaper, which);
notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
}
}
}
参考文章:https://www.kancloud.cn/wizardforcel/deepin-android-vol3/122360
https://www.freesion.com/article/6656482862/