news 2026/10/26 3:22:23

IntelliJ插件开发上卷指南:从Gradle工程到Action与Tool Window实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
IntelliJ插件开发上卷指南:从Gradle工程到Action与Tool Window实战

简介:这是一份面向 Intellij IDEA 插件开发者的系统手册上册,基于 JetBrains Runtime 17.0.9,适用于 IDEA 2023 及以上版本(兼容 2024)。手册从零讲解插件开发基础,梳理了 IDE 插件类型、开发环境要求、开发流程与官方参考网站等预备知识;随后通过“开发第一个插件”章节,逐步演示工程创建、插件配置、测试调试等完整链路,并深入介绍图形化插件开发,可用于框架集成、代码统计、效率工具等 UI 类插件的编写。资源仅包含 1 个 PDF 文件,大小 15.82MB,目录层级分明,方便按需查阅对应章节。上册定位为入门到进阶,适合具备 Java 基础、希望扩展 IDE 功能的开发者;若结合下册语言类插件与附录工具清单,可继续探索代码自动完成、依赖管理、代码检查等高级收费插件方向。目前已有 383 人学习下载,全书基于官方指导与项目实践整理,可作为日常开发的案头参考。

1. 做 IntelliJ 插件的第一个 30 分钟:这份手册解决了什么

很多人开始做 Idea 插件开发,第一步不是写代码,而是卡在环境上。SDK 加载不出来、Gradle 任务起不来、plugin.xml 满屏报红,任一条都能耗掉你整个下午。这份《Intellij Platform PlugIn插件开发手册(上)》把这段最劝退的路提前趟平了:从 IntelliJ Platform 工程的初始化讲起,把 ActionSystem、Tool Window、PSI 这些必踩的地基按“能跑通”的顺序串了起来。它适合两类人:一类是给团队做内部工具链、想少走弯路快速交出第一个可用插件的开发者;另一类是写过几个插件但底子不牢、想系统理一遍 IDE 扩展机制的熟手。上卷一般不涉及打包签名和上架,那部分通常留在下卷,但能把上卷吃透,你的调试循环就已经比大多数半路出家的人快一截。

2. 插件工程怎么立起来:Gradle 构建、DevKit 与版本选型

2.1 为什么是 Gradle IntelliJ Plugin 而不是 DevKit

早期插件开发的主流方式是用 IDE 自带的 Plugin DevKit,在 Project Structure 里手动添加 IntelliJ Platform SDK,把安装目录里的 lib 整个挂进来。这套方式的痛点是版本敏感:切换 IDE 版本要重新配 SDK;依赖下载不完整或者缓存损坏,IDE 会直接报基础组件缺失,比如提示需要安装 J2SE plugin 版本之类的错误,和业务代码一点关系都没有,纯粹是环境问题。

现在官方和社区的主流做法已经统一到 Gradle + IntelliJ Gradle Plugin。SDK 按intellij.version动态拉取,不手工维护;构建、打包、验证都有现成任务。用 Gradle 方案有四个实际收益:依赖版本能写进构建脚本,换机器不慌;runIde、buildPlugin、verifyPlugin覆盖开发到验证全流程;patchPluginXml能动态改写 plugin.xml 里的兼容版本区间;本地跑一次clean build直接产出可分发的 zip。这些正是做 idea 插件开发步骤里最容易出错、也最该模板化的部分。

选型时注意版本绑定关系:IntelliJ Gradle Plugin 的版本、IDE 版本、JDK 版本三者互相约束。最省事的记忆方式是用“和你的主力 IDE 发布年份接近”的插件版本。比如 IDE 用 2023.2、本地 JDK 是 17,那么org.jetbrains.intellij用 1.16.x 或 1.17.x 都能正常跑通。版本对不上最常见的症状是 Gradle 同步报“Could not resolve dependencies”或runIde启动后插件列表空白。

2.2 最小工程文件:build.gradle 与 plugin.xml

新建插件项目我一般直接在一个空目录里手写build.gradle、settings.gradle、gradle-wrapper.properties,再用 IntelliJ 的 Open 打开这个目录,让 IDE 以 Gradle 工程识别。与其在 UI 向导里一步步点,不如从官方项目模板抄一个最小结构,后面改起来更可控。最小可跑的build.gradle长这样:

