当前位置: 首页 > news >正文

车载技术——Window Display之surface的绘制过程与原理

一、Surface 概述

OpenGL ES/Skia定义了一组绘制接口的规范,为什么能够跨平台? 本质上需要与对应平台上的本地窗口建立连接。也就是说OpenGL ES负责输入了绘制的命令,但是需要一个 “画布” 来承载输出结果,最终展示到屏幕。这个画布就是本地窗口。

因此,每个平台的有着不一样的本地窗口的实现。Android平台上是 ANativeWindow。

疑问:

  • 那么如何将OpenGL本地化? 通过 EGL来对OpenGL ES来进行配置。关键点就是提供本地化窗口。
  • 本地化窗口的作用是什么? 本地窗口是OpenGL ES和 物理屏幕之间的桥梁。

1.1 Android本地窗口简述

Android图形系统提供的本地窗口,可以分为两类:

  • FrameBufferNativeWindow

面对SF(SurfaceFlinger)。它通过HAL层的Gralloc系统调用(alloc/free)来分配内核中的FrameBuffer帧缓冲区。 这个帧缓冲区就代表了物理屏幕(fb驱动节点,表示屏幕数。如fb0主屏幕、fb1等)。 FrameBuffer的数量一般情况下是2,也就是双缓冲。当然还有三倍缓冲。

  • Surface

面向应用程序。对应的是内存中一块缓冲区,称为:GraphicBuffer。是由SF来进行分配。app从SF中获取一块GraphicBuffer, 通过OpenGL/Skia将图形数据绘制(软件/硬件)到GraphicBuffer上。最终SF会把各个应用的GraphicBuffer数据进行合成,最终 通过 FrameBufferNativeWindow 输出到屏幕上。

有了一个整体的概念,接下来就好理解很多。

二、引出SurfaceSession

2.1 从WindowManagerImpl的addView()说起

app:
WindowManagerImpl.addView()WindowManagerGlobal.addView()ViewRootImpl的setView()IWindowSession.addToDisplay()
​
WMS:new WindowStateWindowState.attach()session.windowAddedLocked()new SurfaceSession()

复制代码

view添加到window的过程中, 从WindowManagerImpl 的 addView(),到WindowManagerGlobal(构造方法中会在system server 进程中创建一个Session对象)的addView()。最后会调用 ViewRootImpl的setView()方法。 内部会调用 IWindowSession 的addToDisplay() 方法。IWindowSession是WMS提供的一个binder服务(实现类就是Session)。

2.2 IWindowSession.windowAddedLocked()

内部会创建一个WindowState 对象。 调用 WindowState的 attach()方法。最终调到Session中的windowAddedLocked(),会创建 一个SurfaceSession对象。这就是我们要找的的跟SurfaceFlinger建立联系的地方。

 SurfaceSession mSurfaceSession;
void windowAddedLocked(String packageName) {mPackageName = packageName;mRelayoutTag = "relayoutWindow: " + mPackageName;if (mSurfaceSession == null) {// 一个进程只有一个session,因此也只创建一次 SurfaceSession 对象
​// 创建 SurfaceSession 对象mSurfaceSession = new SurfaceSession();
​// 每个session 都存入WMS中的mService.mSessions.add(this);
​if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {mService.dispatchNewAnimatorScaleLocked(this);}}mNumWindow++; // 进程中所有窗口的数量+1
}

复制代码

一个应用进程对应一个Session对象,一个Session对象对应一个SurfaceSession。 WMS会把 这个Session 存储起来。也就是说WMS 会把所有跟SurfaceFlinger保持连接状态的应用Session存储起来。

2.3 SurfaceSession 创建过程

这个类的实例代表了和SurfaceFlinger的一个连接。我们可以通过它 创建一个或多个 Surface 对象。

2.3.1 构造方法

> SurfaceSession.java
private long mNativeClient; // SurfaceComposerClient*
​
public SurfaceSession() {//native 方法mNativeClient = nativeCreate();
}
​
> frameworks/base/core/jni/android_view_SurfaceSession.cpp
static jlong nativeCreate(JNIEnv* env, jclass clazz) {// 新建一个 SurfaceComposerClient 对象SurfaceComposerClient* client = new SurfaceComposerClient();client->incStrong((void*)nativeCreate);//返回SurfaceComposerClient对象的引用到java层。return reinterpret_cast<jlong>(client);
}

复制代码

SurfaceComposerClient 是什么呢?

2.3.2 SurfaceComposerClient

在 SurfaceComposerClient第一次被引用的时候会走onFirstRef()方法。

> frameworks/native/libs/gui/SurfaceComposerClient.cpp
void SurfaceComposerClient::onFirstRef() {//创建sf代理binder对象sf,类型为 ISurfaceComposersp<ISurfaceComposer> sf(ComposerService::getComposerService());if (sf != nullptr && mStatus == NO_INIT) {sp<ISurfaceComposerClient> conn;//创建一个 ISurfaceComposerClient 对象,用来跨进程调用conn = sf->createConnection();if (conn != nullptr) {mClient = conn;mStatus = NO_ERROR;}}
}

复制代码

  • ISurfaceComposer 实现类就是 SurfaceFlinger对象。在server进程的代理对象是 ComposerService。This class defines the Binder IPC interface for accessing various SurfaceFlinger features.
  • 通过SF.createConnection(),创建一个 ISurfaceComposerClient 对象 mClient,用来跨进程调用。

那么 ISurfaceComposerClient的实现类是哪个呢? 继续看看 SF.createConnection()。

2.3.3 SurfaceFlinger.createConnection()

注意,此时是在SF进程。
> frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() {// new client对象。return initClient(new Client(this));
}
static sp<ISurfaceComposerClient> initClient(const sp<Client>& client) {status_t err = client->initCheck();if (err == NO_ERROR) {// 返回该对象return client;}return nullptr;
}
> frameworks/native/services/surfaceflinger/Client.h
class Client : public BnSurfaceComposerClient{...class BnSurfaceComposerClient : public SafeBnInterface<ISurfaceComposerClient> {...

复制代码

原来,ISurfaceComposerClient的实现类就是 SF中定义的 Client。也是一个binder服务。 我们回到 SurfaceComposerClient 类,它持有 ISurfaceComposerClient的binder引用 mClient。通过 mClient实现与SF通信。

2.3 小结

  • Session 类中,创建了一个 SurfaceSession 对象,内部引用c++层的 SurfaceComposerClient 对象。
  • SurfaceComposerClient 对象是通过SF创建的另一个binder服务。减轻SF的工作量。
  • SurfaceComposerClient 对象则通过 mClient成员(ISurfaceComposerClient)代理binder,后续用来创建 Surface。

Surface绘制原理

Surface的Buffer是从哪里来的?

源码:frameworks/base/core/java/android/view/ViewRootImpl.java View触发绘制是通过requestLayout()函数或者setLayoutParms()函数:

performTravsersals()函数实现:

private void performTraversals() {……performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);performLayout(lp, mWidth, mHeight);performDraw();……
}

perfomrDraw()函数调用draw()函数开始绘制:

private void performDraw() {……boolean canUseAsync = draw(fullRedrawNeeded);……
}

ViewRootImpl.draw()函数实现:

private boolean draw(boolean fullRedrawNeeded) {Surface surface = mSurface;……if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty, surfaceInsets)) {return false;}……return useAsyncReport;
}

drawSoftware()软件绘制,默认是软件绘制。

drawSoftware()函数软件绘制流程:

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
​// Draw with software renderer.final Canvas canvas;……canvas = mSurface.lockCanvas(dirty);……mView.draw(canvas);……surface.unlockCanvasAndPost(canvas);
​
}

获取:通过lockCanvas函数获取Canvas对象,

绘制:再通过mView.draw(canvas)函数向在canvas上绘制,

提交:最后通过surface.unlockCanvasAndPost(canvas)函数提交Canvas。

通过lockCanvas()函数获取Canvas对象,lockCanvas()函数如何获取Canvas对象。

lockCanvas()函数实现:

/*** Gets a {@link Canvas} for drawing into this surface.** After drawing into the provided {@link Canvas}, the caller must* invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.** @param inOutDirty A rectangle that represents the dirty region that the caller wants* to redraw.  This function may choose to expand the dirty rectangle if for example* the surface has been resized or if the previous contents of the surface were* not available.  The caller must redraw the entire dirty region as represented* by the contents of the inOutDirty rectangle upon return from this function.* The caller may also pass <code>null</code> instead, in the case where the* entire surface should be redrawn.* @return A canvas for drawing into the surface.** @throws IllegalArgumentException If the inOutDirty rectangle is not valid.* @throws OutOfResourcesException If the canvas cannot be locked.*/
public Canvas lockCanvas(Rect inOutDirty)throws Surface.OutOfResourcesException, IllegalArgumentException {synchronized (mLock) {checkNotReleasedLocked();if (mLockedObject != 0) {// Ideally, nativeLockCanvas() would throw in this situation and prevent the// double-lock, but that won't happen if mNativeObject was updated.  We can't// abandon the old mLockedObject because it might still be in use, so instead// we just refuse to re-lock the Surface.throw new IllegalArgumentException("Surface was already locked");}mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);return mCanvas;}
}

通过Native层android_view_Surface.cpp的nativeLockCanvas(mNativeObject, mCanvas, inOutDirty)函数获取,mNativeOjbect参数是Java层的Surface在Native层对应的Surface对象的指针。mCanvas是Surface的变量,在lockCanvas()函数调用时mCanvas是空的。

在调用nativeLockCanvas()函数后mCanvas就有值了,最后返回mCanvas对象。

static jlong nativeLockCanvas(JNIEnv* env, jclass clazz,jlong nativeObject, jobject canvasObj, jobject dirtyRectObj) {// (1)sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));……// (2)ANativeWindow_Buffer buffer;status_t err = surface->lock(&buffer, dirtyRectPtr);……// (3)graphics::Canvas canvas(env, canvasObj);canvas.setBuffer(&buffer, static_cast<int32_t>(surface->getBuffersDataSpace()));……// (4)// Create another reference to the surface and return it.  This reference// should be passed to nativeUnlockCanvasAndPost in place of mNativeObject,// because the latter could be replaced while the surface is locked.sp<Surface> lockedSurface(surface);lockedSurface->incStrong(&sRefBaseOwner);return (jlong) lockedSurface.get();
}

(1) 获取Native层的Surface对象。

(2) 获取Native层的Surface对象的Buffer。

(3) 将Buffer设置给Canvas,这里Canvas就有一个Buffer了。在每次都申请一个新的Buffer给Canvas对象。

(4) 向Java层返回Native的Surface对象,这里返回的是一个Long型数据,这个Long型数据是Surface指针。

获取Buffer实现,surface -> lock(&buffer, ),这里传入Buffer地址:

源码:frameworks/native/libs/gui/Surface.cpp

status_t Surface::lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
{……// (1)ANativeWindowBuffer* out;status_t err = dequeueBuffer(&out, &fenceFd);……// (2)sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));……// (3)void* vaddr;status_t res = backBuffer->lockAsync(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, newDirtyRegion.bounds(), &vaddr, fenceFd);……// (4)mLockedBuffer = backBuffer;// (5)outBuffer->bits   = vaddr;……
}

(1) 获取dequeueBuffer()函数在SurfaceFlinger的Buffer队列中获取Buffer。

(2) 创建GraphicBuffer对象backBuffer。在SharedBufferStack中有双缓冲机制,分别为FontBuffer和BackBuffer。

FontBuffer:代表当前将显示在屏幕的Buffer数据。属于前台Buffer。 BackBuffer:代表绘制的Buffer数据,是准备渲染的数据Buffer。属于后台Buffer。   (3) 锁定Buffer,并将Buffer地址返回,将返回的Buffer地址给Canvas的Buffer。

(4) 切换Buffer,将后台BackBuffer切换到前台,交给mLockedBuffer。FontBuffer的变量就是mLockedBuffer。

(5) 将vaddr赋值给outBuffer->bits,bits最后赋值给Canvas的Buffer,就是BkBitmap,作为Canvas的缓冲区。

dequeteBuffer()是如何获取Buffer的,dequeteBuffer()函数实现:

int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {……int buf = -1;// (1)status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight,reqFormat, reqUsage, &mBufferAge,enableFrameTimestamps ? &frameTimestamps: nullptr);……// (2)sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);……// (3)if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr) {if (mReportRemovedBuffers && (gbuf != nullptr)) {mRemovedBuffers.push_back(gbuf);}result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);if (result != NO_ERROR) {ALOGE("dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: %d", result);mGraphicBufferProducer->cancelBuffer(buf, fence);return result;}}……// (4)*buffer = gbuf.get();
}

(1) 通过mGaphicBufferProducer->dequeteBuffer()函数在远端的Buffer slots中获得一个空闲的Buffer,返回远端Buffer地址指针。

(2) 通过gbp从本地Buffer Slots里获取Buffer,在(1)中从远端,在(2)中从本地,这里涉及远端Buffer queue与本地Buffer queue同步问题。

(3) 负责本地Buffer与远端Buffer同步,远端返回的Buffer的result是BUFFER_NEEDS_REALLOCATION或者本地的gbp是null,通过gbp的requestBuffer()获取新的远端Buffer指针地址。mGaphicBufferProducer->requestBuffer()函数。

(4) 获取Buffer。

Surface的Buffer是如何提交的?

通过surface.unlockCanvasAndPost(canvas)向远端提交更新的Buffer,unlockCanvasAndPost()函数实现:

/*** Posts the new contents of the {@link Canvas} to the surface and* releases the {@link Canvas}.** @param canvas The canvas previously obtained from {@link #lockCanvas}.*/
public void unlockCanvasAndPost(Canvas canvas) {synchronized (mLock) {unlockSwCanvasAndPost(canvas);}
}
​
private void unlockSwCanvasAndPost(Canvas canvas) {try {nativeUnlockCanvasAndPost(mLockedObject, canvas);} finally {nativeRelease(mLockedObject);mLockedObject = 0;}
}

最后调用到Native层的nativeUnlockCanvasAndPost(mLockedObject, canvas)。

Native层,nativeUnlockCanvasAndPost()函数实现:

static void nativeUnlockCanvasAndPost(JNIEnv* env, jclass clazz,jlong nativeObject, jobject canvasObj) {// (1)sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
​// (2)// detach the canvas from the surfacegraphics::Canvas canvas(env, canvasObj);canvas.setBuffer(nullptr, ADATASPACE_UNKNOWN);// (3)// unlock surfacestatus_t err = surface->unlockAndPost();
}

(1) 获取对应Java层的Native层的Surface对象。

(2) 获取对应Java层的Native层的Canvas对象。

(3) 将本地Buffer更新到远端的Buffer queue中。

Native层更新远端Buffer queue,surface->unlockAndPost()函数实现:

源码:frameworks/native/libs/gui/Surface.cpp

status_t Surface::unlockAndPost()
{if (mLockedBuffer == nullptr) {ALOGE("Surface::unlockAndPost failed, no locked buffer");return INVALID_OPERATION;}
​int fd = -1;status_t err = mLockedBuffer->unlockAsync(&fd);ALOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle);err = queueBuffer(mLockedBuffer.get(), fd);ALOGE_IF(err, "queueBuffer (handle=%p) failed (%s)",mLockedBuffer->handle, strerror(-err));mPostedBuffer = mLockedBuffer;mLockedBuffer = nullptr;return err;
}

通过函数queueBuffer(mLockedBuffer.get(), )函数实现更新:

int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {……// (1)int i = getSlotFromBufferLocked(buffer);……// (2)status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);……return err;
}

(1) 获取Buffer的index。

(2) 通过mGraphicBufferProducer->queueBuffer(i, )函数,将本地的Buffer同步到远端Buffer queue中。

以上为车载技术中Window Display板块的surface的绘制过程与原理;更多车载技术参考,可点击《车载技术手册》查看类目学习。

Android车载学习手册​docs.qq.com/doc/DUldvclB5d0JZSVFn

三、总结

相关文章:

车载技术——Window Display之surface的绘制过程与原理

一、Surface 概述 OpenGL ES/Skia定义了一组绘制接口的规范&#xff0c;为什么能够跨平台&#xff1f; 本质上需要与对应平台上的本地窗口建立连接。也就是说OpenGL ES负责输入了绘制的命令&#xff0c;但是需要一个 “画布” 来承载输出结果&#xff0c;最终展示到屏幕。这个…...

2023年全国最新工会考试精选真题及答案10

百分百题库提供工会考试试题、工会考试预测题、工会考试真题、工会证考试题库等&#xff0c;提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 51.&#xff08;&#xff09;是企业工会的权力机关&#xff0c;每年召开一至两次会议。 A.会员大会 B.会…...

pytorch-复现经典深度学习模型-LeNet5

Neural Networks 使用torch.nn包来构建神经网络。nn包依赖autograd包来定义模型并求导。 一个nn.Module包含各个层和一个forward(input)方法&#xff0c;该方法返回output。 一个简单的前馈神经网络&#xff0c;它接受一个输入&#xff0c;然后一层接着一层地传递&#xff0c;…...

【C++】类和对象(上)

文章目录对象的介绍类的介绍类的两种定义方式类的访问限定符及封装访问限定符封装类的作用域类的实例化类的对象模型对象的介绍 C语言是面向过程的&#xff0c;关注的是过程&#xff0c;分析出求解问题的步骤&#xff0c;通过函数调用逐步解决问题&#xff1b;   C是基于面向…...

工作中责任链模式用法及其使用场景?

前言 笔者是金融保险行业&#xff0c;有这么一种场景&#xff0c;业务员录完单后提交核保&#xff0c;这时候系统会对保单数据进行校验&#xff0c;如不允许手续费超限校验&#xff0c;客户真实性校验、费率限额校验等等&#xff0c;当校验一多时&#xff0c;维护起来特别麻烦…...

三八女神节有哪些数码好物?2023年三八女神节数码好物清单

2023年的三八女神节就快到了&#xff0c;大家还在烦恼&#xff0c;不知道有哪些数码好物&#xff1f;在此&#xff0c;我来给大家分享几款三八女神节实用性强的数码好物&#xff0c;一起来看看吧。 一、蓝牙耳机&#xff1a;南卡小音舱 参考价&#xff1a;239 推荐理由&…...

FairGuard-Windows加固工具版本更新日志

FairGuard-Windows加固工具1.2.2版本更新日志&#xff1a; ■ 增加Unity Resources资源加密的支持; ■ 增加单独Assetbundle资源加密&#xff0c;并同时支持压缩包和文件夹作为输入的方式; ■ 增加对游戏原文件夹加固的支持; Windows加固方案介绍 FairGuard专为游戏量身定…...

基于RT-Thread完整版搭建的极简Bootloader

项目背景Agile Upgrade: 用于快速构建 bootloader 的中间件。example 文件夹提供 PC 上的示例特性适配 RT-Thread 官方固件打包工具 (图形化工具及命令行工具)使用纯 C 开发&#xff0c;不涉及任何硬件接口&#xff0c;可在任何形式的硬件上直接使用加密、压缩支持如下&#xf…...

3.flinkDateStreamAPI介绍env与source

执行环境 Flink可以在不同的环境上下文中运行.可以本地集成开发环境中运行也可以提交到远程集群环境运行. 不同的运行环境对应的flink的运行过程不同,需要首先获取flink的运行环境,才能将具体的job调度到不同的TaskManager 在flink中可以通过StreamExecutionEnvironment类获取…...

$ 2 :数据类型

1.数据类型 1.1基本类型 a、整型int b、浮点型float c、字符型char 1.2构造类型 a、数组[ ] b、结构体struct 1.3指针类型 * 1.4空类型(void) 2.关键字 autoconstdoublefloatintshortstructunsignedbreakcontinueelseforlongsignedswitchvoidcasedefaultenumgotoregistersiz…...

类和对象 - 上

本文已收录至《C语言》专栏&#xff01; 作者&#xff1a;ARMCSKGT 目录 前言 正文 面向过程与面向对象 面向过程的解决方法 面向对象的解决方法 面向对象的优势 类的引入 早期C类的实现 class定义类 class定义规则 类成员的两种定义方式 类的访问限定符及封装 访…...

补档:红黑树代码实现+简略讲解

红黑树讲解和实现1 红黑树介绍1.1 红黑树特性1.2 红黑树的插入1.3 红黑树的删除2 完整代码实现2.1 rtbtree.h头文件2.2 main.c源文件1 红黑树介绍 红黑树( Red-Black tree&#xff0c;简称RB树)是一种自平衡二叉查找树&#xff0c;是计算机科学中常见的一种数据结构&#xff0c…...

FirePower X2 14.0.1 for RAD Studio Alexandria

介绍 FirePower X2 FirePower X2 集成了 RAD Studio 11.0 Alexandria 中的新功能&#xff0c;并预览了我们的新特色组件 TwwDataGrouper。 FirePower X2 还允许您为 Apple 的新 M1 芯片构建应用程序&#xff0c;这样您就可以进一步利用 M1 芯片来提高本机应用程序的性能&#x…...

二十九、MongoDB 恢复数据( mongorestore )

MongoDB mongorestore 脚本命令可以用来恢复备份的数据 语法 MongoDB mongorestore 命令脚本语法如下 $ mongorestore -h <hostname><:port> -d dbname <path> 参数说明 -h <:port>, -h<:port> MongoDB 所在服务器地址&#xff0c;默认为 l…...

【数据分析】缺失数据如何处理?pandas

本文目录1. 基础概念1.1. 缺失值分类1.2. 缺失值处理方法2. 缺失观测及其类型2.1. 了解缺失信息2.2. 三种缺失符号2.3. Nullable类型与NA符号2.4. NA的特性2.5. convert_dtypes方法3. 缺失数据的运算与分组 3.1. 加号与乘号规则3.2. groupby方法中的缺失值4. 填充与剔除4.1. fi…...

嵌入式开发--STM32H750VBT6开发中,新版本CubeMX的时钟问题,不能设置到最高速度480MHZ

嵌入式开发–STM32H750VBT6开发中&#xff0c;新版本CubeMX的时钟问题&#xff0c;不能设置到最高速度480MHZ 问题描述 之前开发的项目&#xff0c;开发环境是CubeMX6.6.1&#xff0c;H7系列的支持包版本是1.10.0。跑得没问题&#xff0c;最近需要对项目做修改&#xff0c;同…...

一文读懂PaddleSpeech中英混合语音识别技术

语音识别技术能够让计算机理解人类的语音&#xff0c;从而支持多种语音交互的场景&#xff0c;如手机应用、人车协同、机器人对话、语音转写等。然而&#xff0c;在这些场景中&#xff0c;语音识别的输入并不总是单一的语言&#xff0c;有时会出现多语言混合的情况。例如&#…...

问题三十四:傅立叶变换——高通滤波

高通滤波器是一种可以通过去除图像低频信息来增强高频信息的滤波器。在图像处理中&#xff0c;高通滤波器常常用于去除模糊或平滑效果&#xff0c;以及增强边缘或细节。在本篇回答中&#xff0c;我们将使用Python和OpenCV实现高通滤波器。 Step 1&#xff1a;加载图像并进行傅…...

flink 键控状态(keyed state)

github开源项目flink-note的笔记。本博客的实现代码都写在项目的flink-state/src/main/java/state/keyed/KeyedStateDemo.java文件中。 项目github地址: github 1. flink键控状态 flink键控状态是作用与flink KeyedStream上的,也就是说需要将DataStream先进行keyby之后才能使…...

【ChatGPT】sqlachmey 多表连表查询语句

感受下科技带来的魅力&#xff0c;这篇文章是通过ChatGPT自动生成的&#xff0c;不得不说技术强大!!! 在SQLAlchemy中进行多表连接查询可以使用join()方法或join()函数&#xff0c;具体用法如下&#xff1a; join()方法 join()方法可以在SQLAlchemy ORM中的查询中使用。假设…...

win11 系统登录问题,PIN 设置问题

我的电脑配置是华为MateBook X Pro 12&#xff0c;i7处理器&#xff0c;16G&#xff0c;1T&#xff0c;win11 系统通过微软账户登录&#xff0c;下午一直登录不进去&#xff0c;网络能连外网&#xff0c;分析应该是连微软服务器不行。连续登录几十次&#xff0c;偶尔可能有一次…...

数据结构六大排序

1.插入排序 思路&#xff1a; 从第一个元素开始认为是有序的&#xff0c;去一个元素tem从有序序列从后往前扫描&#xff0c;如果该元素大于tem&#xff0c;将该元素一刀下一位&#xff0c;循环步骤3知道找到有序序列中小于等于的元素将tem插入到该元素后&#xff0c;如果已排序…...

快速生成QR码的方法:教你变成QR Code Master

目录 简介: 具体实现步骤&#xff1a; 一、可以使用Python中的qrcode和tkinter模块来生成QR码。以下是一个简单的例子&#xff0c;演示如何在Tkinter窗口中获取用户输入并使用qrcode生成QR码。 1&#xff09;首先需要安装qrcode模块&#xff0c;可以使用以下命令在终端或命令…...

tensorflow1.14.0安装教程--保姆级

//方法不止一种&#xff0c;下面仅展示一种。 注&#xff1a;本人电脑为win11&#xff0c;anaconda的python版本为3.9&#xff0c;但tensorflow需要python版本为3.7&#xff0c;所以下面主要阐述将python版本改为3.7后的安装过程以及常遇到的问题。 1.首先电脑安装好anaconda…...

AcWing算法提高课-3.1.3香甜的黄油

宣传一下算法提高课整理 <— CSDN个人主页&#xff1a;更好的阅读体验 <— 题目传送门点这里 题目描述 农夫John发现了做出全威斯康辛州最甜的黄油的方法&#xff1a;糖。 把糖放在一片牧场上&#xff0c;他知道 N 只奶牛会过来舔它&#xff0c;这样就能做出能卖好价…...

私库搭建1:Nexus 安装 Docker 版

本文内容以语雀为准 文档 https://hub.docker.com/r/sonatype/nexus3Docker 安装&#xff1a;https://www.yuque.com/xuxiaowei-com-cn/gitlab-k8s/docker-install 安装 创建文件夹 由于 Nexus 的数据可能会很大&#xff0c;比如&#xff1a;作为 Docker、Maven 私库时&…...

LeetCode-面试题 05.02. 二进制数转字符串【数学,字符串,位运算】

LeetCode-面试题 05.02. 二进制数转字符串【数学&#xff0c;字符串&#xff0c;位运算】题目描述&#xff1a;解题思路一&#xff1a;简单暴力。小数点后面的二进制&#xff0c;now首先从0.5开始之和每次除以2。然后依次判断当前数是否大于now&#xff0c;是则答案加1。若等于…...

pandas: 三种算法实现递归分析Excel中各列相关性

目录 前言 目的 思路 代码实现 1. 循环遍历整个SDGs列&#xff0c;两两拿到数据 2. 调用pandas库函数直接进行分析 完整源码 运行效果 总结 前言 博主之前刚刚被学弟邀请参与了2023美赛&#xff0c;这也是第一次正式接触数学建模竞赛&#xff0c;现在已经提交等待结果…...

【Python百日进阶-Web开发-Vue3】Day543 - Vue3 商城后台 03:登录页面初建

文章目录 一、创建登录页面 login.vue二、登录页面响应式处理,以适应不同大小的屏幕2.1 element-plus 的layout布局中关于响应式的说明2.2 修改login.vue文件2.2.1 :lg=16 大于1200px 横排 2:12.2.2 :md=12 大于992小于1200px 横排 1:12.2.3 小于992 竖排三、引入Element-plus…...

python画直方图,刻画数据分布

先展示效果 准备一维数据 n 个数据元素计算最大值&#xff0c;最小值、均值、标准差、以及直方图分组 import numpy as np data list() for i in range(640):data.append(np.random.normal(1)) print(data)z np.histogram(data, bins64) print(list(z[0])) ### 对应 x 轴数据…...

网站 关键词 挖掘/推广引流软件

命令怎么用&#xff08;三种方式&#xff09;shutdown --help shutdown --? man shutdown &#xff08;man 就是manual 手册, 指南&#xff09; 服务 service怎么知道服务的名字呢&#xff1f;用这个命令chkconfig&#xff1a; chkconfig --list 会列出所有的服务项 启动服务…...

武汉 网站 合作伙伴/爱站网ip反域名查询

日常使用PHP开发较多&#xff0c;但是有些地方PHP的语言的瓶颈就显露出来了&#xff0c;例如&#xff0c;同样是抓取一个网站的内容&#xff0c;使用PHP需要较为复杂的正则匹配&#xff0c;效率较为低下。python具有丰富的类库&#xff0c;拿过来直接可以使用&#xff0c;功能强…...

东莞疫情什么时候开始的/seo快速排名软件网站

这是linux中一个非常重要命令&#xff0c;请大家一定要熟悉。它的功能是为某一个文件在另外一个位置建立一个同不的链接&#xff0c;这个命令最常用的参数是-s, 具体用法是&#xff1a;ln -s 源文件 目标文件 不论是硬连结或软链结都不会将原本的档案复制一份&#xff0c;只会…...

新手学网站建设视频教程共30课高清版/网站搭建免费

计算机程序最基本的描述也许是一系列处理数据的操作&#xff0c;即使是最复杂的实例&#xff0c;这个论述也是正确的。应用程序的用户虽然看不到它们&#xff0c;但这些操作总是在后台上进行。我们在屏幕上进行的任何操作&#xff0c;无论是移动鼠标指针&#xff0c;单击图标&a…...

wordpress改wp admin/软文营销名词解释

题目&#xff1a; 0,1&#xff0c;...n-1这n个数字排成一个圆圈&#xff0c;从数字0开始每次从这个圆圈里删除第m个数字&#xff0c;求出这个圆圈里剩下的最后一个数字。 思路&#xff1a; 1、环形链表模拟圆圈 创建一个n个节点的环形链表&#xff0c;然后每次在这个链表中删除…...

张家口做网站公司/云搜索app

小米一纸公告宣布米聊关闭&#xff0c;这背后的影响可能将是相当深远&#xff0c;或许说明小米更适合做硬件业务&#xff0c;缺乏互联网基因&#xff0c;这导致它的硬件业务做得越来越成功&#xff0c;而互联网业务却似乎进入瓶颈。小米业绩增长主要靠硬件业务小米在2020年Q3公…...