Android WallpaperManager 壁纸分析

本文深入解析Android壁纸服务的实现原理,包括静态壁纸和动态壁纸的实现机制,以及WallpaperService和WallpaperManager的重要作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android中的壁纸分为静态壁纸和动态壁纸, 这两类壁纸本质都是一样的, 都是通过继承WallpaperService来实现的,只不过是绘制方面的差异。WallpaperManagerService用于管理壁纸的运行与切换,并通过WallpaperManager类向外界提供操作壁纸的接口,主要体现了对壁纸的管理方式。WallpaperService则对应壁纸的具体实现,实现壁纸服务相关的核心是WallpaperService中的Engine类

1 简单壁纸案列
1.1 新建壁纸服务
public class MyWallPaperService extends WallpaperService {

    private static final String TAG = "MyWallPaperService";

    @Override
    public Engine onCreateEngine() {
        return new MyEngine();
    }

    class MyEngine extends Engine {
        @Override
        public void onSurfaceCreated(SurfaceHolder holder) {
            super.onSurfaceCreated(holder);
            Canvas canvas = holder.lockCanvas();
            canvas.drawColor(Color.GREEN);
            holder.unlockCanvasAndPost(canvas);
        }
    }
}
1.2 配置文件中配置相关
<!-- AndroidManifest.xml中service配置   -->
<service android:name=".MyWallPaperService"
	android:enabled="true"
	android:permission="android.permission.BIND_WALLPAPER">
	<intent-filter >
		<action android:name="android.service.wallpaper.WallpaperService"/>
	</intent-filter>
	<meta-data
		android:name="android.service.wallpaper"
		android:resource="@xml/wallpaper_resource"/>
</service>

<!-- wallpaper_resource.xml配置  -->
<?xml version="1.0" encoding="utf-8"?>
<wallpaper xmlns:android="http://schemas.android.com/apk/res/android"
    />
1.3 通过壁纸选择器设置壁纸,最终壁纸显示为绿色背景
void setWallpaper() {
	Intent intent = new Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER);
	ComponentName componentName = new ComponentName(this, MyWallPaperService.class);
	intent.putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT, componentName);
	startActivity(intent);
}
2 WallpaperManager相关介绍
// 主屏幕壁纸flag
public static final int FLAG_SYSTEM = 1 << 0;

// 锁屏壁纸flag
public static final int FLAG_LOCK = 1 << 1;
// 设置动态壁纸
public boolean setWallpaperComponent(ComponentName name)

// 清除锁屏壁纸
public void clear(WallpaperManager.FLAG_LOCK)

// 清除所有壁纸
public void clearWallpaper()

// 获取静态壁纸图片
public Bitmap getBitmap()

// 设置静态壁纸
public void setBitmap(Bitmap bitmap)
2.1 WallpaperService和WallpaperManagerService之间的关系

在这里插入图片描述

2.2 WallpaperManager和WallpaperManagerService之间的关系

在这里插入图片描述

2.3 壁纸涉及到的类介绍
// 壁纸服务信息封装
WallpaperInfo

// 提供接口对壁纸服务的访问
WallpaperManager

// IWallpaperEngine接口服务端对象
// class IWallpaperEngineWrapper extends IWallpaperEngine.Stub
IWallpaperEngineWrapper

// IWallpaperService接口服务端对象
// IWallpaperServiceWrapper extends IWallpaperService.Stub
IWallpaperServiceWrapper

// 用户壁纸相关信息存储
WallpaperData

// 监听WallPaperService之间的连接状态和实现IWallpaperConnection接口服务端实现
// class WallpaperConnection extends IWallpaperConnection.Stub
//             implements ServiceConnection
WallpaperConnection

// 壁纸服务
// public class WallpaperManagerService extends IWallpaperManager.Stub
WallpaperManagerService

// 壁纸核心和壁纸绘制相关
Engine

3 壁纸相关代码分析

WallpaperManager的setWallpaperComponent函数为设置动态壁纸函数,接下来以动态壁纸为入口分析相关代码。Globals实现了IWallpaperManagerCallback接口,sGlobals.mService为WallpaperManagerService的代理对象,sGlobals.mService最终通过binder调用WallpaperManagerService的setWallpaperComponent函数,name为对应壁纸服务的包名。