plugins { id 'java' id 'org.jetbrains.intellij' version '1.16.0' } group 'com.example' version '1.0.0' repositories { mavenCentral() } java { sourceCompatibility = '17' targetCompatibility = '17' } intellij { version = '2023.2.5' type = 'IC' plugins = ['com.intellij.java'] } patchPluginXml { sinceBuild = '232' untilBuild = '242.*' }

逻辑说明:外层plugins块是构建工具插件,id 'java'让工程能编译 Java 源码,org.jetbrains.intellij负责把 IntelliJ Platform SDK 挂载到编译和运行环境。java.sourceCompatibility设为 17 是 2023.x 系列的安全选择,因为 IDE 自带的 JBR 就是 17 起的。intellij.version决定当前工程面向哪个 IDE 版本构建,type两个值:IC是社区版,IU是旗舰版,旗舰版才有的类在社区版 SDK 里编译不过。plugins数组写的是 IDE 内置插件 ID,比如要写 Java 语言相关功能就依赖com.intellij.java,做前端方向就依赖com.intellij.javascript。

patchPluginXml里的sinceBuild、untilBuild是插件兼容区间。232 对应 2023.2 的内部 build 号,242 对应 2024.2。区间写得过窄会把插件拦在部分 IDE 之外,写得过宽又可能在 IDE 升级后触发未知的 API 不兼容,所以每次调完版本都要跑一次verifyPlugin确认边界。

再往下是src/main/resources/META-INF/plugin.xml,这是插件的身份证:

<idea-plugin> <id>com.example.demo</id> <name>Demo Plugin</name> <vendor>Example</vendor> <description>一个演示插件,带你跑通开发闭环。</description> <depends>com.intellij.modules.platform</depends> <extensions defaultExtensionNs="com.intellij"> <!-- 扩展点写在这里 --> </extensions> <actions> <!-- Action 注册写在这里 --> </actions> </idea-plugin>

说明:<id>在插件市场里是唯一键,一旦发布不要改;<depends>只写com.intellij.modules.platform是最小依赖,代表只要编辑器核心,不带 Java 或前端语言支持,要用 Java PSI 就得额外依赖com.intellij.java。<extensions>和<actions>分别挂扩展点和行为,SDK 按声明顺序解析,这个顺序问题在避坑章节还要展开。

2.3 用 runIde 跑通调试沙箱

配置写完后,第一件事不是写功能代码,而是确认环境能起来:

./gradlew clean build ./gradlew runIde

第一条命令做全量编译和打包,产物在build/distributions下。第二条命令启动一个独立的 IDE 沙箱实例,这个实例的配置目录、索引目录、插件目录全部隔离在build/idea-sandbox下,不会污染你日常开发用的 IDEA。沙箱启动后,随便打开一个项目,到 Settings 里 Plugins 查看 Installed,应该能看到 Demo Plugin 已经在列表里。如果找不到,直接跳去第 4 章排查第一条。

runIde还支持透传 JVM 和 IDE 属性,常见写法是:

./gradlew runIde -Didea.config.path=build/config -Didea.system.path=build/system

idea.config.path和idea.system.path把沙箱的配置和索引目录单独划分出来。不写也能跑通,但我反复调试时一定带上,因为默认情况下沙箱重建索引会吃掉好几分钟,这时间浪费得毫无价值。

3. 把功能挂进 IDE:Action、Tool Window 与事件扩展

3.1 AnAction:菜单、工具栏、右键菜单的三连注册

IntelliJ Platform 的交互入口统一抽象为 AnAction。你要做的只有两步:写一个继承 AnAction 的类,在 plugin.xml 里把它注册到某个 UI 容器。先看类:

package com.example.demo; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; public class HelloAction extends AnAction { @Override public void actionPerformed(AnActionEvent e) { Project project = e.getProject(); Messages.showMessageDialog(project, "Hello from plugin", "Demo", Messages.getInformationIcon()); } }

逻辑说明:actionPerformed是点击菜单或按下快捷键时的回调,AnActionEvent携带当前上下文,包括 project、文件和编辑器实例。这里通过e.getProject()拿到工程对象,用 Messages 弹了个对话框,这是验证 Action 是否被 IDE 正确加载的最快方式。

注册到右键菜单:

<actions> <action id="com.example.demo.HelloAction" class="com.example.demo.HelloAction" text="Say Hello" description="弹出一个问候框"> <add-to-group group-id="EditorPopupMenu" anchor="last"/> </action> </actions>

