Android R 设置壁纸流程和 Launcher 闪烁问题

您所在的位置:网站首页 啥是壁纸 Android R 设置壁纸流程和 Launcher 闪烁问题

Android R 设置壁纸流程和 Launcher 闪烁问题

2024-07-07 23:18| 来源: 网络整理| 查看: 265

一、设置壁纸流程

系统提供了相应的 API 接口,APP 侧通过 Context.getSystemService(Context.WALLPAPER_SERVICE) 获取 WallpaperManager 对象。WallpaperManager 中可以通过 setBitmap setStream setResource 三种方式进行设置。以 setResource 为例,整个流程大致如下:

不管是 setBitmap setStream setResource 哪种方式,都是调用 WallpaperManagerService 的 setWallpaper 获取 ParcelFileDescriptor 对象

@RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public int setResource(@RawRes int resid, @SetWallpaperFlags int which) throws IOException { if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); throw new RuntimeException(new DeadSystemException()); } final Bundle result = new Bundle(); final WallpaperSetCompletion completion = new WallpaperSetCompletion(); try { Resources resources = mContext.getResources(); /* Set the wallpaper to the default values */ ParcelFileDescriptor fd = sGlobals.mService.setWallpaper( "res:" + resources.getResourceName(resid), mContext.getOpPackageName(), null, false, result, which, completion, mContext.getUserId()); if (fd != null) { FileOutputStream fos = null; boolean ok = false; try { fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); copyStreamToWallpaperFile(resources.openRawResource(resid), fos); // The 'close()' is the trigger for any server-side image manipulation, // so we must do that before waiting for completion. fos.close(); completion.waitForCompletion(); } finally { // Might be redundant but completion shouldn't wait unless the write // succeeded; this is a fallback if it threw past the close+wait. IoUtils.closeQuietly(fos); } } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } return result.getInt(EXTRA_NEW_WALLPAPER_ID, 0); }

而 WallpaperMS 的 setWallpaper 中就是获取通过 getWallpaperSafeLocked 获取系统服务 systemReady() 启动初始化的 WallpaperData ,在通过 updateWallpaperBitmapLocked 获取 WallpaperData 的 ParcelFileDescriptor 返回给 WallpaperManager 。ParcelFileDescriptor 继续进行 copyStreamToWallpaperFile 写入覆盖操作,替换壁纸文件。

而这个 WallpaperData 中有个变量 wallpaperObserver ,也在开机时服务初始化 systemReady() 中调用 switchUser() ,已经执行 wallpaperObserver.startWatching() 。所以文件的变化触发 WallpaperObserver 的 onEvent() 。再去通知对应的模块通过 

@Override public void onEvent(int event, String path) { if (path == null) { return; } final boolean moved = (event == MOVED_TO); final boolean written = (event == CLOSE_WRITE || moved); final File changedFile = new File(mWallpaperDir, path); // System and system+lock changes happen on the system wallpaper input file; // lock-only changes happen on the dedicated lock wallpaper input file final boolean sysWallpaperChanged = (mWallpaperFile.equals(changedFile)); final boolean lockWallpaperChanged = (mWallpaperLockFile.equals(changedFile)); int notifyColorsWhich = 0; WallpaperData wallpaper = dataForEvent(sysWallpaperChanged, lockWallpaperChanged); if (DEBUG) { Slog.v(TAG, "Wallpaper file change: evt=" + event + " path=" + path + " sys=" + sysWallpaperChanged + " lock=" + lockWallpaperChanged + " imagePending=" + wallpaper.imageWallpaperPending + " whichPending=0x" + Integer.toHexString(wallpaper.whichPending) + " written=" + written); } if (moved && lockWallpaperChanged) { // We just migrated sys -> lock to preserve imagery for an impending // new system-only wallpaper. Tell keyguard about it and make sure it // has the right SELinux label. if (DEBUG) { Slog.i(TAG, "Sys -> lock MOVED_TO"); } SELinux.restorecon(changedFile); notifyLockWallpaperChanged(); notifyWallpaperColorsChanged(wallpaper, FLAG_LOCK); return; } synchronized (mLock) { if (sysWallpaperChanged || lockWallpaperChanged) { notifyCallbacksLocked(wallpaper); if (wallpaper.wallpaperComponent == null || event != CLOSE_WRITE // includes the MOVED_TO case || wallpaper.imageWallpaperPending) { if (written) { // The image source has finished writing the source image, // so we now produce the crop rect (in the background), and // only publish the new displayable (sub)image as a result // of that work. if (DEBUG) { Slog.v(TAG, "Wallpaper written; generating crop"); } SELinux.restorecon(changedFile); if (moved) { // This is a restore, so generate the crop using any just-restored new // crop guidelines, making sure to preserve our local dimension hints. // We also make sure to reapply the correct SELinux label. if (DEBUG) { Slog.v(TAG, "moved-to, therefore restore; reloading metadata"); } loadSettingsLocked(wallpaper.userId, true); } generateCrop(wallpaper); if (DEBUG) { Slog.v(TAG, "Crop done; invoking completion callback"); } wallpaper.imageWallpaperPending = false; if (sysWallpaperChanged) { // If this was the system wallpaper, rebind... bindWallpaperComponentLocked(mImageWallpaper, true, false, wallpaper, null); notifyColorsWhich |= FLAG_SYSTEM; } if (lockWallpaperChanged || (wallpaper.whichPending & FLAG_LOCK) != 0) { if (DEBUG) { Slog.i(TAG, "Lock-relevant wallpaper changed"); } // either a lock-only wallpaper commit or a system+lock event. // if it's system-plus-lock we need to wipe the lock bookkeeping; // we're falling back to displaying the system wallpaper there. if (!lockWallpaperChanged) { mLockWallpaperMap.remove(wallpaper.userId); } // and in any case, tell keyguard about it notifyLockWallpaperChanged(); notifyColorsWhich |= FLAG_LOCK; } saveSettingsLocked(wallpaper.userId); // Publish completion *after* we've persisted the changes if (wallpaper.setComplete != null) { try { wallpaper.setComplete.onWallpaperChanged(); } catch (RemoteException e) { // if this fails we don't really care; the setting app may just // have crashed and that sort of thing is a fact of life. } } } } } } // Outside of the lock since it will synchronize itself if (notifyColorsWhich != 0) { notifyWallpaperColorsChanged(wallpaper, notifyColorsWhich); } } }

notifyLockWallpaperChanged 中执行 cb.onWallpaperChanged(); 这里面的 cb 就是 LockscreenWallpaper ,系统开机时在 WallpaperManager setLockWallpaperCallback 中注册的,LockscreenWallpaper 中具体执行在其 run() 函数,完成锁屏壁纸替换操作。

notifyWallpaperColorsChanged 中最终调用到 notifyWallpaperColorsChangedOnDisplay() ,在通知各种注册了监听的模块。具体内容如下问题分析。

二、Android R 版本上设置壁纸时会看到桌面闪烁问题

原因是 Launcher WallpaperColorInfo.java 中添加了 mWallpaperManager.addOnColorsChangedListener 。监听壁纸变化时会根据壁纸颜色的不同,设置不同的 style 然后 recreate() Launcher 。在设置时壁纸,WallpaperManagerService 会先发一次 WallpaperColors 为 null 的,然后才执行 extractColors() 去获取 WallpaperColors 重新发送一次。导致 Launcher 会短时间 recreate() 两次,所以闪烁明显,把系统这里修改掉即可。

private void notifyWallpaperColorsChangedOnDisplay(@NonNull WallpaperData wallpaper, int which, int displayId) { boolean needsExtraction; synchronized (mLock) { final RemoteCallbackList currentUserColorListeners = getWallpaperCallbacks(wallpaper.userId, displayId); final RemoteCallbackList userAllColorListeners = getWallpaperCallbacks(UserHandle.USER_ALL, displayId); // No-op until someone is listening to it. if (emptyCallbackList(currentUserColorListeners) && emptyCallbackList(userAllColorListeners)) { return; } if (DEBUG) { Slog.v(TAG, "notifyWallpaperColorsChangedOnDisplay " + which); } needsExtraction = wallpaper.primaryColors == null; } // Let's notify the current values, it's fine if it's null, it just means // that we don't know yet. // notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId, displayId); if (needsExtraction) { extractColors(wallpaper); synchronized (mLock) { // Don't need to notify if nothing changed. if (wallpaper.primaryColors == null) { Slog.e(TAG, "extract colors is NULL"); // return; } } } notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId, displayId); }

把 notifyColorListeners 移到提取颜色后再调用。这样避免多次调用发送,目前从代码逻辑和实际验证来看,没发现原生设计的意义,发送两次好像没啥用,对于 live wallpaper 来说也是一样的发 null 而已。这块后面又发现再补充。有知道的大佬可以留言下,感谢。

三、影响CTS测试

国内版本可以直接按照上述修改,如果出海外,上述修改会导致 WallpaperManagerTest 模块失败。cts源码中对这里注册了监听,记录设置壁纸后系统发出通知的次数,上述修改注释了一条 notifyColorListeners 通知为null的步骤,故而系统发出的通知少了。cts检测部分代码如下:

private void ensureCleanState() { Bitmap bmp = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); // We expect 5 events to happen when we change a wallpaper: // • Wallpaper changed // • System colors are null // • Lock colors are null // • System colors are known // • Lock colors are known final int expectedEvents = 5; mCountDownLatch = new CountDownLatch(expectedEvents); if (DEBUG) { Log.d(TAG, "Started latch expecting: " + mCountDownLatch.getCount()); } WallpaperManager.OnColorsChangedListener callback = (colors, which) -> { if ((which & WallpaperManager.FLAG_LOCK) != 0) { mCountDownLatch.countDown(); } if ((which & WallpaperManager.FLAG_SYSTEM) != 0) { mCountDownLatch.countDown(); } if (DEBUG) { Log.d(TAG, "color state count down: " + which + " - " + colors); } }; mWallpaperManager.addOnColorsChangedListener(callback, mHandler); try { mWallpaperManager.setBitmap(bmp); // Wait for up to 10 sec since this is an async call. // Will pass as soon as the expected callbacks are executed. Assert.assertTrue(mCountDownLatch.await(10, TimeUnit.SECONDS)); Assert.assertEquals(0, mCountDownLatch.getCount()); } catch (InterruptedException | IOException e) { throw new RuntimeException("Can't ensure a clean state."); } finally { mWallpaperManager.removeOnColorsChangedListener(callback); bmp.recycle(); } }



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3