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

    @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 类来进行的。

二、锁屏壁纸的显示

锁屏壁纸显示流程图:

上面应用程序设置完成了,下面就该进行壁纸显示了。
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

        @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()
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
            }
        }
    }
    @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 的构造方法里赋值调用:
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/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()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/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()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/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()
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);
        // 省略部分代码......
    }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

浅谈路由器交换结构

一、路由器技术概述 路由器&#xff08;Router&#xff09;是连接两个或多个网络的硬件设备&#xff0c;在网络间起网关的作用&#xff0c;是读取每一个数据包中的地址然后决定如何传送的专用智能性的网络设备。它能够理解不同的协议&#xff0c;例如某个局域网使用的以太网协议…

FL Studio版本升级-FL Studio怎么升级-FL Studio升级方案

已经是新年2024年了&#xff0c;但是但是依然有很多朋友还在用FL Studio12又或者FL Studio20&#xff0c;今天这篇文章教大家如何升级FL Studio21 FL Studio 21是Image Line公司开发的音乐编曲软件&#xff0c;除了软件以外&#xff0c;我们还提供了FL Studio的升级服务&#…

Pytorch 安装与配置

每次在服务器上配置环境都需要考虑 Pytorch 版本 / CUDA 版本 / GPU 驱动版本等诸多因素&#xff0c;因为 ⌈ \lceil ⌈Pytorch 只能下载指定 CUDA 版本的库 ⌋ \rfloor ⌋、 ⌈ \lceil ⌈GPU 只能使用特定版本的驱动 ⌋ \rfloor ⌋、 ⌈ \lceil ⌈GPU 有最高支持的 CUDA 版本…

【学习笔记】TypeScript编译选项

TS 中的编译选项 我们写了一个TS的文件&#xff0c;我们需要使用如下的命令将我们的TS文件转换为JS文件。 tsc xxx.ts 这样会产生一个对应的js文件 自动编译文件 编译文件时&#xff0c;使用 -W 指令后&#xff0c;TS编译器会自动监视文件的变化&#xff0c;并在文件发生变…

Android.mk 语法详解

一.Android.mk简介 Android.mk 是Android 提供的一种makefile 文件,注意用来编译生成&#xff08;exe&#xff0c;so&#xff0c;a&#xff0c;jar&#xff0c;apk&#xff09;等文件。 二.Android.mk编写 分析一个最简单的Android.mk LOCAL_PATH : $(call my-dir) //定义了…

第73左侧菜单实现

layout下面新建menu layout index.vue导入menu import Menu from /views/layout/menu菜单实现&#xff1a; <template><el-menuactive-text-color"#ffd04b"background-color"#2d3a4b"class"el-menu-vertical-demo"default-active&quo…

ShardingSphere 5.x 系列【7】元数据持久化

有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot 版本 3.1.0 本系列ShardingSphere 版本 5.4.0 源码地址:https://gitee.com/pearl-organization/study-sharding-sphere-demo 文章目录 1.概述2. 单机模式2.1 H22.2 MySQL3. 集群模式3.1 ZooKeeper3.2 Nacos3.3 Co…

谷粒商城【成神路】-【6】——商品维护

目录 &#x1f9c2;1.发布商品 &#x1f953;2.获取分类关联品牌 &#x1f32d;3.获取分类下所有分组和关联属性 &#x1f37f;4.商品保存功能 &#x1f9c8;5.sup检索 &#x1f95e;6.sku检索 1.发布商品 获取用户系统等级~&#xff0c;前面生成了后端代码&#xff…

opencv mat用法赋值克隆的操作和一些基本属性

//Mat基本结构 (头部 数据部分) //赋值的话 就是修改了指针位置 但还是指向了原来数据 并没创建数据 本质上并没有变 //只有克隆或者拷贝时 它才会真正复制一份数据 //代码实现 //创建方法 - 克隆 //Mat m1 src.clone(); //复制 //Mat m2; //src.copyTo(m2); //赋值法 …

nodejs爬虫框架

nodejs爬虫框架 在Node.js中&#xff0c;有一些常用的爬虫框架可以帮助你实现网页抓取和数据提取的任务。以下是几个流行的Node.js爬虫框架&#xff1a; 1. **Puppeteer**: Puppeteer 是由 Google 开发的一个用于控制 headless Chrome 或 Chromium 浏览器的 Node.js 库。它提供…

深度解析Pandas聚合操作:案例演示、高级应用与实战技巧【第74篇—Pandas聚合】

深度解析Pandas聚合操作&#xff1a;案例演示、高级应用与实战技巧 在数据分析和处理领域&#xff0c;Pandas一直是Python中最受欢迎的库之一。它提供了丰富的数据结构和强大的功能&#xff0c;使得数据清洗、转换和分析变得更加高效。其中&#xff0c;Pandas的聚合操作在数据…

bugku 1

Flask_FileUpload 文件上传 先随便传个一句话木马 看看回显 果然不符合规定 而且发现改成图片什么的都不行 查看页面源代码&#xff0c;发现提示 那应该就要用python命令才行 试试ls 类型要改成图片 cat /flag 好像需要密码 bp爆破 根据提示&#xff0c;我们先抓包 爆破 …

【Effective Objective - C 2.0】——读书笔记(三)

文章目录 十五、用前缀避免命名空间冲突十六、提供全能初始化方法十七、实现description方法十八、尽量使用不可变对象十九、使用清晰而协调的命名方式二十、为私有方法名加前缀二十一、理解Objective-C错误模型二十二、理解NSCopying协议 十五、用前缀避免命名空间冲突 OC语言…

【并发编程】Java的Future机制详解(Future接口和FutureTask类)

目录 一、彻底理解Java的Future模式 二、为什么出现Future机制 2.1 Future 类有什么用&#xff1f; 三、Future的相关类图 2.1 Future 接口 2.2 FutureTask 类 五、FutureTask源码分析 5.1 state字段 5.2 其他变量 5.3 CAS工具初始化 5.4 构造函数 5.5 jdk1.8和之前…

医院挂号预约|医院挂号预约小程序|基于微信小程序的医院挂号预约系统设计与实现(源码+数据库+文档)

医院挂号预约小程序目录 目录 基于微信小程序的医院挂号预约系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、小程序用户端 2、系统服务端 &#xff08;1&#xff09; 用户管理 &#xff08;2&#xff09;医院管理 &#xff08;3&#xff09;医生管理 &…

UI自动刷新大法:DataBinding数据绑定

之前我们讲了DataBinding在Activity、Fragment、RecyclerView中的基础使用&#xff0c;而那些常规使用方法里&#xff0c;每当绑定的变量发生数据变化时&#xff0c;都需要ViewDataBinding重新设值才会刷新对应UI。而DataBinding通过内部实现的观察者模式来进行自动刷新UI&…

浅谈人工智能之深度学习~

目录 前言&#xff1a;深度学习的进展 一&#xff1a;深度学习的基本原理和算法 二&#xff1a;深度学习的应用实例 三&#xff1a;深度学习的挑战和未来发展方向 四&#xff1a;深度学习与机器学习的关系 五&#xff1a;深度学习与人类的智能交互 悟已往之不谏&#xff0…

Gemini VS GPT-4,当前两大顶级AI模型实测

随着谷歌在AI军备竞赛中急起直追&#xff0c;“有史以来最强大模型”Gemini Advanced终于上线&#xff0c;AI爱好者们总算等来了一款号称能够匹敌GPT-4的大语言模型。 月费19.99美元&#xff08;包含Google One订阅&#xff09;的Gemini Advanced实际表现如何&#xff1f;究竟…

MongoDB系列之WiredTiger引擎

概述 关系型数据库MySQL有InnoDB存储引擎&#xff0c;存储引擎很大程度上决定着数据库的性能。 在MongoDB早期版本中&#xff0c;默认使用MMapV1存储引擎&#xff0c;其索引就是一个B-树&#xff08;也称B树&#xff09;。 从MongoDB 3.0开始引入WiredTiger&#xff08;以下…

arduino D1 中esp8266 没有ide的库

http://arduino.esp8266.com/stable/package_esp8266com_index.json https://arduino.esp8266.com/stable/package_esp8266com_index.json 这个是官网的包地址 拿到后复制到arduino ide中 然后在开发板管理器&#xff0c;搜索esp&#xff0c;搜出来后安装 去开发板选择 然后测…