参数说明:class必须能反射到刚才写的类,id在整个 IDE 范围内唯一。add-to-group决定挂载位置,EditorPopupMenu是编辑器右键菜单,ProjectViewPopupMenu是项目树右键,MainToolBar是工具栏,MainMenu是主菜单栏。anchor支持first、last、before、after,用before或after时还要加relative-to-action指定锚点 Action。注册完右键编辑器,菜单底部就应该出现 Say Hello。如果没出现,先按 Ctrl+Shift+A 打开 Actions 搜索框,输入类名里的关键词,如果能在列表里搜到,说明 Action 已加载,只是菜单位置不对;搜不到就是 plugin.xml 解析失败。

3.2 Tool Window:创建一个能刷新的自定义面板

Tool Window 是插件和 IDE 左侧/右侧/底部侧边栏的入口,适合放树、表格、日志流这类常驻内容。实现一个 Tool Window 只需要实现ToolWindowFactory:

package com.example.demo; import com.intellij.openapi.project.Project; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowFactory; import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentFactory; import javax.swing.*; public class DemoToolWindowFactory implements ToolWindowFactory { @Override public void createToolWindowContent(Project project, ToolWindow toolWindow) { JPanel panel = new JPanel(); panel.add(new JLabel("这是插件面板")); Content content = ContentFactory.getInstance() .createContent(panel, "Main", false); toolWindow.getContentManager().addContent(content); } }

注意ContentFactory的取法:2021.3 之前是ContentFactory.SERVICE.getInstance(),之后统一为ContentFactory.getInstance()。如果你抄到老代码,在较新的 SDK 下会直接编译失败,这是插件开发里常见的代差坑。

plugin.xml 里对应的注册:

<extensions defaultExtensionNs="com.intellij"> <toolWindow id="DemoPane" anchor="right" secondary="false" factoryClass="com.example.demo.DemoToolWindowFactory"/> </extensions>

参数说明:anchor取值left、right、bottom,secondary为 true 时面板会和同方向已有面板合并到 tab 标签页里。factoryClass指向刚才实现的工厂类。Tool Window 最大的特点是内容面板是普通 Swing/JComponent,你可以往里塞 JTable、JTree、JEditorPane,也可以挂按钮和输入框,它能承载的 UI 复杂度比 Action 弹窗高一个量级。

3.3 扩展点与 Listener:插件被 IDE 事件驱动的机制

Action 是用户主动发起的,Tool Window 是常驻 UI,那“项目打开时自动执行”“保存文件时触发检查”这类场景怎么实现?答案是扩展点与 Listener。扩展点本质上是在 plugin.xml 的<extensions>里声明的自定义实现,SDK 在特定时机实例化并调用。常见的扩展点包括:applicationService、projectService、toolWindow、editorNotificationProvider、lineMarkerProvider。

监听器则面向事件驱动。以最常见的“项目打开时初始化”为例:

package com.example.demo; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManagerListener; import org.jetbrains.annotations.NotNull; public class DemoProjectListener implements ProjectManagerListener { @Override public void projectOpened(@NotNull Project project) { System.out.println("插件感知到项目打开:" + project.getName()); } }

plugin.xml 注册:

<extensions defaultExtensionNs="com.intellij"> <projectManagerListener implementation="com.example.demo.DemoProjectListener"/> </extensions>

说明:projectManagerListener扩展点会让 SDK 在项目打开时回调projectOpened。这里的实现类生命周期由平台管理,不要在监听器里持有重型资源,因为项目打开和关闭是高频操作。更精细的监听比如文件保存、编辑器光标移动,都有各自对应的扩展点,核心套路一致:实现接口 → 在 plugin.xml 里注册 → 在回调里消费事件。把这三步走熟,插件才真正算是“跟 IDE 长在了一起”,而不是一个孤立的弹窗。

4. 插件开发避坑与排查:从编译失败到运行翻车的 5 个高频问题

4.1 现象:runIde 启动后插件列表里找不到自己的插件

原因:最常见的是META-INF/plugin.xml放错了目录,Gradle 只认src/main/resources/META-INF/plugin.xml,放成resources/plugin.xml或以其他路径存在都不会被当成插件描述文件。其次是intellij.plugins里声明了当前 IDE 版本不存在的依赖插件 ID,导致 SDK 加载阶段就中断,整个插件被静默丢弃。

