[Android] Graphic Buffer 的申请
前言:
MediaCodec 支持 texture mode,即MediaCodec解码video完毕后把 yuv 数据填入 GPU 共享出来的 graphic buffer 里面,app 会把 video 的 yuv数据 和 ui 的数据通过通过软件渲染组件(opengl等)发送给GPU 进行一并渲染。这样做的效率较低,但是稳定性较好。且性能取决于GPU的性能。
另外一种是 surface mode,就是常用的模式,这种模式下 app 不再获取 yuv 数据,而是只负责输送 es 数据并决定 render 还是 discard 解码后的数据,且是根据 bufferid 来判断的,根本没有机会接触解码后的 yuv 数据,所有的一切都在平台完成。这种办法效率搞且功耗也很低,因为不需要GPU的参与。大部分情况下都是这种模式。
本文简单理清一下 Graphic Buffer 的框架,以对 texture mode 中的 Graphic Buffer 申请流程有一个大致的概念。
Graphic Buffer 的申请
始于 ANativeWindow 类
frameworks/native/libs/nativewindow/include/system/window.h
struct ANativeWindow
{
#ifdef __cplusplusANativeWindow(): flags(0), minSwapInterval(0), maxSwapInterval(0), xdpi(0), ydpi(0){common.magic = ANDROID_NATIVE_WINDOW_MAGIC;common.version = sizeof(ANativeWindow);memset(common.reserved, 0, sizeof(common.reserved));}/* Implement the methods that sp<ANativeWindow> expects so that itcan be used to automatically refcount ANativeWindow's. */void incStrong(const void* /*id*/) const {common.incRef(const_cast<android_native_base_t*>(&common));}void decStrong(const void* /*id*/) const {common.decRef(const_cast<android_native_base_t*>(&common));}
#endifstruct android_native_base_t common;/* flags describing some attributes of this surface or its updater */const uint32_t flags;/* min swap interval supported by this updated */const int minSwapInterval;/* max swap interval supported by this updated */const int maxSwapInterval;/* horizontal and vertical resolution in DPI */const float xdpi;const float ydpi;/* Some storage reserved for the OEM's driver. */intptr_t oem[4];/** Set the swap interval for this surface.** Returns 0 on success or -errno on error.*/int (*setSwapInterval)(struct ANativeWindow* window,int interval);/** Hook called by EGL to acquire a buffer. After this call, the buffer* is not locked, so its content cannot be modified. This call may block if* no buffers are available.** The window holds a reference to the buffer between dequeueBuffer and* either queueBuffer or cancelBuffer, so clients only need their own* reference if they might use the buffer after queueing or canceling it.* Holding a reference to a buffer after queueing or canceling it is only* allowed if a specific buffer count has been set.** Returns 0 on success or -errno on error.** XXX: This function is deprecated. It will continue to work for some* time for binary compatibility, but the new dequeueBuffer function that* outputs a fence file descriptor should be used in its place.*/int (*dequeueBuffer_DEPRECATED)(struct ANativeWindow* window,struct ANativeWindowBuffer** buffer);/** hook called by EGL to lock a buffer. This MUST be called before modifying* the content of a buffer. The buffer must have been acquired with* dequeueBuffer first.** Returns 0 on success or -errno on error.** XXX: This function is deprecated. It will continue to work for some* time for binary compatibility, but it is essentially a no-op, and calls* to it should be removed.*/int (*lockBuffer_DEPRECATED)(struct ANativeWindow* window,struct ANativeWindowBuffer* buffer);/** Hook called by EGL when modifications to the render buffer are done.* This unlocks and post the buffer.** The window holds a reference to the buffer between dequeueBuffer and* either queueBuffer or cancelBuffer, so clients only need their own* reference if they might use the buffer after queueing or canceling it.* Holding a reference to a buffer after queueing or canceling it is only* allowed if a specific buffer count has been set.** Buffers MUST be queued in the same order than they were dequeued.** Returns 0 on success or -errno on error.** XXX: This function is deprecated. It will continue to work for some* time for binary compatibility, but the new queueBuffer function that* takes a fence file descriptor should be used in its place (pass a value* of -1 for the fence file descriptor if there is no valid one to pass).*/int (*queueBuffer_DEPRECATED)(struct ANativeWindow* window,struct ANativeWindowBuffer* buffer);/** hook used to retrieve information about the native window.** Returns 0 on success or -errno on error.*/int (*query)(const struct ANativeWindow* window,int what, int* value);/** hook used to perform various operations on the surface.* (*perform)() is a generic mechanism to add functionality to* ANativeWindow while keeping backward binary compatibility.** DO NOT CALL THIS HOOK DIRECTLY. Instead, use the helper functions* defined below.** (*perform)() returns -ENOENT if the 'what' parameter is not supported* by the surface's implementation.** See above for a list of valid operations, such as* NATIVE_WINDOW_SET_USAGE or NATIVE_WINDOW_CONNECT*/int (*perform)(struct ANativeWindow* window,int operation, ... );/** Hook used to cancel a buffer that has been dequeued.* No synchronization is performed between dequeue() and cancel(), so* either external synchronization is needed, or these functions must be* called from the same thread.** The window holds a reference to the buffer between dequeueBuffer and* either queueBuffer or cancelBuffer, so clients only need their own* reference if they might use the buffer after queueing or canceling it.* Holding a reference to a buffer after queueing or canceling it is only* allowed if a specific buffer count has been set.** XXX: This function is deprecated. It will continue to work for some* time for binary compatibility, but the new cancelBuffer function that* takes a fence file descriptor should be used in its place (pass a value* of -1 for the fence file descriptor if there is no valid one to pass).*/int (*cancelBuffer_DEPRECATED)(struct ANativeWindow* window,struct ANativeWindowBuffer* buffer);/** Hook called by EGL to acquire a buffer. This call may block if no* buffers are available.** The window holds a reference to the buffer between dequeueBuffer and* either queueBuffer or cancelBuffer, so clients only need their own* reference if they might use the buffer after queueing or canceling it.* Holding a reference to a buffer after queueing or canceling it is only* allowed if a specific buffer count has been set.** The libsync fence file descriptor returned in the int pointed to by the* fenceFd argument will refer to the fence that must signal before the* dequeued buffer may be written to. A value of -1 indicates that the* caller may access the buffer immediately without waiting on a fence. If* a valid file descriptor is returned (i.e. any value except -1) then the* caller is responsible for closing the file descriptor.** Returns 0 on success or -errno on error.*/int (*dequeueBuffer)(struct ANativeWindow* window,struct ANativeWindowBuffer** buffer, int* fenceFd);/** Hook called by EGL when modifications to the render buffer are done.* This unlocks and post the buffer.** The window holds a reference to the buffer between dequeueBuffer and* either queueBuffer or cancelBuffer, so clients only need their own* reference if they might use the buffer after queueing or canceling it.* Holding a reference to a buffer after queueing or canceling it is only* allowed if a specific buffer count has been set.** The fenceFd argument specifies a libsync fence file descriptor for a* fence that must signal before the buffer can be accessed. If the buffer* can be accessed immediately then a value of -1 should be used. The* caller must not use the file descriptor after it is passed to* queueBuffer, and the ANativeWindow implementation is responsible for* closing it.** Returns 0 on success or -errno on error.*/int (*queueBuffer)(struct ANativeWindow* window,struct ANativeWindowBuffer* buffer, int fenceFd);/** Hook used to cancel a buffer that has been dequeued.* No synchronization is performed between dequeue() and cancel(), so* either external synchronization is needed, or these functions must be* called from the same thread.** The window holds a reference to the buffer between dequeueBuffer and* either queueBuffer or cancelBuffer, so clients only need their own* reference if they might use the buffer after queueing or canceling it.* Holding a reference to a buffer after queueing or canceling it is only* allowed if a specific buffer count has been set.** The fenceFd argument specifies a libsync fence file decsriptor for a* fence that must signal before the buffer can be accessed. If the buffer* can be accessed immediately then a value of -1 should be used.** Note that if the client has not waited on the fence that was returned* from dequeueBuffer, that same fence should be passed to cancelBuffer to* ensure that future uses of the buffer are preceded by a wait on that* fence. The caller must not use the file descriptor after it is passed* to cancelBuffer, and the ANativeWindow implementation is responsible for* closing it.** Returns 0 on success or -errno on error.*/int (*cancelBuffer)(struct ANativeWindow* window,struct ANativeWindowBuffer* buffer, int fenceFd);
};
上述代码中有很多函数指针,比如
int (*dequeueBuffer)(struct ANativeWindow* window,struct ANativeWindowBuffer** buffer, int* fenceFd);
这些指针的具体实现在其子类中有实现和设置,这里是一种 C 的 hook 写法,而不是 c++ 的虚函数写法,可能出于兼容性考虑。
\frameworks\native\libs\nativewindow\ANativeWindow.cpp 里面并不是 ANativeWindow的实现,而是对其封装,比如:
int ANativeWindow_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, int* fenceFd) {return window->dequeueBuffer(window, buffer, fenceFd);
}
可以看到,这里调用了 ANativeWindow的dequeueBuffer函数,而这个函数在 ANativeWindow 结构里是一个函数指针,而这个函数指针又指向哪里呢?
Hook的实现者Surface
\frameworks\native\libs\gui\include\gui\Surface.h
\frameworks\native\libs\gui\Surface.cpp
class Surface: public ANativeObjectBase<ANativeWindow, Surface, RefBase>
{}
Surface 是 ANativeWindow的子类,但是考虑到 ANativeWindow通过 函数指针的方式实现多态,所以Surface里面一定有设置Hook的地方,那就是在构造函数里。
Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp): mGraphicBufferProducer(bufferProducer),mCrop(Rect::EMPTY_RECT),mBufferAge(0),mGenerationNumber(0),mSharedBufferMode(false),mAutoRefresh(false),mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT),mSharedBufferHasBeenQueued(false),mQueriedSupportedTimestamps(false),mFrameTimestampsSupportsPresent(false),mEnableFrameTimestamps(false),mFrameEventHistory(std::make_unique<ProducerFrameEventHistory>()) {// Initialize the ANativeWindow function pointers.ANativeWindow::setSwapInterval = hook_setSwapInterval;ANativeWindow::dequeueBuffer = hook_dequeueBuffer;ANativeWindow::cancelBuffer = hook_cancelBuffer;ANativeWindow::queueBuffer = hook_queueBuffer;ANativeWindow::query = hook_query;ANativeWindow::perform = hook_perform;ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED;ANativeWindow::cancelBuffer_DEPRECATED = hook_cancelBuffer_DEPRECATED;ANativeWindow::lockBuffer_DEPRECATED = hook_lockBuffer_DEPRECATED;ANativeWindow::queueBuffer_DEPRECATED = hook_queueBuffer_DEPRECATED;
至此,所有调用 ANativeWindow_xxx 的入口都会被导入 Surface 里的相应接口中,进入 hook_xxx。
int Surface::hook_dequeueBuffer(ANativeWindow* window,ANativeWindowBuffer** buffer, int* fenceFd) {Surface* c = getSelf(window);return c->dequeueBuffer(buffer, fenceFd);
}
进而在进入 Surface 的 xxx 函数。从 xxx 函数开始,和 graphic service 打交道就正式开始了。
int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {ATRACE_CALL();status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight,reqFormat, reqUsage, &mBufferAge,enableFrameTimestamps ? &frameTimestamps: nullptr);}
Graphic Buffer 服务
Surface 的 所有和 Graphic Buffer 相关的接口都会使用到成员 mGraphicBufferProducer
sp<IGraphicBufferProducer> mGraphicBufferProducer;
这个成员的类型一看就是一个 binder Interface,继续看 IGraphicBufferProducer。
\frameworks\native\libs\gui\include\gui\IGraphicBufferProducer.h
class IGraphicBufferProducer : public IInterface
{
public:using HGraphicBufferProducerV1_0 =::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer;using HGraphicBufferProducerV2_0 =::android::hardware::graphics::bufferqueue::V2_0::IGraphicBufferProducer;。。。virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h,PixelFormat format, uint64_t usage, uint64_t* outBufferAge,FrameEventHistoryDelta* outTimestamps) = 0;。。。}
至此,一个binder 模型已经形成。
Surface::mGraphicBufferProducer as Bp <--IGraphicBufferProducer BINDER--> ? as Bn
那么这里的Bn是什么呢?只需要在源码里搜一下哪些类继承IGraphicBufferProducer 即可。其中一个常用的类就是 BufferQueueProducer。
\frameworks\native\libs\gui\include\gui\BufferQueueProducer.h
class BufferQueueProducer : public BnGraphicBufferProducer,private IBinder::DeathRecipient {。。。virtual status_t queueBuffer(int slot,const QueueBufferInput& input, QueueBufferOutput* output);
。。。}
继承自BnGraphicBufferProducer,Bn开头,一看就知道是server端。
status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* outFence,uint32_t width, uint32_t height, PixelFormat format,uint64_t usage, uint64_t* outBufferAge,FrameEventHistoryDelta* outTimestamps) {sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(width, height, format, BQ_LAYER_COUNT, usage,{mConsumerName.string(), mConsumerName.size()});status_t error = graphicBuffer->initCheck();}
在上面的代码里有 new GraphicBuffer 和 initCheck() 这两个动作,这里就是在创建管理对象并进行初始化检查。
内存管理
\frameworks\native\libs\ui\include\ui\GraphicBuffer.h
class GraphicBuffer: public ANativeObjectBase<ANativeWindowBuffer, GraphicBuffer, RefBase>,public Flattenable<GraphicBuffer>
{GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,uint32_t inUsage, std::string requestorName = "<Unknown>");}
\frameworks\native\libs\ui\GraphicBuffer.cpp
GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,uint32_t inLayerCount, uint64_t inUsage, std::string requestorName): GraphicBuffer() {mInitCheck = initWithSize(inWidth, inHeight, inFormat, inLayerCount, inUsage,std::move(requestorName));
}
status_t GraphicBuffer::initWithSize(uint32_t inWidth, uint32_t inHeight,PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage,std::string requestorName)
{GraphicBufferAllocator& allocator = GraphicBufferAllocator::get();uint32_t outStride = 0;status_t err = allocator.allocate(inWidth, inHeight, inFormat, inLayerCount,inUsage, &handle, &outStride, mId,std::move(requestorName));if (err == NO_ERROR) {mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts);width = static_cast<int>(inWidth);height = static_cast<int>(inHeight);format = inFormat;layerCount = inLayerCount;usage = inUsage;usage_deprecated = int(usage);stride = static_cast<int>(outStride);}return err;
}
上面创建 Graphic Buffer 的过程中会有一个 initWithSize , 这是初始化的过程,在初始化过程里又又 allocator.allocate,可见是一个内存分配的过程。
内存分配 GraphicBufferAllocator
\frameworks\native\libs\ui\include\ui\GraphicBufferAllocator.h
\frameworks\native\libs\ui\GraphicBufferAllocator.cpp
class GraphicBufferAllocator : public Singleton<GraphicBufferAllocator>
{}
allocate 方法如下
status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height,PixelFormat format, uint32_t layerCount, uint64_t usage,buffer_handle_t* handle, uint32_t* stride,uint64_t /*graphicBufferId*/, std::string requestorName)
{。。。status_t error =mAllocator->allocate(width, height, format, layerCount, usage, 1, stride, handle);
。。。}
mAllocator 是什么 ?
sp<hardware::graphics::allocator::V2_0::IAllocator> mAllocator;
又是一个 Interface ,这次是 hardware 的,因此其实现是由硬件完成的。具体细节参考 /hardware/interfaces/graphics/allocator/2.0/
相关文章:
[Android] Graphic Buffer 的申请
前言: MediaCodec 支持 texture mode,即MediaCodec解码video完毕后把 yuv 数据填入 GPU 共享出来的 graphic buffer 里面,app 会把 video 的 yuv数据 和 ui 的数据通过通过软件渲染组件(opengl等)发送给GPU 进行一并渲染。这样做的效率较低&…...

