Android 10.0 锁屏壁纸 LockscreenWallpaper(三)

计算机软件开发 2024-9-2 03:34:08 44 0 来自 中国
学习条记:
一、设置壁纸

通过系统设置举行锁屏壁纸和桌面壁纸的设置。
Setting 部门的代码:
// 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 的对象。
// WallpaperManagerCompatV16.java    @Override    public 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 类来举行的。
二、锁屏壁纸的表现



锁屏壁纸表现流程图: 1.png 上面应用程序设置完成了,下面就该举行壁纸表现了。
WallpaperManager#setStream()
// 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()方法:
// WallpaperManagerService.java    @Override    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()方法:
// 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() :
// WallpaperManagerService.java        @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);            // 获取发生了 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 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():
// 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            }        }    }    @Override    public 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  的构造方法里赋值调用:
// LockscreenWallpaper.java    @Inject    public 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 Automotive            try {                // iWallpaperManager 是 WallpaperManagerService 的 binder对象,                // 通过 dagger 在 SystemServicesModule 实例化。                iWallpaperManager.setLockWallpaperCallback(this);            } catch (RemoteException e) {                Log.e(TAG, "System dead?" + e);            }        }    }以是当锁屏壁纸更新时,就会回调到 LockscreenWallpaper#onWallpaperChanged()
// LockscreenWallpaper.java     @Override    public 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()
// LockscreenWallpaper.java    @Override    public 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>() {            @Override            protected LoaderResult doInBackground(Void... params) {                return loadBitmap(currentUser, selectedUser);            }            @Override            protected 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()
// 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。
// 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()
// 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);        // 省略部门代码......    }
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-10-18 16:53, Processed in 0.169106 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表