private static class Globals extends IWallpaperManagerCallback.Stub {
    private final IWallpaperManager mService;
}

public boolean setWallpaperComponent(ComponentName name, int userId) {
	if (sGlobals.mService == null) {
		Log.w(TAG, "WallpaperService not running");
		throw new RuntimeException(new DeadSystemException());
	}
	try {
		// step 1, 调用WallpaperManagerService的setWallpaperComponentChecked函数
		sGlobals.mService.setWallpaperComponentChecked(name, mContext.getOpPackageName(),
				userId);
		return true;
	} catch (RemoteException e) {
		throw e.rethrowFromSystemServer();
	}
}
3.1 WallpaperManagerService::setWallpaperComponent

拥有android.Manifest.permission.SET_WALLPAPER_COMPONENT权限的apk才能设置动态壁纸,壁纸选择器有这个设置权限,首先进行权限的相关检查, 然后获取用户设置的壁纸相关信息, 错误判断等, 接下来bindWallpaperComponentLocked函数才是核心,对WallpaperService的条件判断以及绑定

private void setWallpaperComponent(ComponentName name, int userId) {
	userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
			false /* all */, true /* full */, "changing live wallpaper", null /* pkg */);
	// step 1, 权限检查
	checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);

	int which = FLAG_SYSTEM;
	boolean shouldNotifyColors = false;
	WallpaperData wallpaper;

	synchronized (mLock) {
		if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name);
		// step 2, 获取用户壁纸相关信息
		wallpaper = mWallpaperMap.get(userId);
		if (wallpaper == null) {
			throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
		}
		final long ident = Binder.clearCallingIdentity();

		// step 3 , 如果锁屏壁纸没有添加,则将锁屏壁纸flag添加上
		if (mLockWallpaperMap.get(userId) == null) {
			which |= FLAG_LOCK;
		}

		try {
			wallpaper.imageWallpaperPending = false;
			boolean same = changingToSame(name, wallpaper);
			// step 3, 绑定WallpaperService服务
			if (bindWallpaperComponentLocked(name, false, true, wallpaper, null)) {
				if (!same) {
					wallpaper.primaryColors = null;
				}
				wallpaper.wallpaperId = makeWallpaperIdLocked();
				notifyCallbacksLocked(wallpaper);
				shouldNotifyColors = true;
			}
		} finally {
			Binder.restoreCallingIdentity(ident);
		}
	}

	if (shouldNotifyColors) {
		// step 4, 通知壁纸状态改变
		notifyWallpaperColorsChanged(wallpaper, which);
		notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
	}
}
3.2 WallpaperManagerService::bindWallpaperComponentLocked
  • bindWallpaperComponentLocked函数较长,分开来分析。
  • step 1, 如果设置的壁纸服务包名和现在设置的包名相同则返回不做处理
  • step 2, 根据componentName来查询对应的服务相关信息,即ServiceInfo,si为空说明对应包名的服务不存在,也返回不做处理了
  • step 3, 对应包名的WallpaperService没有包含android.Manifest.permission.BIND_WALLPAPER权限,返回不做后续处理
// step 1 , 壁纸对应的包名是否改变,不是则是相同的壁纸返回,不做处理
if (!force && changingToSame(componentName, wallpaper)) {
	return true;
}

int serviceUserId = wallpaper.userId;
// step 2, 通过PackageManager查找是否存在对应包名的Service相关信息,不存在则返回
ServiceInfo si = mIPackageManager.getServiceInfo(componentName,
		PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, serviceUserId);
if (si == null) {
	Slog.w(TAG, "Attempted wallpaper " + componentName + " is unavailable");
	return false;
}

// step 3, 如果service不包含android.Manifest.permission.BIND_WALLPAPER权限则返回
if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) {
	String msg = "Selected service does not have "
			+ android.Manifest.permission.BIND_WALLPAPER
			+ ": " + componentName;
	if (fromUser) {
		throw new SecurityException(msg);
	}
	Slog.w(TAG, msg);
	return false;
}
3.3 WallpaperManagerService::bindWallpaperComponentLocked

