@guhuizaifeiyang
2017-08-31T16:36:27.000000Z
字数 14065
阅读 4076
Android开发
Android壁纸开发流程分析
android壁纸服务流程浅析
深入理解Android卷III 第八章深入理解Android壁纸
本文讨论的是开机默认壁纸的加载流程,这里只分析静态壁纸。静态壁纸是运行于SystemUI进程中的一个名为ImageWallpaper的特殊WallpaperService。
Android的壁纸功能的实现主要由散布在下面几个文件中的类来完成:
// TODO 类图
下面是WallpaperManagerService的启动时序图:
SystemServer.java->startOtherServices()
try {
Slog.i(TAG, "Wallpaper Service");
wallpaper = new WallpaperManagerService(context);
ServiceManager.addService(Context.WALLPAPER_SERVICE, wallpaper);
} catch (Throwable e) {
reportWtf("starting Wallpaper Service", e);
}
WallpaperManagerService.java->WallpaperManagerService()
public WallpaperManagerService(Context context) {
mImageWallpaper = ComponentName.unflattenFromString(
context.getResources().getString(R.string.image_wallpaper_component));
// 获取WindowManagerService的实例
mIWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
// ...
getWallpaperDir(UserHandle.USER_OWNER).mkdirs();
loadSettingsLocked(UserHandle.USER_OWNER);
}
public static final @UserIdInt int USER_OWNER = 0;
WallpaperManagerService.java->getWallpaperDir()
private static File getWallpaperDir(int userId) {
// 返回系统的用户目录
return Environment.getUserSystemDirectory(userId);
}
这里返回的结果是:/data/system/users/0。所以Step1中getWallpaperDir(UserHandle.USER_OWNER).mkdirs();
就是创建了:/data/system/users/0/这个目录。
WallpaperManagerService.java->loadSettingsLocked()
private void loadSettingsLocked(int userId) {
// (1)
JournaledFile journal = makeJournaledFile(userId);
FileInputStream stream = null;
File file = journal.chooseForRead();
if (!file.exists()) {
// This should only happen one time, when upgrading from a legacy system
migrateFromOld();
}
// (2)
WallpaperData wallpaper = mWallpaperMap.get(userId);
if (wallpaper == null) {
wallpaper = new WallpaperData(userId);
mWallpaperMap.put(userId, wallpaper);
}
boolean success = false;
// (3) 解析/data/system/users/0/wallpaper_info.xml文件
// 省略
// 第一次启动时,wallpaer_info.xml不存在,也就不采用其里面的值。这时调用getMaximumSizeDimension来赋值。
int baseSize = getMaximumSizeDimension();
if (wallpaper.width < baseSize) {
wallpaper.width = baseSize;
}
if (wallpaper.height < baseSize) {
wallpaper.height = baseSize;
}
}
载入系统保存的配置,这里也是分三步:
1.用JournaledFile这个工具类封装/data/system/users/0/wallpaper_info.xml文件。这个工具类会包装两个文件,一个是wallpaper_info.xml正式文件,另一个是wallpaper_info.xml.tmp临时文件。如果正式文件存在就选出正式文件,并删除临时文件;如果正式文件不存在就将临时文件重名为正式文件。
private static JournaledFile makeJournaledFile(int userId) {
final String base = new File(getWallpaperDir(userId), WALLPAPER_INFO).getAbsolutePath();
return new JournaledFile(new File(base), new File(base + ".tmp"));
}
static final String WALLPAPER_INFO = "wallpaper_info.xml";
2.创建一个WallpaperData并存入mWallpaperMap ,我们可以看看WallpaperData 的构造函数,这是一个内部类:
static final String WALLPAPER = "wallpaper";
WallpaperData(int userId) {//0
this.userId = userId;
//位于/data/system/users/0/wallpaper,是存放壁纸的文件
wallpaperFile = new File(getWallpaperDir(userId), WALLPAPER);
}
WallpaperData 的构造方法主要是创建了一个/data/system/users/0/wallpaper的File对象。
3.最后就是解析/data/system/users/0/wallpaper_info.xml文件
Android系统的壁纸信息存放在/data/system/users/0/目录下,WallpaperManagerService启动后,会生成如下两个文件在/data/system/users/0/目录下:
static final String WALLPAPER = wallpaper; //设置的壁纸图片,一般为jpeg格式
static final String WALLPAPER_INFO = wallpaper_info.xml; //包含墙纸的规格信息:高、宽
Wallpaper_info.xml的解析可以查看WallpaperManagerService的loadSettingsLocked()方法。
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<wp id="1" width="720" height="1280" cropLeft="0" cropTop="0" cropRight="1280" cropBottom="1280" name="" backup="true" />
<kwp id="2" width="1280" height="1280" cropLeft="0" cropTop="0" cropRight="0" cropBottom="0" name="" />
第一次启动时,wallpaer_info.xml不存在,也就不采用其里面的值。这时,则采用的是getMaximumSizeDimension,也就是背景图的宽和高由getMaximumSizeDimension决定。
分析到这里,我们看到,除了创建了WallpaperData,并没有其它和加载壁纸相关的操作。并且,这里创建的WallpaperData还未填充壁纸的有效信息。
所以,接下来我们从另外一条线索开始分析。壁纸经常伴随着创建、移动、切换、删除等操作,那么肯定会有一个监听机制来监听壁纸的这一系列动作。于是,我们从WallpaperManagerService类的systemRunning方法中找到了一个WallpaperObserver类。
SystemServer.java->startOtherServices()
try {
if (wallpaperF != null) wallpaperF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying WallpaperService running", e);
}
WallpaperManagerService.java->systemRunning()
public void systemRunning() {
if (DEBUG) Slog.v(TAG, "systemReady");
WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_OWNER);
switchWallpaper(wallpaper, null);
wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper);
wallpaper.wallpaperObserver.startWatching();
// ...
}
wallpaperObserver是监听壁纸变化的重要组件,后面会详细介绍。
WallpaperManagerService.java->switchWallpaper()
void switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply) {
synchronized (mLock) {
RuntimeException e = null;
try {
ComponentName cname = wallpaper.wallpaperComponent != null ? wallpaper.wallpaperComponent : wallpaper.nextWallpaperComponent;
if (bindWallpaperComponentLocked(cname, true, false, wallpaper, reply)) {
return;
}
} catch (RuntimeException e1) {
e = e1;
}
Slog.w(TAG, "Failure starting previous wallpaper", e);
clearWallpaperLocked(false, wallpaper.userId, reply);
}
}
switchWallpaper中正常执行到bindWallpaperComponentLocked就会返回了,bindWallpaperComponentLocked的工作是启动一个新的壁纸服务并终止旧的壁纸服务,只有启动壁纸异常的情况下才会执行下面的clearWallpaperLocked清理操作。
WallpaperObserver是一个内部类,用来监听壁纸的变化。首次设置壁纸时会调用CREATE事件,CLOSE_WRITE事件在壁纸每次变化时都会被调用。
private class WallpaperObserver extends FileObserver {
final WallpaperData mWallpaper;
final File mWallpaperDir;
final File mWallpaperFile;
final File mWallpaperInfoFile;
public WallpaperObserver(WallpaperData wallpaper) {
// 监听/data/system/users/0/下CLOSE_WRITE | MOVED_TO | DELETE | DELETE_SELF几个事件
super(getWallpaperDir(wallpaper.userId).getAbsolutePath(),
CLOSE_WRITE | MOVED_TO | DELETE | DELETE_SELF);
mWallpaperDir = getWallpaperDir(wallpaper.userId);
mWallpaper = wallpaper;
mWallpaperFile = new File(mWallpaperDir, WALLPAPER);
mWallpaperInfoFile = new File(mWallpaperDir, WALLPAPER_INFO);
}
@Override
public void onEvent(int event, String path) {
if (path == null) {
return;
}
synchronized (mLock) {
// ...
// 绑定壁纸组件
File changedFile = new File(mWallpaperDir, path);
if (mWallpaperFile.equals(changedFile)) {
bindWallpaperComponentLocked(mImageWallpaper, true,
false, mWallpaper, null);
// 保存壁纸信息,和loadSettingsLocked相反。
saveSettingsLocked(mWallpaper);
}
}
}
}
当/data/system/users/0/wallpaper发生改动时,则需要绑定壁纸组件,调用bindWallpaperComponentLocked方法。这里mImageWallpaper是个ComponentName,指向com.android.systemui/com.android.systemui.ImageWallpaper。
bindWallpaperComponentLocked()方法将会启动由ComponentName所指定的WallpaperService,并向WMS申请用于加入壁纸窗体的窗体令牌。
boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) {
// ...
try {
/* 当componentName为null时表示使用默认壁纸。
这里会将componentName參数改为默认壁纸的componentName */
if (componentName == null) {
/* 首先会尝试从com.android.internal.R.string.default_wallpaper_component
中获取默认壁纸的componentName,这个值的设置位于res/values/config.xml中。*/
// 这里获取的componentName=null
componentName = WallpaperManager.getDefaultWallpaperComponent(mContext);
if (componentName == null) {
// 使用静态壁纸
// Fall back to static image wallpaper
componentName = mImageWallpaper;
if (DEBUG) Slog.v(TAG, "Using image wallpaper");
}
}
// ...
// 对ComponentName所描写叙述的Service进行一系列的验证,以确保它是一个壁纸服务。
// WallpaperConnection实现了ServiceConnection接口用于监听和WallpaperService之间的连接状态。
WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper);
intent.setComponent(componentName);
// ...
/* 启动指定的壁纸服务。当服务启动完毕后,剩下的启动流程会在WallpaperConnection.onServiceConnected()中继续 */
if (!mContext.bindServiceAsUser(intent, newConn,
Context.BIND_AUTO_CREATE | Context.BIND_SHOWING_UI
| Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
new UserHandle(serviceUserId))) {
// ...
}
// 新的的壁纸服务启动成功后。便通过detachWallpaperLocked()销毁旧有的壁纸服务
if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null) {
detachWallpaperLocked(mLastWallpaper);
}
// 将新的壁纸服务的执行信息保存到WallpaperData中
wallpaper.wallpaperComponent = componentName;
wallpaper.connection = newConn;
// 最后向WMS申请注冊一个WALLPAPER类型的窗体令牌。
mIWindowManager.addWindowToken(newConn.mToken,
WindowManager.LayoutParams.TYPE_WALLPAPER);
}
WallpaperManagerService.java$WallpaperConnection->onServiceConnected()
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mLock) {
if (mWallpaper.connection == this) {
// ImageWallpaper的onBind方法返回值就是这个mService
mService = IWallpaperService.Stub.asInterface(service);
attachServiceLocked(this, mWallpaper);
saveSettingsLocked(mWallpaper);
}
}
}
WallpaperConnection它不仅实现了ServiceConnection接口用于监听和WallpaperService之间的连接状态。在服务绑定成功后的WallpaperConnection.onServiceConnected()方法调用中,WallpaperConnection的实例会被发送给WallpaperService,使其作为WallpaperService向WallpaperManagerService进行通信的桥梁。
WallpaperManagerService.java->attachServiceLocked()
void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
try {
// 这里会调用IWallpaperServiceWrapper的attach方法,将创建壁纸窗体所需的信息发送给壁纸服务。
conn.mService.attach(conn, conn.mToken,
WindowManager.LayoutParams.TYPE_WALLPAPER, false,
wallpaper.width, wallpaper.height, wallpaper.padding);
} catch (RemoteException e) {
// ...
}
}
}
WallpaperService$IWallpaperServiceWrapper->attach
public void attach(IWallpaperConnection conn, IBinder windowToken,
int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) {
// 使用WallpaperManagerService提供的參数构造一个IWallpaperEngineWarapper实例
new IWallpaperEngineWrapper(mTarget, conn, windowToken,
windowType, isPreview, reqWidth, reqHeight, padding);
}
IWallpaperEngineWrapper(WallpaperService context,
IWallpaperConnection conn, IBinder windowToken,
int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) {
// 类成员变量初始化
// ...
Message msg = mCaller.obtainMessage(DO_ATTACH);
mCaller.sendMessage(msg);
}
接下来分析DO_ATTACH消息的处理:
WallpaperService$IWallpaperEngineWrapper->executeMessage()
public void executeMessage(Message message) {
switch (message.what) {
case DO_ATTACH: {
try {
// 把IWallpaperEngineWrapper实例传递给WallpaperConnection进行保存。
mConnection.attachEngine(this);
} catch (RemoteException e) {
Log.w(TAG, "Wallpaper host disappeared", e);
return;
}
// 通过onCreateEngine()方法创建一个Engine。WallpaperService仅仅是提供壁纸执行的场所,而Engine才是真正的壁纸的实现
Engine engine = onCreateEngine();
mEngine = engine;
mActiveEngines.add(engine);
engine.attach(this);
return;
}
// ...
}
}
ImageWallpaper.java->onCreateEngine():
WallpaperService类中的onCreateEngine方法是一个抽象方法,ImageWallpaper类中实现了此方法。回想一下,在bindWallpaperComponentLocked方法中我们绑定的就是ImageWallpaper服务。
@Override
public Engine onCreateEngine() {
// 创建了一个DrawableEngine实例
mEngine = new DrawableEngine();
return mEngine;
}
Engine创建完毕之后会通过Engine.attach()方法完毕Engine的初始化工作。
void attach(IWallpaperEngineWrapper wrapper) {
// ...
// ...
// Engine的初始化工作
onCreate(mSurfaceHolder);
mInitializing = false;
mReportedVisible = false;
// 绘制壁纸
updateSurface(false, false, false);
}
ImageWallpaper$DrawableEngine->updateSurfaceSize():
@Override
public void onCreate(SurfaceHolder surfaceHolder) {
// ...
updateSurfaceSize(surfaceHolder, getDefaultDisplayInfo());
}
void updateSurfaceSize(SurfaceHolder surfaceHolder, DisplayInfo displayInfo) {
// ...
// 将mWallpaper和mDefaultWallpaper重置为null
WallpaperManager.forgetLoadedWallpaper();
updateWallpaperLocked();
...
}
private void updateWallpaperLocked() {
// ...
mBackground = mWallpaperManager.getBitmap();
// ...
}
public Bitmap getBitmap() {
return sGlobals.peekWallpaperBitmap(mContext, true);
}
public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault) {
synchronized (this) {
// 如果已经加载了壁纸,则直接返回。
if (mWallpaper != null) {
return mWallpaper;
}
// 如果没有加载壁纸,则返回默认壁纸。
if (mDefaultWallpaper != null) {
return mDefaultWallpaper;
}
mWallpaper = null;
try {
// 首次开机,这里获取的wallpaper=null
mWallpaper = getCurrentWallpaperLocked(context);
} catch (OutOfMemoryError e) {
Log.w(TAG, "No memory load current wallpaper", e);
}
if (returnDefault) {
if (mWallpaper == null) {
mDefaultWallpaper = getDefaultWallpaperLocked(context);
return mDefaultWallpaper;
} else {
mDefaultWallpaper = null;
}
}
return mWallpaper;
}
}
private Bitmap getCurrentWallpaperLocked(Context context) {
if (mService == null) {
Log.w(TAG, "WallpaperService not running");
return null;
}
try {
Bundle params = new Bundle();
// 获取/data/system/users/0/wallpaper的文件描述符
ParcelFileDescriptor fd = mService.getWallpaper(this, params);
if (fd != null) {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
return BitmapFactory.decodeFileDescriptor(
fd.getFileDescriptor(), null, options);
} catch (OutOfMemoryError e) {
Log.w(TAG, "Can't decode file", e);
} finally {
try {
fd.close();
} catch (IOException e) {
// Ignore
}
}
}
} catch (RemoteException e) {
// Ignore
}
return null;
}
WallpaperManagerService.java->getWallpaper()
public ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb,
Bundle outParams) {
synchronized (mLock) {
// ...
// 从mWallpaperMap中获取对应用户的WallpapaerData
WallpaperData wallpaper = mWallpaperMap.get(wallpaperUserId);
if (wallpaper == null) {
return null;
}
// 从WallpaperData中获取壁纸的尺寸信息并保存在outParms中
try {
if (outParams != null) {
outParams.putInt("width", wallpaper.width);
outParams.putInt("height", wallpaper.height);
}
wallpaper.callbacks.register(cb);
File f = new File(getWallpaperDir(wallpaperUserId), WALLPAPER);
if (!f.exists()) {
return null;
}
// 将文件包装为一个文件描述符并返回给调用者
return ParcelFileDescriptor.open(f, MODE_READ_ONLY);
} catch (FileNotFoundException e) {
/* Shouldn't happen as we check to see if the file exists */
Slog.w(TAG, "Error getting wallpaper", e);
}
return null;
}
}
private Bitmap getDefaultWallpaperLocked(Context context) {
InputStream is = openDefaultWallpaper(context);
// ...
}
public static InputStream openDefaultWallpaper(Context context) {
// 配置wallpaper路径
// ...
return context.getResources().openRawResource(
com.android.internal.R.drawable.default_wallpaper);
}
WallpaperService$Engine->updateSurface():
Engine的updateSurface()方法将会创建壁纸窗体及Surface,并进行壁纸的绘制。
// Question
根据机器颜色加载相应的壁纸
// Solution
WallpaperManager.java->getDefaultWallpaperLocked:
private Bitmap getDefaultWallpaperLocked(Context context) {
try {
InputStream is = context.getResources().openRawResource( com.android.internal.R.drawable.default_wallpaper);
}
直接修改com.android.internal.R.drawable.default_wallpaper。