静态壁纸和动态壁纸看起来差别很大,但其实两者的性质是差不多的,都是以service的形式运行在后台,在类型为TYPE_WALLPAPER窗口上绘制内容,静态壁纸就是一种特殊的动态壁纸
壁纸的绘制由WallpaperService来控制,继承和实现WallpaperEngine是壁纸相关开发的第一步,Engine是WallpaperService里面的一个内部类,还提供了一系列可以让子类重写的回调。
WallpaperManagerService用来管理壁纸的运行和切换,通过WallpaperManager向外提供用户操作壁纸的接口,切换壁纸的时候会取消当前壁纸的WallpaperService绑定,重新启动新的WallpaperService
动态壁纸服务的启动流程
启动动态壁纸可以通过调用WallpaperManagerService.setWallpaperComponent()方法来完成,这里是首先在WallpaperManager里面定义的这个方法
/frameworks/base/core/java/android/app/WallpaperManager.java
/**
* Set the live wallpaper.
*
* This can only be called by packages with android.permission.SET_WALLPAPER_COMPONENT
* permission. The caller must hold the INTERACT_ACROSS_USERS_FULL permission to change
* another user's wallpaper.
*
* @hide
*/
@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 {
sGlobals.mService.setWallpaperComponentChecked(name, mContext.getOpPackageName(),
userId);
return true;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
在方法中调用了sGlobals.mService.setWallpaperComponentChecked,这里的是调用了
private static class Globals extends IWallpaperManagerCallback.Stub {
private final IWallpaperManager mService;
mService(IWallpaperManager)
@Override public void setWallpaperComponentChecked(android.content.ComponentName name, java.lang.String callingPackage, int userId) throws android.os.RemoteException
{}
/**
* Set the live wallpaper. This only affects the system wallpaper.
*/
@Override public void setWallpaperComponent(android.content.ComponentName name) throws android.os.RemoteException
{
这里运用了aidl的知识。 WallpaperManagerService 继承了 IWallpaperManager.Stub
frameworks/base/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
private void setWallpaperComponent(ComponentName name, int userId) {
//...省略代码
synchronized (mLock) {
if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name);
wallpaper = mWallpaperMap.get(userId);
//...省略代码
try {
wallpaper.imageWallpaperPending = false;
boolean same = changingToSame(name, wallpaper);
if (bindWallpaperComponentLocked(name, false, true, wallpaper, null)) {
if (!same) {
wallpaper.primaryColors = null;
//...省略代码
}
在里面有几处关键地方:
-
mWallpaperMap.get(userId)通过userId来获取壁纸的信息
因为WallpaperManagerService支持多用户机制,每个用户都可以设置自己的壁纸,mWallpaperMap为每个用户保存了一个WallpaperData实例,里面包含壁纸的状态和相关运行信息
-
bindWallpaperComponentLocked来绑定服务,启动新壁纸的WallpaperService
具体的过程包括:
- 认证服务,会进行一系列的认证检查,确认是壁纸服务后,才会启动WallpaperService
-
服务必须是以android.permission.BIND_WALLPAPER作为访问权限,该权限必须通过WallpaperService服务来请求,只有系统才能用,防止被其他应用意外启动
if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission))
-
服务必须声明可以处理
android.service.wallpaper.WallpaperService
这个action,因为WallpaperManagerService会使用这个action进行绑定
public static final String SERVICE_INTERFACE = "android.service.wallpaper.WallpaperService"; Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
- 服务必须在AndroidManifest.xml中提供android.service.wallpaper的meta-data
-
-
创建WallpaperConnection,其实现了ServiceConnection接口用于监听和WallpaperService之间的连接状态,同时还实现了IWallpaperConnection.stub
WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper, componentUid);
class WallpaperConnection extends IWallpaperConnection.Stub implements ServiceConnection {
-
调用mContext.bindServiceAsUser启动指定的服务。之后的流程会在WallpaperConnection.onServiceConnected回调中完成。
-
新的壁纸服务启动之后,销毁旧的壁纸服务
-
将新的壁纸服务的信息保存到WallpaperData中
wallpaper.wallpaperComponent = componentName; wallpaper.connection = newConn; //wallpaper就是WallpaperData
- 认证服务,会进行一系列的认证检查,确认是壁纸服务后,才会启动WallpaperService
-
传递创建窗口所需信息
仅仅将指定的壁纸服务启动起来还不能让壁纸显示出来,因为还没有窗口令牌而无法添加窗口。所以这后半部流程会在WallpaperConnection的onServiceConnected方法回调中进行。
在WallpaperService的onBind方法中会返回一个IWallpaperServiceWrapper实例。这个类继承了IWallpaperService.stub。保存了Wallpaper的实例,同时也实现了唯一的一个接口attach()。
/** * Implement to return the implementation of the internal accessibility * service interface. Subclasses should not override. */ @Override public final IBinder onBind(Intent intent) { return new IWallpaperServiceWrapper(this); }
WallpaperService.attach方法中的各参数意义是:
@Override public void attach(IWallpaperConnection conn, IBinder windowToken, int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding, int displayId) { mEngineWrapper = new IWallpaperEngineWrapper(mTarget, conn, windowToken, windowType, isPreview, reqWidth, reqHeight, padding, displayId); } //conn:WallpaperConnection。继承自IWallpaperConnection,只提供了两个接口定义:attachEngine和engineShown。 //conn.Token:向WindowManagerService申请的令牌 //WindowManager.LayoutParams.TYPE_WALLPAPER:指明需要添加TYPE_WALLPAPER类型的窗口。另一种情况是壁纸预览的时候,会使用TYPE_APPLICATION_MEDIA_OVERLAY类型创建窗口,此时壁纸服务创建的窗口将会以子窗口的形式衬在LivePicker的窗口之下。 //isPreview:实际作为壁纸的时候是false,壁纸预览的时候是true
WallpaperManagerService会在WallpaperConnection.onServiceConnected方法中收到回调,然后会进行以下三步:
@Override public void onServiceConnected(ComponentName name, IBinder service) { synchronized (mLock) { if (mWallpaper.connection == this) { mService = IWallpaperService.Stub.asInterface(service); attachServiceLocked(this, mWallpaper); // XXX should probably do saveSettingsLocked() later // when we have an engine, but I'm not sure about // locking there and anyway we always need to be able to // recover if there is something wrong. if (!mWallpaper.equals(mFallbackWallpaper)) { saveSettingsLocked(mWallpaper.userId); } FgThread.getHandler().removeCallbacks(mResetRunnable); } } }
-
将WallpaperService传回的IWallpaperService接口保存为mService
-
绑定壁纸服务,调用attachServiceLocked方法,这个方法会调用IWallpaperService.attach方法来传递壁纸服务创建窗口的信息
-
saveSettingLocked,保存壁纸运行状态到文件系统中
-
-
创建Engine
调用IWallpaperService.attach是WallpaperManagerService与WallpaperService的第一次接触。该方法会创建IWallpaperEngineWrapper,其继承自IWallpaperEngine.stub,支持跨进程调用。在其中会创建并封装Engine的实例。Engine创建完成之后会通过Engine.attach来初始化Engine,步骤如下:
- 设置必要的信息,包括:
- mSurfaceHolder:BaseSurfaceHolder类型的内部类实例,可以通过它来定制需要的Surface类型
- 获取WindowSession,用于与WMS通信
- mWindow.setSession(mSession):用于接受WMS的回调
- 设置监听屏幕状态,保证屏幕关闭之后停止动画渲染节省电量
- 调用Engine.onCreate,重写的子类一般需要重写该方法来修改mSurfaceHolder的属性。此时还没有创建窗口,修改的属性会在窗口创建时生效。
- updateSurface:根据SurfaceHolder的属性创建窗口以及Surface,并进行壁纸的第一次绘制
- 设置必要的信息,包括: