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.mzph.cn/news/680718.shtml

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

相关文章

第6个-滚动动画

Day 6 - Scroll Animation 1. 演示效果 2. 分析思路 布局 所有的内容进行水平垂直居中&#xff0c;可以使用**margin:0 auto;&#xff0c;也可以使用flex**布局&#xff1a; body {background-color: #efedd6;display: flex;flex-direction: column;justify-content: center…

树结构 严蔚敏 数据结构代码

一&#xff0c;树顺序和链式存储结构的定义 //树用儿子-兄弟表示法&#xff0c;就成了二叉树//一般二叉树用顺序存储浪费空间&#xff0c;所以大都用链式存储//特殊的二叉树有完美 或 满二叉树、完全树 可以用顺序存储//严#define MAXSIZE 100 //二叉树的最大结点数typedef TE…

前端架构: 本地调试脚手架的2种方式

一、 调试简单的脚手架方式 假定脚手架名称是 xxx 1 &#xff09;方式1 在xxx脚手架项目目录的上一级&#xff0c;执行 npm i -g xxx这时候&#xff0c;就可以本地调试脚手架&#xff0c;在前文中已经说明软链的作用参考&#xff1a;https://blog.csdn.net/Tyro_java/article…

【C语言】实现栈

目录 &#xff08;一&#xff09;栈 &#xff08;二&#xff09;头文件 &#xff08;三&#xff09;功能实现 &#xff08;1&#xff09;初始化栈 &#xff08;2&#xff09; 栈的销毁 &#xff08;3&#xff09;压栈 &#xff08;4&#xff09; 出栈 &#xff08;5&a…

【html学习笔记】1.概念

1.概念 1.1 HTML标准格式 <html><body><p>Hello World</p></body> </html>1.2 编辑方式 新建一个笔记本文件&#xff0c;将html语法格式的内容写入。保存后将记事本的.txt后缀换成.html,就可以在浏览器里运行了 1.3 中文问题 为了避…

前端架构: 从vue-cli探究脚手架原理

从使用角度理解什么是脚手架 脚手架本质是一个操作系统的客户端 在终端中去执行一个命令&#xff0c;这个命令本身它就是一个客户端我们其实可以把脚手架理解为操作系统的一个客户端通过命令去执行它的时候&#xff0c;这个命令往往是这样的一个构造&#xff0c;如下 比如&…

PoW算法,请出示你的证明

口信消息型拜占庭问题的解可以防止 (n - 1) / 3 个坏人 (其中 n 为节点数)作恶&#xff0c;这样一来也是可以通过不断增加节点数来突破 (n - 1) / 3 的限制。为了防止这一行为可以使用区块链技术中的工作量证明&#xff08;Proof of Work&#xff09;算法。 原理 PoW算法&…

devc++跑酷小游戏1.2.5

更新了在关卡中的复位和地图的刷新z或Z&#xff0c;存档还是没写出来&#xff0c;文件操作好难&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;…

【leetcode热题100】解码方法

难度&#xff1a; 中等通过率&#xff1a; 21.5%题目链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 题目描述 一条包含字母 A-Z 的消息通过以下方式进行了编码&#xff1a; A -> 1 B -> 2 ... Z -> 26给定一个只包含数字…

continue、break、return的区别

continue、break、return的区别 continuebreakreturn continue 作用&#xff1a;跳过本次循环&#xff0c;继续接下来的循环 for(int i 1;i < 10;i){if(i4){continue;}System.out.print(i " "); } //输出结果为&#xff1a;1 2 3 5 6 7 8 9 10 //4的输出被跳过…

C#中 Combine 静态方法

在C#中&#xff0c;Combine是System.IO.Path类的一个静态方法&#xff0c;用于将多个路径片段组合成一个完整的路径。 Combine方法的详细解释如下&#xff1a; public static string Combine(string path1, string path2);参数&#xff1a; path1&#xff1a;要组合的第一个…

【手写数据库toadb】数据字典的内容结构,它的生成,避免鸡生蛋蛋生鸡的问题,高频访下的性能应对

411 数据字典的作用 ​专栏内容: 手写数据库toadb 本专栏主要介绍如何从零开发,开发的步骤,以及开发过程中的涉及的原理,遇到的问题等,让大家能跟上并且可以一起开发,让每个需要的人成为参与者。 本专栏会定期更新,对应的代码也会定期更新,每个阶段的代码会打上tag,方…

【好玩AI】【Prompt】情人节了,用GPT写个【骂醒恋爱脑】的机器人跟自己对话吧

情人节了&#xff0c;让我们用GPT写个【骂醒恋爱脑】的机器人跟自己对话吧。 通过本文&#xff0c;你能学到&#xff1a; 1. 如何零代码搭建一个自己的机器人Bot 2. 骂醒恋爱脑的高级Prompt 通过本文&#xff0c;你还能得到&#xff1a; 恋爱脑可能被骂醒 为了白嫖&#xff0c;…

Linux下的多线程

前面学习了进程、文件等概念&#xff0c;接下里为大家引入线程的概念 多线程 线程是什么&#xff1f;为什么要有线程&#xff1f;线程的优缺点Linux线程操作线程创建线程等待线程终止线程分离 线程间的私有和共享数据理解线程库和线程id深刻理解Linux多线程&#xff08;重点&a…

【selenium】

selenium是一个Web的自动化测试工具&#xff0c;最初是为网站自动化测试而开发的。Selenium可以直接调用浏览器&#xff0c;它支持所有主流的浏览器。其本质是通过驱动浏览器&#xff0c;完成模拟浏览器操作&#xff0c;比如挑战&#xff0c;输入&#xff0c;点击等。 下载与打…

算法刷题:快乐数

快乐数 .习题链接题目题目解析初始值算法原理我的答案 . 习题链接 快乐数 题目 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字的平方和。 然后重复这个过程直到这个数变为 1…

【C语言】解析刘谦春晚魔术《守岁共此时》

今年的春晚上刘谦表演了魔术《守岁共此时》&#xff0c;台上台下积极互动&#xff08;尤其是小尼&#xff09;&#xff0c;十分的有趣。刘谦老师的魔术不仅仅是他的高超手法&#xff0c;还有这背后的严谨逻辑&#xff0c;下面我们来用C语言来解析魔术吧。 源代码 #define _CRT…

已解决ModuleNotFoundError: No module named ‘PIL‘异常的正确解决方法,亲测有效!!!

已解决ModuleNotFoundError: No module named PIL异常的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01; 文章目录 问题分析 报错原因 解决思路 解决方法 总结 Python是一个强大的编程语言&#xff0c;拥有丰富的第三方库来扩展它的功能。但有时&…

【笔记】Helm-5 Chart模板指南-13 调是模版

调试模板 调试模板可能很棘手&#xff0c;因为渲染后的模板发送了kubernetes API server&#xff0c;可能会以格式化以外的原因拒绝YAML文件。 以下命令有助于调试&#xff1a; 1、helm lint 是验证chart是否遵循最佳实践的首选工具。 2、helm template --debug在本地测试渲…