ris为查询系统中所有的WallpaperService,筛选componentName包名相同的WallpaperService,并创建相关WallpaperInfo,wi为null的情况, 则说明对应的WallpaperService不存在,返回

WallpaperInfo wi = null;

Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
if (componentName != null && !componentName.equals(mImageWallpaper)) {
	// Make sure the selected service is actually a wallpaper service.
	List<ResolveInfo> ris =
			mIPackageManager.queryIntentServices(intent,
					intent.resolveTypeIfNeeded(mContext.getContentResolver()),
					PackageManager.GET_META_DATA, serviceUserId).getList();
	for (int i=0; i<ris.size(); i++) {
		ServiceInfo rsi = ris.get(i).serviceInfo;
		// step 1, 如果componentName包名对应的service存在,创建WallpaperInfo保存ServiceInfo信息
		if (rsi.name.equals(si.name) &&
				rsi.packageName.equals(si.packageName)) {
			try {
				wi = new WallpaperInfo(mContext, ris.get(i));
			} catch (XmlPullParserException e) {
				if (fromUser) {
					throw new IllegalArgumentException(e);
				}
				Slog.w(TAG, e);
				return false;
			} catch (IOException e) {
				if (fromUser) {
					throw new IllegalArgumentException(e);
				}
				Slog.w(TAG, e);
				return false;
			}
			break;
		}
	}
	// step 2, componentName包名对应的service不存在, 直接返回
	if (wi == null) {
		String msg = "Selected service is not a wallpaper: "
				+ componentName;
		if (fromUser) {
			throw new SecurityException(msg);
		}
		Slog.w(TAG, msg);
		return false;
	}
}
3.4 WallpaperManagerService::bindWallpaperComponentLocked

wi不为空, 对应的WallpaperService存在,后续做绑定服务的工作,创建WallpaperConnection对象,WallpaperConnection继承IWallpaperConnection接口, 并且实现了ServiceConnection接口。接下来就是通过mContext.bindServiceAsUser来绑定componentName对应的WallpaperService,绑定服务成功后会回调ServiceConnection接口的onServiceConnected函数,即WallpaperConnection的onServiceConnected函数,onServiceConnected函数放到后面分析,同一个用户,并且上次用户设置的信息不为空(mLastWallpaper != null),取消掉上一次的壁纸设定。

class WallpaperConnection extends IWallpaperConnection.Stub
	ServiceConnection {}

final int componentUid = mIPackageManager.getPackageUid(componentName.getPackageName(),
			MATCH_DIRECT_BOOT_AUTO, wallpaper.userId);
// step 1, 创建WallpaperConnection对象
WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper, componentUid);
intent.setComponent(componentName);
intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
		com.android.internal.R.string.wallpaper_binding_label);
intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(
		mContext, 0,
		Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),
				mContext.getText(com.android.internal.R.string.chooser_wallpaper)),
		0, null, new UserHandle(serviceUserId)));

// step 2, 绑定服务, 服务绑定成功会回调WallpaperConnection的onServiceConnected函数
if (!mContext.bindServiceAsUser(intent, newConn,
		Context.BIND_AUTO_CREATE | Context.BIND_SHOWING_UI
				| Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
				| Context.BIND_INCLUDE_CAPABILITIES,
		new UserHandle(serviceUserId))) {
	String msg = "Unable to bind service: "
			+ componentName;
	if (fromUser) {
		throw new IllegalArgumentException(msg);
	}
	Slog.w(TAG, msg);
	return false;
}

// step 3, 取消上一次的壁纸服务
if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null
		&& !wallpaper.equals(mFallbackWallpaper)) {
	detachWallpaperLocked(mLastWallpaper);
}
wallpaper.wallpaperComponent = componentName;
wallpaper.connection = newConn;
newConn.mReply = reply;
if (wallpaper.userId == mCurrentUserId && !wallpaper.equals(mFallbackWallpaper)) {
	mLastWallpaper = wallpaper;
}

3.5 detachWallpaperLocked

