Android 10.0 锁屏壁纸 LockscreenWallpaper

前言

一、设置壁纸

通过系统设置进行锁屏壁纸和桌面壁纸的设置。
Setting 部分的代码:
packages/apps/WallpaperPicker2/src/com/android/wallpaper/module/DefaultWallpaperPersister.java

    private int setStreamToWallpaperManagerCompat(InputStream inputStream, boolean allowBackup,int whichWallpaper) {try {// whichWallpaper  // 壁纸类型return mWallpaperManagerCompat.setStream(inputStream, null, allowBackup,whichWallpaper);} catch (IOException e) {return 0;}}...
//    int whichWallpaper;    // 壁纸类型
//    if (mDestination == DEST_HOME_SCREEN) {    // 桌面壁纸
//        whichWallpaper = WallpaperManagerCompat.FLAG_SYSTEM;
//    } else if (mDestination == DEST_LOCK_SCREEN) {  // 锁屏壁纸
//        whichWallpaper = WallpaperManagerCompat.FLAG_LOCK;
//    } else { // DEST_BOTH    // 桌面壁纸 和 锁屏壁纸//       whichWallpaper = WallpaperManagerCompat.FLAG_SYSTEM
//                | WallpaperManagerCompat.FLAG_LOCK;
//    }...

mWallpaperManagerCompat 其实就是 WallpaperManagerCompatV16 的对象。
packages/apps/WallpaperPicker2/src/com/android/wallpaper/compat/WallpaperManagerCompatV16.java

    @Overridepublic int setStream(InputStream data, Rect visibleCropHint, boolean allowBackup,int whichWallpaper) throws IOException {mWallpaperManager.setStream(data);// Return a value greater than zero to indicate success.return 1;}

由此可知,壁纸的设置是通过 WallpaperManager 类来进行的。

二、锁屏壁纸的显示

锁屏壁纸显示流程图:

上面应用程序设置完成了,下面就该进行壁纸显示了。
WallpaperManager#setStream()
frameworks/base/core/java/android/app/WallpaperManager.java

    public int setStream(InputStream bitmapData, Rect visibleCropHint,boolean allowBackup, @SetWallpaperFlags int which)throws IOException {// 省略部分代码......try {//sGlobals.mService即 WallpaperManagerService。// WallpaperManager 在 SystemServiceRegistry 实例化,// 过程中传入 WallpaperManagerService 的 binder 对象。   ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null,mContext.getOpPackageName(), visibleCropHint, allowBackup,result, which, completion, mContext.getUserId());if (fd != null) {FileOutputStream fos = null;try {// 将壁纸copy一份并存储到对应目录,// 默认是/data/system/users/0/wallpaper(或wallpaper_lock),// 其中0是主用户的userId,支持多用户fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);copyStreamToWallpaperFile(bitmapData, fos);fos.close();completion.waitForCompletion();} finally {IoUtils.closeQuietly(fos);}}} catch (RemoteException e) {throw e.rethrowFromSystemServer();}return result.getInt(EXTRA_NEW_WALLPAPER_ID, 0);}

这里注意两个方法:sGlobals.mService.setWallpaper()和fos.close()。

先看第一个 WallpaperManagerService#setWallpaper() 方法:
frameworks/base/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java

    public ParcelFileDescriptor setWallpaper(String name, String callingPackage,Rect cropHint, boolean allowBackup, Bundle extras, int which,IWallpaperManagerCallback completion, int userId) {userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,false /* all */, true /* full */, "changing wallpaper", null /* pkg */);// 检查有没有设置壁纸的权限checkPermission(android.Manifest.permission.SET_WALLPAPER);//调用setStream方法的时候参数which必须是正确的if ((which & (FLAG_LOCK|FLAG_SYSTEM)) == 0) {final String msg = "Must specify a valid wallpaper category to set";Slog.e(TAG, msg);throw new IllegalArgumentException(msg);}// 省略部分代码......synchronized (mLock) {if (DEBUG) Slog.v(TAG, "setWallpaper which=0x" + Integer.toHexString(which));WallpaperData wallpaper;//如果当前没有锁屏壁纸的话,并且是设置桌面壁纸即which == FLAG_SYSTEM,那么同时设置为锁屏壁纸if (which == FLAG_SYSTEM && mLockWallpaperMap.get(userId) == null) {migrateSystemToLockWallpaperLocked(userId);}wallpaper = getWallpaperSafeLocked(userId, which);final long ident = Binder.clearCallingIdentity();try {// updateWallpaperBitmapLocked() 将创建一个文件描述符ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper, extras);if (pfd != null) {wallpaper.imageWallpaperPending = true;wallpaper.whichPending = which;wallpaper.setComplete = completion;wallpaper.cropHint.set(cropHint);wallpaper.allowBackup = allowBackup;}return pfd;} finally {Binder.restoreCallingIdentity(ident);}}}

这里再跟进一步,看下 updateWallpaperBitmapLocked() 方法:
frameworks/base/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java

    ParcelFileDescriptor updateWallpaperBitmapLocked(String name, WallpaperData wallpaper,Bundle extras) {if (name == null) name = "";try {// 通过getWallpaperDir() 获取文件路径;这个方法值得注意:后面会讲到。File dir = getWallpaperDir(wallpaper.userId);if (!dir.exists()) {dir.mkdir();FileUtils.setPermissions(dir.getPath(),FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,-1, -1);}// 创建一个文件描述符,并返回。ParcelFileDescriptor fd = ParcelFileDescriptor.open(wallpaper.wallpaperFile,MODE_CREATE|MODE_READ_WRITE|MODE_TRUNCATE);// 省略部分代码......return fd;} catch (FileNotFoundException e) {Slog.w(TAG, "Error setting wallpaper", e);}return null;}

这里再看 fos.close() ,这个方法本身没什么可以看的,就是 FileOutputStream 文件字节输出流结束。但是这里涉及到了 WallpaperManagerService 的一个内部类 WallpaperObserver,通过名字我们就能知道它是一个观察者。
WallpaperObserver 初始化:在 WallpaperManagerService 初始化时,会调用 systemReady() 通过getWallpaperSafeLocked()方法初始化 WallpaperData,而这个 WallpaperData 中有个变量 wallpaperObserver ,也在开机时服务初始化, systemReady() 中调用 switchUser() 执行了 wallpaperObserver.startWatching()。
WallpaperObserver 这个内部类的作用:观察壁纸的变化并通知所有 IWallpaperServiceCallbacks 壁纸已经改变。 CREATE 在没有设置壁纸时触发,并且是第一次创建。每次更改壁纸时都会触发 CLOSE_WRITE,这也是关注 fos.close() 的原因。
所以文件的变化触发 WallpaperObserver 的 onEvent() :
frameworks/base/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java

        @Overridepublic void onEvent(int event, String path) {if (path == null) {return;}final boolean moved = (event == MOVED_TO);final boolean written = (event == CLOSE_WRITE || moved);// 获取发生了 CLOSE_WRITE 事件的文件路径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 (moved && lockWallpaperChanged) {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) {SELinux.restorecon(changedFile);if (moved) {loadSettingsLocked(wallpaper.userId, true);}generateCrop(wallpaper);wallpaper.imageWallpaperPending = false;if (sysWallpaperChanged) {// 桌面壁纸变化,那么bind ImageWallpaper,ImageWallpaper是负责显示静态桌面壁纸的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");}if (!lockWallpaperChanged) {//如果参数which是system+lock,也就是同时设置锁屏和桌面壁纸,那么remove锁屏壁纸,因为已经是同一张壁纸了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()
frameworks/base/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java

    private void notifyLockWallpaperChanged() {final IWallpaperManagerCallback cb = mKeyguardListener;if (cb != null) {try {cb.onWallpaperChanged();} catch (RemoteException e) {// Oh well it went away; no big deal}}}@Overridepublic boolean setLockWallpaperCallback(IWallpaperManagerCallback cb) {checkPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW);synchronized (mLock) {mKeyguardListener = cb;}return true;}

notifyLockWallpaperChanged 中执行 cb.onWallpaperChanged();这里的 cb = mKeyguardListener,而 mKeyguardListener 在 setLockWallpaperCallback() 方法中得到。 跟进我们发现 cb 其实就是 LockscreenWallpaper 引用,在 LockscreenWallpaper 的构造方法里赋值调用:
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java

    @Injectpublic LockscreenWallpaper(WallpaperManager wallpaperManager,@Nullable IWallpaperManager iWallpaperManager,KeyguardUpdateMonitor keyguardUpdateMonitor,DumpManager dumpManager,NotificationMediaManager mediaManager,@Main Handler mainHandler) {// 省略部分代码......if (iWallpaperManager != null) {// Service is disabled on some devices like Automotivetry {// iWallpaperManager 是 WallpaperManagerService 的 binder对象,// 通过 dagger 在 SystemServicesModule 实例化。iWallpaperManager.setLockWallpaperCallback(this);} catch (RemoteException e) {Log.e(TAG, "System dead?" + e);}}}

所以当锁屏壁纸更新时,就会回调到 LockscreenWallpaper#onWallpaperChanged()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java

    @Overridepublic void onWallpaperChanged() {// Called on Binder thread.postUpdateWallpaper();}private void postUpdateWallpaper() {if (mH == null) {Log.wtfStack(TAG, "Trying to use LockscreenWallpaper before initialization.");return;}mH.removeCallbacks(this);mH.post(this);}

而 LockscreenWallpaper 类实现了 Runnable 接口的,所以看下它的 run() 方法; LockscreenWallpaper#run()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java

    @Overridepublic void run() {// Called in response to onWallpaperChanged on the main thread.if (mLoader != null) {mLoader.cancel(false /* interrupt */);}final int currentUser = mCurrentUserId;final UserHandle selectedUser = mSelectedUser;mLoader = new AsyncTask<Void, Void, LoaderResult>() {@Overrideprotected LoaderResult doInBackground(Void... params) {return loadBitmap(currentUser, selectedUser);}@Overrideprotected void onPostExecute(LoaderResult result) {super.onPostExecute(result);if (isCancelled()) {return;}if (result.success) {mCached = true;mCache = result.bitmap;mUpdateMonitor.setHasLockscreenWallpaper(result.bitmap != null);// 通知StatusBar更新壁纸mMediaManager.updateMediaMetaData(true /* metaDataChanged */, true /* allowEnterAnimation */);}mLoader = null;}}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);}

异步获取壁纸,并通知StatusBar去更新壁纸。
NotificationMediaManager#updateMediaMetaData()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java

    public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {Trace.beginSection("StatusBar#updateMediaMetaData");// 省略部分代码......Bitmap artworkBitmap = null;if (mediaMetadata != null && !mKeyguardBypassController.getBypassEnabled()) {artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);if (artworkBitmap == null) {artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);}}//在后台线程上处理图稿并将生成的位图发送到finishUpdateMediaMetaData。if (metaDataChanged) {for (AsyncTask<?, ?, ?> task : mProcessArtworkTasks) {task.cancel(true);}mProcessArtworkTasks.clear();}if (artworkBitmap != null && !Utils.useQsMediaPlayer(mContext)) {mProcessArtworkTasks.add(new ProcessArtworkTask(this, metaDataChanged,allowEnterAnimation).execute(artworkBitmap));} else {finishUpdateMediaMetaData(metaDataChanged, allowEnterAnimation, null);}Trace.endSection();}

对锁屏壁纸所在 view 做 setImageBitmap。
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java

    private void finishUpdateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation,@Nullable Bitmap bmp) {Drawable artworkDrawable = null;if (bmp != null) {artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), bmp);}// 省略部分代码......if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)&& (mStatusBarStateController.getState() != StatusBarState.SHADE || allowWhenShade)&&  mBiometricUnlockController != null && mBiometricUnlockController.getMode()!= BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING&& !hideBecauseOccluded) {// 省略部分代码......if (metaDataChanged) {if (mBackdropBack.getDrawable() != null) {Drawable drawable =mBackdropBack.getDrawable().getConstantState().newDrawable(mBackdropFront.getResources()).mutate();// 设置壁纸 setImageDrawable()mBackdropFront.setImageDrawable(drawable);mBackdropFront.setAlpha(1f);mBackdropFront.setVisibility(View.VISIBLE);} else {mBackdropFront.setVisibility(View.INVISIBLE);}if (DEBUG_MEDIA_FAKE_ARTWORK) {final int c = 0xFF000000 | (int)(Math.random() * 0xFFFFFF);Log.v(TAG, String.format("DEBUG_MEDIA: setting new color: 0x%08x", c));mBackdropBack.setBackgroundColor(0xFFFFFFFF);mBackdropBack.setImageDrawable(new ColorDrawable(c));} else {mBackdropBack.setImageDrawable(artworkDrawable);}if (mBackdropFront.getVisibility() == View.VISIBLE) {if (DEBUG_MEDIA) {Log.v(TAG, "DEBUG_MEDIA: Crossfading album artwork from "+ mBackdropFront.getDrawable()+ " to "+ mBackdropBack.getDrawable());}mBackdropFront.animate().setDuration(250).alpha(0f).withEndAction(mHideBackdropFront);}}} else {// 省略部分代码......}}

通过 mBackdropFront.setImageDrawable(drawable) 方法将图片设置进去,完成锁屏壁纸的更新。
mBackdropFront 在 NotificationMediaManager的 setup() 方法被赋值,而 setup() 方法在 StatusBar 的 makeStatusBarView() 中被调用初始化。
StatusBar#makeStatusBarView()
frameworks/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java

    protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {// 省略部分代码......mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front),backdrop.findViewById(R.id.backdrop_back), mScrimController, mLockscreenWallpaper);// 省略部分代码......}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/465459.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RBAC权限控制实现方案

上一文章讲述了利用RBAC实现访问控制的思路&#xff08;RBAC实现思路&#xff09;&#xff0c;本文主要详细讲解利用vuex实现RBAC权限控制。 一、准备工作 从后台获取到权限对照表&#xff0c;如下&#xff1a; 1、添加/编辑楼宇 park:building:add_edit 2、楼宇管理 pa…

测试编码规范

0.测试代码和业务代码要分离 把测试代码和业务代码放进各自的所属的"盒子"中&#xff0c;互不干扰 Q:为什么要分离? 分门别类&#xff0c;避免混乱&#xff0c;方便维护 不在试卷上打草稿而是专门准备草稿纸 没人会在客厅做饭吧&#xff0c;不然要厨房干什么 Q:如…

BootstrapBlazor 模板适配移动设备使用笔记

项目模板 Bootstrap Blazor App 模板 为了方便大家利用这套组件快速搭建项目&#xff0c;作者制作了 项目模板&#xff08;Project Templates&#xff09;&#xff0c;使用 dotnet new 命令行模式&#xff0c;使用步骤如下&#xff1a; 安装项目模板 dotnet new install Boo…

第四节 zookeeper集群与分布式锁

目录 1. Zookeeper集群操作 1.1 客户端操作zk集群 1.2 模拟集群异常操作 1.3 curate客户端连接zookeeper集群 2. Zookeeper实战案例 2.1 创建项目引入依赖 2.2 获取zk客户端对象 2.3 常用API 2.4 客户端向服务端写入数据流程 2.5 服务器动态上下线、客户端动态监听 2…

寒假 day10

1、请使用递归实现n! #include<stdio.h> #include<string.h> #include<stdlib.h>int fun(int m) {if(m0)return 1;else{return m*fun(m-1);} } int main(int argc, const char *argv[]) {int m;printf("please enter m:");scanf("%d",…

linux 08 文件查找

02. 第一. alias&#xff1a;起别名(可以输入别名就可以执行对应的命令)&#xff0c;语法&#xff1a;alias 别名‘ls -l’ 第二. locate&#xff1a; locate 找不到最近的文件 更新locate 后 find命令&#xff1a; find&#xff1a; find 路径 选项 文件名&#x…

ZigBee学习——在官方例程实现组网

✨Z-Stack版本&#xff1a;3.0.2 ✨IAR版本&#xff1a;10.10.1 ✨这篇博客是在善学坊BDB组网实验的基础上进行完善&#xff0c;并指出实现的过程中会出现的各种各样的问题&#xff01; 善学坊教程地址&#xff1a; ZigBee3.0 BDB组网实验 文章目录 一、基础工程选择二、可能遇…

C++入门学习(二十七)跳转语句—continue语句

当在循环中遇到continue语句时&#xff0c;它会跳过当前迭代剩余的代码块&#xff0c;并立即开始下一次迭代。这意味着continue语句用于跳过循环中特定的执行步骤&#xff0c;而不是完全终止循环。 直接看一下下面的代码更清晰&#xff1a; 与上一节的break语句可以做一下对比…

CentOS 7.9安装Tesla M4驱动、CUDA和cuDNN

正文共&#xff1a;1333 字 21 图&#xff0c;预估阅读时间&#xff1a;2 分钟 上次我们在Windows上尝试用Tesla M4配置深度学习环境&#xff08;TensorFlow识别GPU难道就这么难吗&#xff1f;还是我的GPU有问题&#xff1f;&#xff09;&#xff0c;但是失败了。考虑到Windows…

python+vue+django体育场地器材预约管理系统dyn9h

技术栈 后端&#xff1a;python 前端&#xff1a;vue.jselementui 框架&#xff1a;django Python版本&#xff1a;python3.7 数据库&#xff1a;mysql5.7 数据库工具&#xff1a;Navicat 开发软件&#xff1a;PyCharm .体育馆管理系统有管理员和用户两个角色。用户功能有场地…

算法沉淀——位运算(leetcode真题剖析)

算法沉淀——位运算 常用位运算总结1.基础位运算2.确定一个数中第x位是0还是13.将一个数的第x位改成14.将一个数的第x位改成05.位图6.提取一个数最右边的17.删掉一个数最右边的18.异或运算9.基础例题 力扣题目讲解01.面试题 01.01. 判定字符是否唯一02.丢失的数字03.两整数之和…

给定具体日期 返回给定日期是星期几 calendar.weekday(year,month,day)

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 给定具体日期 返回给定日期是星期几 calendar.weekday(year,month,day) [太阳]选择题 如果2024年2月12日是星期一&#xff0c;请问最后一个print语句的运行结果是&#xff1f; import calenda…