@guhuizaifeiyang
2017-08-31T08:36:27.000000Z
字数 14065
阅读 4675
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 systemmigrateFromOld();}// (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) {//0this.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);}@Overridepublic 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=nullcomponentName = WallpaperManager.getDefaultWallpaperComponent(mContext);if (componentName == null) {// 使用静态壁纸// Fall back to static image wallpapercomponentName = 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()
@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {synchronized (mLock) {if (mWallpaper.connection == this) {// ImageWallpaper的onBind方法返回值就是这个mServicemService = 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服务。
@Overridepublic 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():
@Overridepublic void onCreate(SurfaceHolder surfaceHolder) {// ...updateSurfaceSize(surfaceHolder, getDefaultDisplayInfo());}void updateSurfaceSize(SurfaceHolder surfaceHolder, DisplayInfo displayInfo) {// ...// 将mWallpaper和mDefaultWallpaper重置为nullWallpaperManager.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=nullmWallpaper = 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中获取对应用户的WallpapaerDataWallpaperData 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。