一、设置壁纸流程

系统提供了相应的 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() 。再去通知对应的模块通过

        @Overridepublic 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 filefinal 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 itnotifyLockWallpaperChanged();notifyColorsWhich |= FLAG_LOCK;}saveSettingsLocked(wallpaper.userId);// Publish completion *after* we've persisted the changesif (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 itselfif (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<IWallpaperManagerCallback> currentUserColorListeners =getWallpaperCallbacks(wallpaper.userId, displayId);final RemoteCallbackList<IWallpaperManagerCallback> 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 knownfinal 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();}}

Android R 设置壁纸流程和 Launcher 闪烁问题相关推荐

  1. Android 10 设置壁纸流程

    ---------------------------------------------------------------------------------------------------- ...

  2. Android R WiFi热点流程浅析

    Android R WiFi热点流程浅析 Android上的WiFi SoftAp功能是用户常用的功能之一,它能让我们分享手机的网络给其他设备使用. 那Android系统是如何实现SoftAp的呢,这 ...

  3. Android Wallpaper之设置壁纸流程

    What? 什么是壁纸? android wallpaper包括锁屏壁纸和桌面壁纸,壁纸又区分静态和动态两种.我们每天使用手机第一眼看到的就是壁纸,好看的壁纸对于手机的颜值也有大大的提升(滑稽),就让 ...

  4. android蓝牙设置名称流程,Android 8 设置蓝牙名称 流程

    记录android 8设置蓝牙名称的流程.java packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothDeviceR ...

  5. Android 设置壁纸流程

    W/InputManager-JNI: Input channel object '7d2a123 com.android.wallpaperpicker/com.android.wallpaperp ...

  6. android 壁纸设置成功,在Android上设置壁纸刷新

    我正在尝试更改当前的用户壁纸. 我已经设置了set_wallpaper权限,但它确实有效. 但是当我改变壁纸时,我必须等待大约15秒才能看到壁纸的变化. 这是很奇怪的,因为如果我检查锁定屏幕它已经改变 ...

  7. android 设置壁纸,Android 代码设置壁纸的方式,兼容各大ROM

    废话不多说,主要代码: public static void intent2SetWallPaper(Context context, String path) { Uri uriPath = get ...

  8. android 壁纸服务,Android开发学习之WallPaper设置壁纸详细介绍与实例

    今天和大家分享的是关于在android中设置壁纸的方法,在android中设置壁纸的方法有三种,分别是: 1.使用wallpapermanager的setresource(int resourceid ...

  9. android 5.1 壁纸路径,RTFSC – Android5.1 壁纸设置流程简析 – RustFisher

    Android5.1 壁纸设置流程浅析 Ubuntu14.04  Android5.1  Source Insight3 这里只是简单分析一下5.1里是如何设置壁纸的:这个流程和4.4有一些不同.但基 ...

  10. Android Launcher 设置壁纸

    版本:1.0  日期:2014.11.25 2014.11.26 版权:©kince 特别推荐:泡在网上的日子 一.概述 一般Launcher都带有壁纸设置的功能,Android提供了设置壁纸的API ...

最新文章

  1. JS Range 对象的使用
  2. mysql8.0用mybatis哪个版本_SpringBoot整合MyBatis与MySql8.0
  3. python【蓝桥杯vip练习题库】BASIC-18 矩形面积交(线段交)
  4. Java中的三目运算符
  5. Python中的线程、进程、协程以及区别
  6. 华为敏捷DevOps实践:如何从Excel管理软件的方式中走出来
  7. php框架之laravel
  8. NumPy Cookbook 带注释源码 六、NumPy 特殊数组与通用函数
  9. python字符串转64位数字_python-将String转换为64位整数映射字符以自定义两位值映射...
  10. 《播客》项目总结——web标准页面设计方面(转)
  11. html dom之iframe对象
  12. vlan未能连接服务器,PC单机局域网连接VLAN的方法
  13. lookup基础用法
  14. Android Debug Bridge
  15. java题目练习笔记,java 20道、数量关系8道、判断推理8道、智力题8道
  16. 不想失业?你得学会为自己工作
  17. 类似婚礼纪的Java项目_「婚礼纪」婚礼纪 java面试 - seo实验室
  18. 通过证书管理解决无法连接 Citrix XenApp SSL 61 您还未选择信任证书颁发者的问题
  19. Pytorch SoftMax回归
  20. 在Android平台上搭建Qualcomm的FastCv

热门文章

  1. 使用皮卡(pika)操作RabbitMQ
  2. 男人也离不开维生素(转)
  3. Dashboard Design 4.0(Xcelsius)数据直接绑定功能:瑕瑜互见
  4. 物联网环境下信息安全问题与对策
  5. 关于sql server中isnull(值,值)是什么意思
  6. Protell99中的铺铜设置
  7. 编程高手与IT民工的区别在哪?
  8. problems encountered during text search
  9. Solidity ERC777标准
  10. 如何入门多视角人脸正面化生成?不得不看的超详细最新综述!