解决:先看build/idea-sandbox/plugins目录,确认插件 jar 有没有被复制进去;没有 jar 就检查build.gradle的sourceSets和资源目录结构。有 jar 但列表里不显示,打开沙箱里的idea.log(在build/idea-sandbox/system/log下),搜插件的 id,插件描述解析失败时日志会直接给异常栈。

4.2 现象:plugin.xml 里写了 action,右键菜单却不出现

原因:这个坑通常不在 XML,而在类加载。class属性写成了内部类或包名错误,反射实例化失败后 IDE 只会在日志里记一笔;另一个高频原因是把<add-to-group>写进了<extensions>而不是<actions>,SDK 对这两个标签的解析路径完全不同,写错位置不会报错,只是静默忽略。还有一种场景:EditorPopupMenu 只在编辑器获得焦点时出现,如果你在项目树上点右键,看到的自然是 ProjectViewPopupMenu,两者的菜单内容是两套。

解决:用 Ctrl+Shift+A 搜索 Action 类名。能搜到就说明 Action 已注册,问题在菜单分组,检查 group-id 和 anchor;搜不到就回到 plugin.xml 确认注册位置,并检查类的全限定名是否和package一致。再不行,在actionPerformed里加个System.out.println,断点打上,确认 IDE 是否真的实例化了这个类。

4.3 现象:编译报 package com.intellij.openapi.actionSystem does not exist

原因:SDK 没有挂到编译 classpath。常见于org.jetbrains.intellij插件版本太老、intellij.version解析失败,或者本机 Gradle 处于离线模式,依赖没有提前缓存在~/.gradle/caches。也见过有人把type设置成IU,但网络刚好拉不下来旗舰版 SDK,Gradle 静默降级失败后给出成片 cannot find symbol。

解决:先跑./gradlew clean build --refresh-dependencies,排除缓存问题;确认plugins块的版本号能访问 Maven Central 或 JetBrains Plugin Repository;最后把intellij.version换成你本机已安装的 IDE 版本号,比如 2023.2.5,通常能绕开下载障碍。这类问题一旦解决,后续编译基本不会再回到这个坑。

4.4 现象:沙箱 IDE 直接崩溃,或者启动后秒退

原因:多数是 JDK 版本不匹配。IntelliJ Platform 2021.1 之后要求 JDK 11 以上,2022.3 之后建议 17;如果你本机 Gradle JVM 是 Java 8,runIde启动 JBR 时会直接崩。第二个常见原因是内存不足,沙箱 IDE 默认堆太小,插件加载到一半被 OOM 杀掉。第三个是本地杀毒或监控工具拦截了 JBR 的临时文件写入,这类问题最玄学,报错没有规律。

解决:在gradle.properties里显式固定 Gradle JVM 参数:

org.gradle.java.home=/path/to/jdk17 org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=1024m

同时在build.gradle里给 runIde 加大堆:

runIde { jvmArgs '-Xmx2048m', '-XX:MaxMetaspaceSize=1024m' }

如果还崩,去build/idea-sandbox/system/log里看idea.log和hs_err_pid*.log,hs_err 文件会把崩溃点和当前线程栈打出来,大部分情况能定位到具体是哪个类触发的。

4.5 现象:升级 IDE 后插件失效,提示版本不兼容

原因:sinceBuild/untilBuild区间没有覆盖新 IDE,或者插件用了旧版 API,新平台已经移除。IntelliJ Platform 对 API 兼容性管理严格,很多接口标记@Deprecated之后还会保留几个大版本,但有些内部 API 说删就删,编译能过不代表运行时不会抛NoSuchMethodError。

解决:升级插件工程时不要只在patchPluginXml里把untilBuild改大。正确路径是:先把intellij.version升到目标 IDE 版本,重新编译,解决所有废弃 API 警告,然后跑./gradlew verifyPlugin,这个任务会校验插件描述、图标和版本边界。最后用buildPlugin打包,在目标 IDE 版本的真实环境里做一轮冒烟测试,比直接放开版本区间可靠得多。

5. 上卷手册的路要怎么读:从搭建环境到提交审核的验证清单

5.1 上卷的章节结构:按这个顺序读最省时间

资源包里的这份手册是“上卷”,按常见编排,它会从环境准备一路讲到 PSI 基础,把插件开发的地基部分覆盖完。我建议按下面这个顺序读,而不是拿到就从中间翻开:

常见分章核心内容建议用时动手要求
环境准备Gradle 工程初始化、SDK 加载30 分钟跑通 runIde
ActionSystemAnAction、菜单分组、快捷键1 小时给右键菜单加一个项
Tool Window面板布局、内容填充1 小时做一个带按钮的面板
PSI 基础PsiFile、PsiElement、Visitor 遍历2 小时写一个类统计脚本
调试与日志沙箱目录、idea.log、断点30 分钟用日志代替 print

注意一个边界:语言注入、框架支持、打包签名这些通常放在下卷。所以读上卷时不要指望看到发布上架的完整流程,那不是缺陷,是分册定位。上卷的核心目标就是让你在任何一台机器上都能把插件工程跑起来、调得动、看得见效果。

5.2 把 Action 和 Tool Window 串成一个完整工程

很多人读手册时每个章节都看懂了,合上书还是不知道从哪下手。我的做法是准备一个空工程,把手册前五章的代码一步一步合到一起,形成一份最小但完整的 plugin.xml,落地的效果是:右键菜单弹窗 + 一个右侧 Tool Window 面板 + 项目打开时的日志打印。

<idea-plugin> <id>com.example.demo</id> <name>Demo Plugin</name> <description>手册配套验证工程</description> <depends>com.intellij.modules.platform</depends> <extensions defaultExtensionNs="com.intellij"> <toolWindow id="DemoPane" anchor="right" factoryClass="com.example.demo.DemoToolWindowFactory"/> <projectManagerListener implementation="com.example.demo.DemoProjectListener"/> </extensions> <actions> <action id="com.example.demo.HelloAction" class="com.example.demo.HelloAction" text="Say Hello" description="弹窗验证"> <add-to-group group-id="EditorPopupMenu" anchor="last"/> </action> </actions> </idea-plugin>

说明:这份 XML 把第 3 章里三件事全部串在一起,extensions里挂 Tool Window 和项目监听器,actions里挂右键菜单。工程能编译、能启动、三个功能都能触达,就说明你对 IntelliJ Platform 的基础扩展模型已经建立了肌肉记忆。之后每读一个新主题(比如 PSI、Line Marker),就把它也加进这个工程,保持“边读边扩”的节奏,比单独看代码样例有效得多。

5.3 提交审核前的验证清单

手册读完了,功能也写完了,别急着打包。我每次发新版本前都强制跑一遍这几项检查:

./gradlew verifyPlugin ./gradlew buildPlugin ./gradlew buildSearchableOptions

verifyPlugin检查插件描述、图标、依赖、版本区间的合法性;buildPlugin产出待分发的 zip;buildSearchableOptions生成设置在 Settings 搜索框里的索引,不跑这个,用户在设置里搜不到你插件的配置项,这个坑很多人压根不知道。

清单也就四到五项:插件描述里是否写清了适用场景;图标是否同时准备了 16x16 和 32x32 两种尺寸;sinceBuild、untilBuild是否覆盖了目标用户群;沙箱里新建一个项目做冒烟测试;idea.log里有没有非预期异常。把这些都过完,插件才算真正“能交出去”。

6. 进阶一步:动态插件与状态持久化,把手册没讲的边界补上

6.1 dynamic 属性:热卸载的前提

手册上卷通常不会细讲动态插件机制,但这个属性直接影响开发体验。在 plugin.xml 根元素上加上dynamic="true",插件就支持在 IDE 运行状态下卸载和重装,调试迭代时不用反复重启沙箱。代价是插件代码必须能正确释放资源:监听器要取消注册、后台线程要停掉、不要持有静态引用。做不到这些,卸载时 IDE 会打印泄漏警告,久而久之会导致运行期状态错乱。我的习惯是第一版就把这个属性写上,强制自己从一开始就养成“可回收”的写码方式。

6.2 PersistentStateComponent:把插件状态写进配置目录

另一个容易被忽略的是状态持久化。插件里记录的配置项,比如上次选中的路径、用户偏好,不能每次启动都从零开始。IntelliJ Platform 提供了标准方案:

@State(name = "DemoPluginState", storages = @Storage("demoPluginState.xml")) public class DemoPluginState implements PersistentStateComponent<DemoPluginState> { public String lastSelectedPath = ""; public boolean firstRun = true; @Override public @NotNull DemoPluginState getState() { return this; } @Override public void loadState(@NotNull DemoPluginState state) { this.lastSelectedPath = state.lastSelectedPath; this.firstRun = state.firstRun; } }