【大数据学习 | HBASE高级】storeFile文件的合并
Compaction 操作分成下面两种: Minor Compaction:是选取一些小的、相邻的StoreFile将他们合并成一个更大的StoreFile,对于删除、过期、多余版本的数据不进行清除。 Major Compaction:是指将所有的StoreFile合并成一个StoreFile&am…...
多平台编包动态引入依赖的解决方案
最近开发时遇到了这样的需求,A 平台需要引入一个 video.js,B 平台却是不需要的,那么面向 B 平台打包的时候把依赖装进去自然就不大合适。最好的方法是动态引入依赖,根据平台来判断要不要引入 动态引入依赖 很快啊,动…...

[单例模式]
目录 [设计模式] 单例模式 1. 饿汉模式 2. 懒汉模式 3. 单例模式的线程安全问题 [设计模式] 设计模式是软件工程中的一种常见做法, 它可以理解为"模板", 是针对一些常见的特定场景, 给出的一些比较好的固定的解决方案. 不同语言适用的设计模式是不一样的. 这里…...
速盾:游戏盾的功能和原理详解
速盾有一款专注于网络游戏安全的防护系统,它通过实时监测游戏网络流量和玩家行为,以及使用先进的算法和技术进行分析和识别,检测出各种外挂、作弊行为和恶意攻击,从而保障游戏的公平性和玩家的安全性。 速盾游戏盾的主要功能包括…...
Spleeter:音频分离的革命性工具
目录 什么是Spleeter?Spleeter的工作原理Spleeter的应用场景Spleeter的技术优势Spleeter的挑战与局限性结论 什么是Spleeter? Spleeter 是一个由 Deezer 开发的开源音频源分离工具。它基于深度学习技术,尤其是卷积神经网络(CNN&a…...

【笔记】自动驾驶预测与决策规划_Part6_不确定性感知的决策过程
文章目录 0. 前言1. 部分观测的马尔可夫决策过程1.1 POMDP的思想以及与MDP的联系1.1.1 MDP的过程回顾1.1.2 POMDP定义1.1.3 与MDP的联系及区别POMDP 视角MDP 视角决策次数对最优解的影响 1.2 POMDP的3种常规解法1.2.1 连续状态的“Belief MDP”方法1. 信念状态的定义2. Belief …...
openresty入门教程:access_by_lua_block
在OpenResty中,access_by_lua_block 是一个功能强大的指令,它允许你在Nginx的访问控制阶段执行Lua脚本。这个阶段发生在Nginx处理请求的过程中,紧接在rewrite阶段之后,但在请求被传递到后端服务器(如PHP、Node.js等&am…...
Caused by: org.apache.flink.api.common.io.ParseException: Row too short:
Flink版本 1.17.2 错误描述 Caused by: org.apache.flink.api.common.io.ParseException: Row too short: 通过flink中的flinkSql直接使用对应的connector去获取csv文件内容,报获取的数据太短了 可能原因 1.创建的表字段多于csv文件当中的表头 定位 在获取csv…...
hbase的安装与简单操作
好的,这里是关于 HBase 的安装和基本操作的详细步骤,分成几个更清晰的阶段: 第一部分:安装和配置 HBase 1. 环境准备 HBase 依赖于 Hadoop,因此首先确保 Hadoop 已经正确安装和配置。如果没有安装,请先下…...

PySpark本地开发环境搭建
一.前置事项 请注意,需要先实现Windows的本地JDK和Hadoop的安装。 二.windows安装Anaconda 资源:Miniconda3-py38-4.11.0-Windows-x86-64,在window使用的Anaconda资源-CSDN文库 右键以管理员身份运行,选择你的安装路径&#x…...

【进阶】Stable Diffusion 插件 Controlnet 安装使用教程(图像精准控制)
Stable Diffusion WebUI 的绘画插件 Controlnet 最近更新了 V1.1 版本,发布了 14 个优化模型,并新增了多个预处理器,让它的功能比之前更加好用了,最近几天又连续更新了 3 个新 Reference 预处理器,可以直接根据图像生产…...
调试、发布自己的 npm 包
查看 npm 的配置 npm config ls登录 whoami 查看当前登录的用户 npm whoamiaduser 登录 adduser 有以下参数: –scope 作用域–registry 注册地址 默认地址:https://registry.npmjs.org/,也可通过.npmrc文件配置 npm login 是 …...
拓扑学与DNA双螺旋结构的奇妙连接:从算法到分子模拟
拓扑的形变指的是通过连续地拉伸、弯曲或扭曲物体而不进行撕裂或粘合来改变其形状的一种数学变换。拓扑形变属于拓扑学的一个分支,研究在这些操作下保持不变的性质。简单来说,它关注的是物体“形状的本质”,而不是具体的几何形状。 拓扑形变…...
mysql数据库(四)单表查询
单表查询 文章目录 单表查询一、单表查询1.1 简单查询1.2where1.3group by1.4having1.5order by1.6limit 一、单表查询 记录的查询语法如下: SELECT DISTINCT(去重) 字段1,字段2… FROM 表名 WHERE 筛选条件 GROUP BY 分组 HAVING 分组筛选 ORDER BY 排序 LIMIT 限…...

JavaEE初阶---properties类+反射+注解
文章目录 1.配置文件properities2.快速上手3.常见方法3.1读取配置文件3.2获取k-v值3.3修改k-v值3.4unicode的说明 4.反射的引入4.1传统写法4.2反射的写法(初识)4.3反射的介绍4.4获得class类的方法4.5所有类型的class对象4.6类加载过程4.7类初始化的过程4…...

HarmonyOS一次开发多端部署三巨头之功能级一多开发和工程级一多开发
功能级一多开发与工程级一多开发 引言功能级一多开发SysCaps机制介绍能力集canlUse接口 工程级一多开发三层架构规范 引言 一次开发多端部署 定义:一套代码工程,一次开发上架,多端按需部署 目标:支撑开发者快速高效的开发多终端设…...
STL常用遍历算法
概述: 算法主要是由头文件<algorithm> <functional> <numeric>组成。 <algorithm>是所有STL头文件中最大的一个,范围涉及到比较、 交换、查找、遍历操作、复制、修改等等 <numeric>体积很小,只包括几个在序列上面进行简…...
前端开发中常见的ES6技术细节分享一
var、let、const之间有什么区别? var: 在ES5中,顶层对象的属性和全局变量是等价的,用var声明的变量既是全局变量,也是顶层变量 注意:顶层对象,在浏览器环境指的是window对象,在 Node 指的是g…...

行业类别-智慧城市-子类别智能交通-细分类别自动驾驶技术-应用场景城市公共交通优化
1.大纲分析 针对题目“8.0 行业类别-智慧城市-子类别智能交通-细分类别自动驾驶技术-应用场景城市公共交通优化”的大纲分析,可以从以下几个方面进行展开: 一、引言 简述智慧城市的概念及其重要性。强调智能交通在智慧城市中的核心地位。引出自动驾驶…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...

ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...

JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...
安卓基础(Java 和 Gradle 版本)
1. 设置项目的 JDK 版本 方法1:通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分,设置 Gradle JDK 方法2:通过 Settings File → Settings... (或 CtrlAltS)…...
用鸿蒙HarmonyOS5实现中国象棋小游戏的过程
下面是一个基于鸿蒙OS (HarmonyOS) 的中国象棋小游戏的实现代码。这个实现使用Java语言和鸿蒙的Ability框架。 1. 项目结构 /src/main/java/com/example/chinesechess/├── MainAbilitySlice.java // 主界面逻辑├── ChessView.java // 游戏视图和逻辑├──…...