wallpaper.connection不为空, 说明WallPaperService还在运行,wallpaper.connection.mService为IWallpaperService接口,detach函数Android9.0以前不存在,回调IWallpaperService的detach函数,最终调用WallPaperService的detach函数来回收相关资源,和attach函数相对应,通过mContext.unbindService(wallpaper.connection)取消绑定服务来销毁服务。WallpaperConnection.DisplayConnector::disconnectLocked善后工作

private void detachWallpaperLocked(WallpaperData wallpaper) {
	// step 1, wallpaper.connection不为空, 说明WallPaperService还在运行
	if (wallpaper.connection != null) {
		try {
			// step 2, 回调WallpaperService的detach函数
			if (wallpaper.connection.mService != null) {
				wallpaper.connection.mService.detach();
			}
		} catch (RemoteException e) {
			Slog.w(TAG, "Failed detaching wallpaper service ", e);
		}
		// step 3, 取消绑定WallpaperService
		mContext.unbindService(wallpaper.connection);

		// step 4调用WallpaperConnection.DisplayConnector::disconnectLocked函数
		// 将壁纸的WindowToken从WindowManagerService中移除,壁纸将无法显示
		wallpaper.connection.forEachDisplayConnector(
				WallpaperConnection.DisplayConnector::disconnectLocked);
		wallpaper.connection.mService = null;
		wallpaper.connection.mDisplayConnector.clear();
		wallpaper.connection = null;
		if (wallpaper == mLastWallpaper) mLastWallpaper = null;
	}
}
3.6 WallpaperConnection::DisplayConnector::disconnectLocked
void WallpaperConnection::DisplayConnector::disconnectLocked() {

	try {
		// step 1 清除壁纸的WindowToken
		mIWindowManager.removeWindowToken(mToken, mDisplayId);
	} catch (RemoteException e) {
	}
	try {
		// step 2 回调mEngine的destroy函数
		if (mEngine != null) {
			mEngine.destroy();
		}
	} catch (RemoteException e) {
	}
	mEngine = null;
}
3.7 WallpaperConnection::onServiceConnected
public void WallpaperConnection::onServiceConnected(ComponentName name, IBinder service) {
	synchronized (mLock) {
		if (mWallpaper.connection == this) {
			mService = IWallpaperService.Stub.asInterface(service);
			// step 1, 调用attachServiceLocked函数
			attachServiceLocked(this, mWallpaper);
			// step 2, 将用户相关数据保存到XML文件中
			if (!mWallpaper.equals(mFallbackWallpaper)) {
				saveSettingsLocked(mWallpaper.userId);
			}
			FgThread.getHandler().removeCallbacks(mResetRunnable);
			if (mPerformance != null) {
				// step 3, 通知壁纸相关信息改变
				mPerformance.notifyWallpaperChanged(name.getPackageName());
			}
		}
	}
}
3.8 WallpaperConnection::attachServiceLocked
private void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
	conn.forEachDisplayConnector(connector-> connector.connectLocked(conn, wallpaper));
}
3.9 WallpaperConnection::DisplayConnector::connectLocked

connection.mService为IWallpaperService接口,如果为空直接返回, 接下来添加壁纸的token到WindowManagerService中, 这样壁纸窗口才能添加到WindowManagerService中显示出来.IWallpaperService的attach为WallpaperService的attach函数,回调attach函数来对WallpaperService的Engine等相关资源的初始化。

void WallpaperConnection::DisplayConnector::connectLocked(
    WallpaperConnection connection, WallpaperData wallpaper) {
	// step 1, IWallpaperService为空直接返回
	if (connection.mService == null) {
		Slog.w(TAG, "WallpaperService is not connected yet");
		return;
	}

	try {
		// step 2, 将壁纸相关的WindowToken添加到WMS中
		// 得有壁纸Token, WallPaperService才能往WMS中添加壁纸窗口
		mIWindowManager.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId);
	} catch (RemoteException e) {
		Slog.e(TAG, "Failed add wallpaper window token on display " + mDisplayId, e);
		return;
	}

	final DisplayData wpdData = getDisplayDataOrCreate(mDisplayId);
	try {
		// step 3, 回调WallPaperService的attach函数
		connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
				wpdData.mWidth, wpdData.mHeight,
				wpdData.mPadding, mDisplayId);
	} catch (RemoteException e) {
		Slog.w(TAG, "Failed attaching wallpaper on display", e);
		if (wallpaper != null && !wallpaper.wallpaperUpdating
				&& connection.getConnectedEngineSize() == 0) {
			bindWallpaperComponentLocked(null /* componentName */, false /* force */,
					false /* fromUser */, wallpaper, null /* reply */);
		}
	}
}
4 WallPaperService代码分析
4.1 WallpaperService::attach