注册成 applicationService:

<extensions defaultExtensionNs="com.intellij"> <applicationService serviceImplementation="com.example.demo.DemoPluginState"/> </extensions>

说明:这个类的实例本身就是状态对象,字段就是持久化字段,SDK 会在合适的时机把实例序列化到配置目录下的demoPluginState.xml。getState返回当前状态,loadState在启动加载时把磁盘上的值塞回来。相比自己读写 properties 文件,这个方案完全由平台管理 IO 时机,不会在 IDE 写配置时造成文件冲突。注意所有字段都要有默认值,因为老用户升级插件后,XML 里可能没有新字段。

这块我之前也翻过车。最初做内部工具链插件时,所有配置都丢在内存里,用户每次重启 IDE 都要重新选路径,被组里同事吐槽了两周。后来在源码里看到PersistentStateComponent,才意识到平台早就给了后悔药。从那以后,我每个新插件第一版就把dynamic="true"写上,同时把 State 类定义好,哪怕字段是空的也先占住坑。改插件、升级、再验证,整条链路稳了很多,沙箱里来回重启的时间也省下来了。希望这份上卷手册能帮你把同样的弯路避开。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/25 23:56:42

800G光模块选型实战指南:SR8/DR8/2FR4工程决策手册

1. 项目概述&#xff1a;为什么800G光模块正在从“可选”变成“必选”最近三个月&#xff0c;我连续跑了六家数据中心客户现场&#xff0c;有做AI训练集群的&#xff0c;有部署超大规模云原生平台的&#xff0c;也有在建新一代智算中心的。几乎每一家都在问同一个问题&#xff…

作者头像 李华
网站建设 2026/10/26 3:21:55

SpringBoot+Thymeleaf打造游戏百科系统:服务端渲染与数据建模实战

1. 技术选型的实际考量&#xff1a;为什么这套系统我没有走前后端分离 1.1 从"游戏百科信息系统"反推技术栈 这次的项目目标很明确&#xff1a;用 SpringBoot 基于 JavaWeb 技术栈做一套《战舰世界》游戏百科信息系统&#xff0c;把舰船资料、国家线、舰种分类、科技…

作者头像 李华
网站建设 2026/10/26 3:22:00

TINA仿真不收敛?功率MOSFET瞬态仿真五大排查步骤详解

1. 问题现象与根因剖析 1.1 不收敛的表象 在TINA里跑瞬态仿真&#xff0c;不收敛很少是“突然死机”式的崩溃&#xff0c;而是一步步给你添堵。最常见的场景是&#xff1a;仿真进度条走到某个时间点就停住&#xff0c;接着弹窗提示“Time step too small”或者“Minimum step …

作者头像 李华
网站建设 2026/10/26 3:22:00

Flutter鸿蒙跨平台开发实战:门店查询App从架构到打包

先交代一下背景。我最近接了一个偏工具型的应用项目&#xff1a;全国手账实体店查询。用户打开 App 能看到附近的手账专卖店、文具集合店、文创杂货铺&#xff0c;支持按省市筛选、地图标记、门店详情和收藏。产品提的需求很明确——鸿蒙要跑&#xff0c;iOS 和 Android 也要跑…

作者头像 李华
网站建设 2026/10/26 3:22:00

毕业论文AI工具实测推荐:8个网站搞定文献综述与降重

每年3月到5月&#xff0c;我的私信里会被同一种问题塞满&#xff1a;“老师&#xff0c;毕业论文写不出来了&#xff0c;有没有靠谱的AI论文网站推荐&#xff1f;”说实话&#xff0c;我理解这种焦虑&#xff1a;开题报告赶完了&#xff0c;文献综述像一座山&#xff0c;查重红…

作者头像 李华
网站建设 2026/10/25 19:27:03

DRAM存储原理与刷新机制:从1T1C结构到内存暂存技术

2. 从存储层级看DRAM的位置&#xff1a;为什么它叫“内存” 聊DRAM之前&#xff0c;得先把存储层级这件事捋清楚。很多人在学习计算机体系结构时&#xff0c;第一次接触存储金字塔&#xff08;Storage Hierarchy&#xff09;往往会有个困惑&#xff1a;寄存器、Cache、内存、硬…

作者头像 李华