背景
大家可能对于壁纸部分都默认以为是桌面壁纸,一直对于锁屏壁纸这块其实比较陌生的。其实锁屏壁纸也是可以进行单独的设置,具体设置方式如下:

这里在设置的壁纸中就有壁纸设置的入口,选择正确壁纸图片,然后可以选着设置锁屏还是桌面壁纸。注意分别进行桌面和锁屏的差异图片设置。
锁屏壁纸显示如下:

桌壁纸显示如下:

今天针对锁屏壁纸显示及设置原理等进行详细的剖析。
锁屏壁纸设置部分
frameworks/base/core/java/android/app/WallpaperManager.java
public int setStream(InputStream bitmapData, Rect visibleCropHint,
boolean allowBackup, @SetWallpaperFlags int which)
throws IOException {
final Bundle result = new Bundle();
final WallpaperSetCompletion completion = new WallpaperSetCompletion();
try {
//核心方法调用到WallpaperManagerService,获取fd
ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null,
mContext.getOpPackageName(), visibleCropHint, allowBackup,
result, which, completion, mContext.getUserId());
if (fd != null) {
FileOutputStream fos = null;
try {
//获取fd往fd中写入壁纸图片数据流
fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
copyStreamToWallpaperFile(bitmapData, fos);
fos.close();
completion.waitForCompletion();
} finally {
IoUtils.closeQuietly(fos);
}
}
return result.getInt(EXTRA_NEW_WALLPAPER_ID, 0);
}
这块客户端设置壁纸都是WallpaperManagersetStream方法,桌面和锁屏就通过这个which参数进行的。
flag参数一共2个:
/**
* Flag: set or retrieve the general system wallpaper.
*/
public static final int FLAG_SYSTEM = 1 << 0;
/**
* Flag: set or retrieve the lock-screen-specific wallpaper.
*/
public static final int FLAG_LOCK = 1 << 1;
app触发setStream的调用堆栈
setStream:1744, WallpaperManager (android.app)
invoke:-1, Method (java.lang.reflect)
setStream:25, WallpaperManagerCompatVN (com.android.wallpaperpicker.common)
doInBackground:64, FileWallpaperInfo$2 (com.android.wallpaperpicker.tileinfo)
call:394, AsyncTask$3 (android.os)
run:264, FutureTask (java.util.concurrent)
run:305, AsyncTask$SerialExecutor$1 (android.os)
runWorker:1137, ThreadPoolExecutor (java.util.concurrent)
run:637, ThreadPoolExecutor$Worker (java.util.concurrent)
run:1012, Thread (java.lang)
这里其实本质调用到了服务端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) {
synchronized (mLock) {
wallpaper = getWallpaperSafeLocked(userId, which);
final long ident = Binder.clearCallingIdentity();
try {
//核心方法就是updateWallpaperBitmapLocked,根据which获取对应Wallpaper文件名字
ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper, extras);
return pfd;
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
看看核心方法1,getWallpaperSafeLocked,主要就是new出锁屏壁纸图片对应的File
WallpaperData getWallpaperSafeLocked(int userId, int which) {
// We're setting either just system (work with the system wallpaper),
// both (also work with the system wallpaper), or just the lock
// wallpaper (update against the existing lock wallpaper if any).
// Combined or just-system operations use the 'system' WallpaperData
// for this use; lock-only operations use the dedicated one.
final SparseArray<WallpaperData> whichSet =
(which == FLAG_LOCK) ? mLockWallpaperMap : mWallpaperMap;
WallpaperData wallpaper = whichSet.get(userId);
if (wallpaper == null) {
if (which == FLAG_LOCK) {
//传递对应WALLPAPER_LOCK_ORIG,WALLPAPER_LOCK_CROP文件名字到WallpaperData构造方法中
wallpaper = new WallpaperData(userId, getWallpaperDir(userId),
WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
mLockWallpaperMap.put(userId, wallpaper);
ensureSaneWallpaperData(wallpaper);
}
}
return wallpaper;
}
WallpaperData(int userId, File wallpaperDir, String inputFileName, String cropFileName) {
this.userId = userId;
//根据名字创建对应的文件
wallpaperFile = new File(wallpaperDir, inputFileName);
cropFile = new File(wallpaperDir, cropFileName);
}
File getWallpaperDir(int userId) {
//目录为/data/system/users/0
return Environment.getUserSystemDirectory(userId);
}
@Deprecated
public static File getUserSystemDirectory(int userId) {
return new File(new File(getDataSystemDirectory(), "users"), Integer.toString(userId));
}
这里主要就是到DataSystem目录获取对应的目录/data/system/users/0,加上锁屏壁纸对应的文件名字wallpaper_lock_orig,wallpaper_lock,构造出对应的File文件对象。
看看核心方法2,updateWallpaperBitmapLocked
ParcelFileDescriptor updateWallpaperBitmapLocked(String name, WallpaperData wallpaper,
Bundle extras) {
if (name == null) name = "";
try {
File dir = getWallpaperDir(wallpaper.userId);
ParcelFileDescriptor fd = ParcelFileDescriptor.open(wallpaper.wallpaperFile,
MODE_CREATE|MODE_READ_WRITE|MODE_TRUNCATE);
wallpaper.name = name;
wallpaper.wallpaperId = makeWallpaperIdLocked();
return fd;
} catch (FileNotFoundException e) {
Slog.w(TAG, "Error setting wallpaper", e);
}
return null;
}
到此服务端返回了fd后就业务完成,下面就是跨进程传递fd客户端,客户端拿到了fd后进行数据写入,这块回到最开头的WallpaperManager.java的setStream方法既可以。
也可以通过adb shell看到如下锁屏壁纸文件情况


锁屏壁纸是显示部分剖析
采用布局工具调研部分
aosp13抓取systemui锁屏窗口的view结构情况:

代码部分剖析
aosp13上锁屏壁纸的情况
堆栈追踪
首次开机进行bitmap的获取部分
LockScreenWallpaper loadBitmap
java.lang.Exception
at com.android.systemui.statusbar.phone.LockscreenWallpaper.loadBitmap(LockscreenWallpaper.java:140)
at com.android.systemui.statusbar.phone.LockscreenWallpaper.getBitmap(LockscreenWallpaper.java:113)
at com.android.systemui.statusbar.NotificationMediaManager.finishUpdateMediaMetaData(NotificationMediaManager.java:652)
at com.android.systemui.statusbar.NotificationMediaManager.updateMediaMetaData(NotificationMediaManager.java:636)
at com.android.systemui.statusbar.phone.StatusBarNotificationPresenter.updateMediaMetaData(StatusBarNotificationPresenter.java:397)
at com.android.systemui.statusbar.phone.StatusBarNotificationPresenter.onUserSwitched(StatusBarNotificationPresenter.java:356)
at com.android.systemui.statusbar.phone.StatusBarNotificationPresenter.lambda$new$0(StatusBarNotificationPresenter.java:228)
at com.android.systemui.statusbar.phone.StatusBarNotificationPresenter.$r8$lambda$pbvP-uxBrWeKHzZ8Oz-h6PVPHRI(Unknown Source:0)
at com.android.systemui.statusbar.phone.StatusBarNotificationPresenter$$ExternalSyntheticLambda0.run(Unknown Source:8)
at com.android.systemui.InitController.executePostInitTasks(InitController.java:57)
at com.android.systemui.SystemUIApplication.startServicesIfNeeded(SystemUIApplication.java:276)
at com.android.systemui.SystemUIApplication.startServicesIfNeeded(SystemUIApplication.java:201)
at com.android.systemui.SystemUIService.onCreate(SystemUIService.java:70)
at android.app.ActivityThread.handleCreateService(ActivityThread.java:4515)
at android.app.ActivityThread.-$$Nest$mhandleCreateService(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2162)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7898)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
LockScreenWallpaper被设置更新后,也会回调通知到更新重新进行loadBitmap
LockScreenWallpaper loadBitmap
java.lang.Exception
at com.android.systemui.statusbar.phone.LockscreenWallpaper.loadBitmap(LockscreenWallpaper.java:140)
at com.android.systemui.statusbar.phone.LockscreenWallpaper$1.doInBackground(LockscreenWallpaper.java:212)
at com.android.systemui.statusbar.phone.LockscreenWallpaper$1.doInBackground(LockscreenWallpaper.java:209)
at android.os.AsyncTask$3.call(AsyncTask.java:394)
at java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637)
at java.lang.Thread.run(Thread.java:1012)
壁纸消失部分:
lambda$finishUpdateMediaMetaData$1:768, NotificationMediaManager (com.android.systemui.statusbar)
run:-1, NotificationMediaManager$$ExternalSyntheticLambda3 (com.android.systemui.statusbar)
onAnimationEnd:1120, ViewPropertyAnimator$AnimatorEventListener (android.view)
:-1, Animator$AnimatorListener (android.animation)
endAnimation:1333, ValueAnimator (android.animation)
doAnimationFrame:1575, ValueAnimator (android.animation)
doAnimationFrame:307, AnimationHandler (android.animation)
doFrame:86, AnimationHandler$1 (android.animation)
run:1229, Choreographer$CallbackRecord (android.view)
run:1239, Choreographer$CallbackRecord (android.view)
doCallbacks:899, Choreographer (android.view)
doFrame:827, Choreographer (android.view)
run:1214, Choreographer$FrameDisplayEventReceiver (android.view)
handleCallback:942, Handler (android.os)
dispatchMessage:99, Handler (android.os)
loopOnce:201, Looper (android.os)
loop:288, Looper (android.os)
main:7898, ActivityThread (android.app)
invoke:-1, Method (java.lang.reflect)
run:548, RuntimeInit$MethodAndArgsCaller (com.android.internal.os)
main:936, ZygoteInit (com.android.internal.os)
核心代码讲解
这块锁屏壁纸核心代码部分如下:
private void finishUpdateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation,
@Nullable Bitmap bmp) {
//省略一些非主要影响逻辑
Bitmap lockWallpaper =
mLockscreenWallpaper != null ? mLockscreenWallpaper.getBitmap() : null;
if (lockWallpaper != null) {
artworkDrawable = new LockscreenWallpaper.WallpaperDrawable(
mBackdropBack.getResources(), lockWallpaper);
// We're in the SHADE mode on the SIM screen - yet we still need to show
// the lockscreen wallpaper in that mode.
allowWhenShade = mStatusBarStateController.getState() == KEYGUARD;
}
}
//显示锁屏壁纸条件,最重要是allowWhenShade
if (/*省略部分*/(mStatusBarStateController.getState() != StatusBarState.SHADE || allowWhenShade)
) {
//mBackdrop设置成可见
if (mBackdrop.getVisibility() != View.VISIBLE) {
mBackdrop.setVisibility(View.VISIBLE);
}
// mBackdropBack设置image也就是锁屏壁纸
mBackdropBack.setImageDrawable(artworkDrawable);
} else{
//隐藏锁屏壁纸条件
mBackdrop.setVisibility(View.GONE);
mBackdropBack.setImageDrawable(null);
}
}
原文地址:
https://mp.weixin.qq.com/s/EYsfM4wcZ5mdndUUHyT0YA
更多framework实战开发干货,请关注“千里马学框架”


被折叠的 条评论
为什么被折叠?