如果服务以及销毁,则返回,接下来是初始化相关.mSession变量的初始化, 用来向WindowManagerService添加壁纸窗口,surfeace相关初始化后,初始化完成, 更新surface相关信息,最后调用updateSurface函数

void attach(IWallpaperEngineWrapper wrapper) {
	// step 1 ,如果已经销毁,返回
	if (mDestroyed) {
		return;
	}

	mInitializing = true;
	// step 2 ,获取Session对象, 和WMS直接通信
	mSession = WindowManagerGlobal.getWindowSession();
	mWindow.setSession(mSession);

	// step 3 , surface相关初始化
	onCreate(mSurfaceHolder);
	mInitializing = false;
	mReportedVisible = false;
	updateSurface(false, false, false);
}
WallpaperService::updateSurface

创建事件接收者对象mInputEventReceiver ,调用mSession的addToDisplay将壁纸窗口mWinodw添加到WindowManagerservice中,mSession.relayout将为mWindow分配surface,以及窗口大小告知WallpaperService。mIWallpaperEngine.reportShown()来告知WallpaperManagerService壁纸已经开始显示了。

void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) {
	if (mDestroyed) {
		Log.w(TAG, "Ignoring updateSurface: destroyed");
	}

	if (forceRelayout || creating || surfaceCreating || formatChanged || sizeChanged
			|| typeChanged || flagsChanged || redrawNeeded
			|| !mIWallpaperEngine.mShownReported) {

		try {
			if (!mCreated) {

				mInputChannel = new InputChannel();
				// step 1, 将mWindow添加到WindowManagerService中
				if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,
						mDisplay.getDisplayId(), mWinFrame, mContentInsets, mStableInsets,
						mOutsets, mDisplayCutout, mInputChannel,
						mInsetsState) < 0) {
					Log.w(TAG, "Failed to add window while updating wallpaper surface.");
					return;
				}
				mCreated = true;
				// step 2, 创建按键接收者
				mInputEventReceiver = new WallpaperInputEventReceiver(
						mInputChannel, Looper.myLooper());
			}

			// step 3, 给壁纸窗口布局和分配surface
			final int relayoutResult = mSession.relayout(
				mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
					View.VISIBLE, 0, -1, mWinFrame, mOverscanInsets, mContentInsets,
					mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,
					mDisplayCutout, mMergedConfiguration, mSurfaceControl,
					mInsetsState);

			} finally {
				mIsCreating = false;
				mSurfaceCreated = true;
				if (redrawNeeded) {
					mSession.finishDrawing(mWindow);
				}
				// step 4, 通知WallPaperManagerService已经显示了
				mIWallpaperEngine.reportShown();
			}
		} catch (RemoteException ex) {
		}
	}
}
壁纸相关总结
  • 1 壁纸服务相关的配置需要在配置文件中声明
  • 2 实现壁纸服务相关的核心是WallpaperService中的Engine类和surface相关操作
  • 3 静态壁纸和动态壁纸都继承自WallpaperService
  • 4 壁纸窗口WindowToken是通过WindowPaperManagerService来向WMS中来添加和移除操作,和输入法窗口相似
  • 5 Android中的壁纸实现采用系统服务和四大组件中的服务两层框架来实现, 只需要我们实现自定义的WallpaperService, 就能够通过系统服务来控制整个壁纸的显示,隐藏,切换等, 隐藏服务之间的接口调用细节,能够很轻松的来开发壁纸服务
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值