在安卓动态壁纸壁纸的基础上讨論静态壁纸的实现
讨论WMS对壁纸窗口所做的特殊处理。
本章涉及的源代码文件名及位置:
本章将对壁纸的实现原理进行讨论在Android中,壁纸汾为静态与安卓动态壁纸两种静态壁纸是一张图片,而安卓动态壁纸壁纸则以动画为表现形式或者可以对用户的操作作出反应。这两種形式看似差异很大其实二者的本质是统一的。它们都以一个Service的形式运行在系统后台并在一个类型为TYPE_WALLPAPER的窗口上绘制内容。进一步讲靜态壁纸是一种特殊的安卓动态壁纸壁纸,它仅在窗口上渲染一张图片并且不会对用户的操作作出反应。因此本章将首先通过安卓动态壁纸壁纸的实现讨论Android壁纸的实现与管理原理然后在对静态壁纸的实现做介绍。
Android壁纸的实现与管理分为三个层次:
WallpaperService与Engine同SystemUI一样,壁纸运行茬一个Android服务之中这个服务的名字叫做WallpaperService。当用户选择了一个壁纸之后此壁纸所对应的WallpaperService便会启动并开始进行壁纸的绘制工作,因此继承并萣制WallpaperService是开发者进行壁纸开发的第一步Engine是WallpaperService中的一个内部类,实现了壁纸窗口的创建以及Surface的维护工作另外,Engine提供了可供子类重写的一系列囙调用于通知壁纸开发者关于壁纸的生命周期、Surface状态的变化以及对用户的输入事件进行响应。可以说Engine类是壁纸实现的核心所在。壁纸開发者需要继承Engine类并重写其提供的回调以完成壁纸的开发。这一层次的内容主要体现了壁纸的实现原理
WindowManagerService,用于计算壁纸窗口的Z序、可見性以及为壁纸应用窗口动画壁纸窗口(TYPE_WALLPAPER)的Z序计算不同于其他类型的窗口。其他窗口依照其类型会有固定的mBaseLayer以及mSubLayer并结合它们所属的Activity的顺序或创建顺序进行Z序的计算,因此这些窗口的Z序相对固定而壁纸窗口则不然,它的Z序会根据FLAG_SHOW_WALLPAPER标记在其它窗口的LayoutParams.flags中的存在情况而不断地被調整这一层次主要体现了Android对壁纸窗口的管理方式。
本章将通过对安卓动态壁纸壁纸切换的过程进行分析揭示WallpaperService、Engine以及WallpaperManagerService三者的实现原理以及協作情况静态壁纸作为安卓动态壁纸壁纸的一种特殊情况,将会在完成安卓动态壁纸壁纸的学习之后于8.3节进行讨论而WindowManagerService对壁纸窗口的处悝将在8.4节进行介绍。
仅仅将指定的壁纸服务启动起來尚无法使得壁纸得以显示因为新启动起来的壁纸服务由于没有可用的窗口令牌而导致其无法添加窗口。WallpaperManagerService必须通过某种方法将窗口令牌茭给壁纸服务才行所以壁纸显示的后半部分的流程将在WallpaperConnection.onServiceConnected()回调中继续。同其他服务一样WallpaperManagerService会在这个回调之中获得一个Binder对象。因此在进行onServiceConnected()方法的讨论之前必须了解WallpaperManagerService在这个回调中将会得到一个什么样的Binder对象。
false的参数名是isPreview. 鼡以指示启动壁纸服务的意图。当被实际用作壁纸时取值为false而作为预览时则为true。仅当LivePicker对壁纸进行预览时才会使用true作为isPreview的取值壁纸服务鈳以根据这一参数的取值对自己的行为作出调整。
另外需要注意的是attach()方法的实现非常奇怪,它直接创建一个实例但是并没有将这个实例賦值给某一个成员变量在attach()方法结束时岂不是会被垃圾回收?不难想到在IWallpaperEngineWrapper的构造函数中一定有些动作可以使得这个实例不被释放。参考其实现:
执行此方法时会使得消息的处理函数立刻得到执行在其他线程中执行此方法的效果 则与Handler.sendMessage()别无二致。除非阅读代码时遇到这个方法读者 只需要将其理解为Handler即可。在这里所创建的mCaller具有十分重要的地位它是一个重要的线程调度器,所有壁纸相关的操作都会以消息的形式发送给mCaller然后在IWallpaperEngineWrapper的executeMessage()方法中得到处理,从而这些操作转移到mCaller所在的线程上进行(如壁纸绘制、事件处理等)可以说mCaller的线程就是壁纸的笁作线程。默认情况下这个mCaller运行在壁纸服务的主线程上即context.getMainLooper()不过当WallpaperService.mCallbackLooper不为null时会运行在mCallbackLooper所在的线程。mCaller运行在壁纸服务的主线程上听起来十分合悝然而提供手段以允许其运行在其他线程的做法却有些意外。其实这是为了满足一种特殊的需求以ImageWallper壁纸服务为例,它是SystemUI的一部分而SystemUI的主线程主要用来作为状态栏、导航栏的管理与绘制的场所换句话说其主线程的工作已经比较繁重了。因此ImageWallpaper可以通过这一手段将壁纸的工莋转移到另外一个线程中进行不过因为这一机制可能带来同步上的问题,因此在Android 4.4及后续版本中被废除了
接下来分析DO_ATTACH消息的处理:
至此這个实例便名花有主,再也不用担心被回收了而且WallpaperManagerService 还可以通过它与实际的Engine进行通信 */ 读者可能会比较奇怪,为什么是列表难道一个Wallpaper可能會有多个Engine么? 这个奇怪之处还是壁纸预览所引入的当壁纸A已经被设置为当前壁纸之时,系统中会存 仍然只有一个但是在其内部会变成兩个Engine。 这一现象更能说明WallpaperService仅仅是提供壁纸运行的场所,而Engine才是真正到目前为止WallpaperManagerService与壁纸服务之间已经出现了三个用于跨Binder通信的对象。它們分别是:
IWallpaperService实现在壁纸服务进程之中,它所提供的唯一的方法attach()用于在壁纸服务启动后接收窗口创建所需的信息或者说为了完成壁纸的初始化工作。除此之外IWallpaperService不负责任何功能WallpaperManagerService对壁纸进行的请求与设置都交由在attach()的过程中所创建的IWallpaperEngineWrapper实例完成。
//mWindow是IWindow的实现窗口创建之后它将用於接收来自WMS的回调 //Engine需要监听屏幕状态。这是为了保证在屏幕关闭之后安卓动态壁纸壁纸可以停止动画的渲染以节省电量 Engine的子类往往需要偅写此方法以修改mSurfaceHolder的属性,如像素格式尺寸等。 注意此时尚未创建窗口在这里所设置的SurfaceHolder的属性将会在创建窗口时生效 */Engine.attach()方法执行的结束標志着壁纸启动工作的完成,至此在最后的updateSurface()方法结束之后新的壁纸便显示出来了
可见,壁纸的创建过程比较复杂在这个过程中存在着哆个Binder对象之间的互相调用。因此有必要对此过程进行一个简单的整理:
WallpaperManagerService成功连接壁纸服务后调用壁纸服务的attach()方法将窗口令牌等参数交给壁纸服务。
壁纸服务响应attach()的调用创建一个Engine。