干货分享:aosp13锁屏壁纸部分原理剖析

背景

大家可能对于壁纸部分都默认以为是桌面壁纸,一直对于锁屏壁纸这块其实比较陌生的。其实锁屏壁纸也是可以进行单独的设置,具体设置方式如下:
在这里插入图片描述
这里在设置的壁纸中就有壁纸设置的入口,选择正确壁纸图片,然后可以选着设置锁屏还是桌面壁纸。注意分别进行桌面和锁屏的差异图片设置。

锁屏壁纸显示如下:
在这里插入图片描述
桌壁纸显示如下:
在这里插入图片描述

今天针对锁屏壁纸显示及设置原理等进行详细的剖析。

锁屏壁纸设置部分

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实战开发干货,请关注“千里马学框架”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千里马学框架

帮助你了,就请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值