首页 > 代码库 > 析壁纸机制分析

析壁纸机制分析

文章记录自己的学习过程,供日后参考。

首先要知道壁纸设置的大体流程:上层应用调用wallpaperManager.setStream()或其他接口进行设置壁纸,WallpaperManagerService首先将壁纸copy到/data/system/user/0/目录下,WallpaperManagerService对该目录注册了一个WallpaperObserver,拷贝壁纸成功后会触发调用WallpaperObserver.onEvent()函数,onEvent()函数首先调用notifyCallbacksLocked()触发回调,然后再调用bindWallpaperComponentLocked()做进行进一步的壁纸更换。虽然整个壁纸机制有WallpaperManager、WallpaperService、ImageWallpaper、WallpaperManagerService、Globals、DrawableEngine、WallpaperData、WallpaperConnection等数据结构,感觉挺复杂的,我们应该剥离他的外表,看清他的本质。我们只要搞清了他们之间怎么交互的?交流些什么东西?就搞清楚了整个壁纸机制,说白了只要搞清楚IWallpaperService.aidl、IWallpaperEngine.aidl、IWallpaperConnection.aidl这三个文件就OK了,因为这三个文件是通信的关键所在。

1.IWallpaperService.aidl

oneway interface IWallpaperService {
    void attach(IWallpaperConnection connection,
    		IBinder windowToken, int windowType, boolean isPreview,
    		int reqWidth, int reqHeight);
}
aidl语言专为Binder设计,因此里面的函数可看做是通信接口。

(1).WallpaperService.IWallpaperServiceWrapper extends IWallpaperService.Stub;

(2).WallpaperService.onBind()

    /**
     * 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);
    }
从上面两点可以知道:IWallpaperService的Binder本地端在WallpaperService中;WallpaperService服务将是调用mContext.bindService()启动的;IWallpaperService的代理对象将在ServiceConnection.onServiceConnected()中获取。很容易验证WallPaperManagerService中确实是这么干的。

WallPaperManagerService.bindWallpaperComponentLocked():

    boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
            boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) {
            ........
            Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
            ........
            WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper);
            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)));
            if (!mContext.bindServiceAsUser(intent, newConn,
                    Context.BIND_AUTO_CREATE | Context.BIND_SHOWING_UI,
                    new UserHandle(serviceUserId))) {
           ..........
            }
   }
文章最开头就说了,每次设置壁纸都会调用bindWallpaperComponentLocked()函数,也就是说每次设置壁纸都会重新new WallpaperConnection,重新调用mContext.bindServiceAsUser()绑定启动WallpaperService,为啥要每次重新bind一次呢?防止systemUI挂掉,无法设置壁纸?不管了,反正记住每次设置壁纸时都会bind一次WallpaperService,并且调用WallpaperConnection.onServiceConnected()函数。onServiceConnected()函数中真的获取了IWallpaperService的Binder代理对象。

        public void onServiceConnected(ComponentName name, IBinder service) {
            synchronized (mLock) {
                if (mWallpaper.connection == this) {
                    mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
                    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.
                    saveSettingsLocked(mWallpaper);
                }
            }
        }
mService = IWallpaperService.Stub.asInterface(service);语句获取了IWallpaperService的Binder代理对象。再看看WallpaperConnection.onServiceConnected()函数中调用WallPaperManagerService.attachServiceLocked()干什么?

    void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
        try {
            conn.mService.attach(conn, conn.mToken,
                    WindowManager.LayoutParams.TYPE_WALLPAPER, false,
                    wallpaper.width, wallpaper.height);
        } catch (RemoteException e) {
            Slog.w(TAG, "Failed attaching wallpaper; clearing", e);
            if (!wallpaper.wallpaperUpdating) {
                bindWallpaperComponentLocked(null, false, false, wallpaper, null);
            }
        }
    }
嗯,跨Binder调用IWallpaperService本地端的attach()。看看attach()做了什么?

        public void attach(IWallpaperConnection conn, IBinder windowToken,
                int windowType, boolean isPreview, int reqWidth, int reqHeight) {
            new IWallpaperEngineWrapper(mTarget, conn, windowToken,
                    windowType, isPreview, reqWidth, reqHeight);
        }
直接new IWallpaperEngineWrapper对象,注意传进来的参数,包含壁纸显示参数。IWallpaperEngineWrapper extends IWallpaperEngine.Stub implements HandlerCaller.Callback,所以又知道了IWallpaperEngine的Binder本地端保存在WallpaperService中,代理端返回给WallPaperManagerService.WallpaperConnection.mEngine变量的呢?这个研究到后边应该自然就知道了。IWallpaperEngineWrapper的构造函数将被调用,在构造函数中会post一个handle消息DO_ATTACH,

            switch (message.what) {
                case DO_ATTACH: {
                    try {
                        mConnection.attachEngine(this);
                    } catch (RemoteException e) {
                        Log.w(TAG, "Wallpaper host disappeared", e);
                        return;
                    }
                    Engine engine = onCreateEngine();
                    mEngine = engine;
                    mActiveEngines.add(engine);
                    engine.attach(this);
                    return;
                }
感觉牵涉的越来越多了,没关系,一个个来分析。

先上班,下班再研究。


析壁纸机制分析