news 2026/10/2 8:53:15

Android备忘录App开发指南:SQLite存储与期末大作业高分实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Android备忘录App开发指南:SQLite存储与期末大作业高分实践

简介:这份移动开发期末大作业的备忘录应用项目包,面向计算机相关专业学生、课程设计人员及移动开发入门者,可作为期末大作业或实战练习的完整参考。项目经导师指导并获评九十八分,所有源码均在本地编译通过并严格调试,确保能够稳定运行。压缩包共六十七个文件,大小约四点零七兆字节,内含二十二个xml界面布局、十个java核心逻辑、十个webp预览图及若干jpg/png项目截图,另附gradle构建配置、properties参数、apk安装包和Android导入步骤及问题解决办法文档,目录结构清晰,便于快速查阅与工程导入。附带的导入文档与截图能帮助还原项目环境,减少配置排错时间,适合需要高质量模板的学生直接使用。当前已有一百五十人学习下载,综合评价较高,很适合作为期末大作业或课程设计的参考资源。

1. 一个备忘录App凭什么拿高分:先看懂评分点在交付物上

期末周接到“移动开发期末大作业-备忘录app”时,第一反应往往是“这题太简单了”。但反过来想,这个题目恰恰是移动应用开发课程里性价比最高的一道题:它同时覆盖界面搭建、数据存取、增删改查、生命周期和状态切换,任何一个环节都能做出区分度。项目名里“含导入文档和项目截图”不是附件要求,而是三个独立评分点——工程能不能在别人电脑上跑起来、截图有没有展示关键交互、导入文档能不能帮评委一次打开项目,这三点甚至比功能本身更影响分数。这篇笔记把一条能直接照做的路径拆开讲:技术选型怎么定不翻车,代码按什么顺序写,数据表怎么设计,以及演示时怎么避免当场卡壳。适合用 Android Studio 做原生开发的读者,也适合想过一次通过、还想冲高分的同学。

2. 技术选型与项目骨架:原生Android路线下,两Activity分包最省心

2.1 先看评分表再定技术栈:为什么原生Android最稳

期末大作业的技术栈选择,优先考虑的不是“哪个框架新”,而是“老师按什么标准给分”。大多数院校的移动应用开发课纲以 Android 原生 Java/Kotlin 为主线,作业验收时老师会直接打开工程看代码结构。这种情况下,选 Flutter 或 React Native 等于给自己增加两道额外的坎:一是课纲里没讲,代码很难自圆其说;二是这两套工具链的依赖体积更大,导入到老师电脑上同步失败的风险更高。

如果你是按移动应用开发技能大赛那种风格准备的,用 Kotlin 写确实更有说服力,但期末节点还是以课程评分表为准。三种路线的取舍可以这样看:

技术栈课程贴合度排错难度演示信任感适合场景
Android 原生 Java/Kotlin高,课纲内容直接覆盖低,报错信息网上全高,评委能看懂每一步绝大多数期末大作业
Flutter / React Native中,课纲一般不涉及高,遇到环境问题耗时中,容易被追问原理做过完整跨平台项目的学生
网页套壳 / 低代码拖拽低,明显偏离课程低,但一问就露馅低,基本无法答辩不推荐

我的建议很直接:用原生 Java 写。Java 在期末阶段比 Kotlin 更保守——Kotlin 的协程、扩展函数如果写不好,答辩时反而被问住;Java 代码啰嗦,但每一行都能解释清楚。技术栈选保守,不是能力问题,是风险控制问题。

2.2 项目骨架:两个Activity加一个工具类,够用到答辩

很多同学一上来就建三个 Activity:列表、编辑、详情。但详情页面在备忘录场景里完全可以复用编辑页——详情就是在只读模式下的编辑页。少一个 Activity 就少一套布局、少一条 Intent 传参路径、少一个生命周期坑。所以我一般会用两 Activity 结构:MainActivity负责列表和搜索,EditActivity负责新增和编辑,数据通过 Intent 的 extra 传递。

MemoApp/ ├── app/ │ ├── src/main/ │ │ ├── java/com/example/memo/ │ │ │ ├── model/MemoBean.java │ │ │ ├── db/DBHelper.java │ │ │ ├── db/MemoDao.java │ │ │ ├── adapter/MemoAdapter.java │ │ │ ├── ui/MainActivity.java │ │ │ ├── ui/EditActivity.java │ │ │ └── util/TimeUtil.java │ │ ├── res/layout/ │ │ │ ├── activity_main.xml │ │ │ ├── activity_edit.xml │ │ │ └── item_memo.xml │ │ └── AndroidManifest.xml │ └── build.gradle ├── 导入文档.md ├── 项目截图/ └── build.gradle

这个分包思路背后是简单的分层:model只放数据载体,db里DBHelper管建库建表、MemoDao管增删改查语句,adapter只负责把数据绑到列表项上,util放时间格式化这类公共方法。答辩时老师问“你的项目怎么组织的”,照这个结构说就能讲清楚职责边界。

项目截图/和导入文档.md放在工程根目录,而不是塞进src里。这样老师打开工程文件第一眼就能看到交付物,不用钻进代码里找。截图放四到五张关键界面就够:主页列表、编辑页、搜索效果、置顶和删除后的状态。

2.3 能让老师直接导入的工程:Gradle、SDK和导入文档

“能导入”是大作业的生命线。工程在你机器上编译通过不算数,要在老师电脑上也能打开才算。最常见的翻车原因是 Gradle 版本、SDK 版本和依赖仓库不一致。我建议 build.gradle 里的版本号按你本机 Android Studio 能跑通的写,不要盲目追新:

android { namespace "com.example.memo" compileSdk 33 defaultConfig { applicationId "com.example.memo" minSdk 24 targetSdk 33 versionCode 1 versionName "1.0" } } dependencies { implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.recyclerview:recyclerview:1.3.2' implementation 'com.google.android.material:material:1.9.0' }

注意两点。第一,compileSdk和targetSdk保持一致最稳,当前用的是 33 的话,模拟器和老师的测试机一般都在覆盖范围内。第二,依赖尽量精简,不引入 Room、Navigation 这类重组件——期末项目用不上,反而把同步时间拉长。如果 Gradle 同步卡住,检查镜像仓库配置或切换离线模式,导入文档里要把这一步写清楚。

“导入文档.md”不是项目说明书,而是“后悔药文档”。第一段写环境要求,第二段写“编译失败先查这三处”:JDK 版本、SDK 路径、依赖仓库是否可达。第三段才是功能简介。写的时候把自己当成一个刚拿到工程的人,照着文档走一遍,看能不能一口气编译过。

3. 核心功能落地的关键代码:增删改查、搜索和提醒代码怎么拆

3.1 首页列表:当老师第一眼看到的是RecyclerView

主界面用 RecyclerView 展示备忘录列表,这是移动端列表的标准做法。不要用 ListView——虽然 ListView 也能跑,但 RecyclerView 的复用机制和 ViewHolder 模式是答辩时的高频问题,主动用上等于给自己铺好一个“被问住但能答上来”的点。

public class MemoAdapter extends RecyclerView.Adapter<MemoAdapter.VH> { private List<MemoBean> data = new ArrayList<>(); private OnItemClickListener listener; public interface OnItemClickListener { void onClick(int position); } public void setData(List<MemoBean> data) { this.data = data == null ? new ArrayList<>() : data; notifyDataSetChanged(); } @Override public void onBindViewHolder(@NonNull VH holder, int position) { MemoBean bean = data.get(position); holder.title.setText(bean.getTitle()); holder.time.setText(TimeUtil.format(bean.getUpdateTime())); if (bean.isPinned() == 1) { holder.pinIcon.setVisibility(View.VISIBLE); } else { holder.pinIcon.setVisibility(View.GONE); } holder.itemView.setOnClickListener(v -> listener.onClick(position)); } @Override public int getItemCount() { return data.size(); } }

setData里的判空处理很容易被忽略——数据库查询结果为 null 时直接传入会让getItemCount空指针,演示时当场崩溃。notifyDataSetChanged()在这里足够用,不需要上 DiffUtil,因为备忘录数据量撑死几十条,全量刷新成本可以忽略。答辩时如果有人问这一点,回答“数据量小,全量刷新简单可靠”反而是加分项。

列表页还需要一个空状态。getItemCount() == 0时显示“还没有备忘录,点右下角新建”,否则隐藏。这个细节看起来小,却是截图里最出效果的一屏,因为空状态能证明你真的处理过边界情况。

3.2 新增和编辑共用一个EditActivity:传id是-1还是真实id

EditActivity接收 Intent 里的memo_id字段,-1表示新增,其他值表示编辑。这样一套布局服务两个场景,保存逻辑里用memoId区分走插入还是更新:

private void saveMemo() { String title = etTitle.getText().toString().trim(); String content = etContent.getText().toString().trim(); long now = System.currentTimeMillis(); long remindTime = remindSwitch.isChecked() ? remindCal.getTimeInMillis() : -1L; MemoBean bean = new MemoBean(); bean.setTitle(title); bean.setContent(content); bean.setUpdateTime(now); bean.setRemindTime(remindTime); if (memoId == -1) { bean.setCreateTime(now); memoDao.insert(bean); } else { bean.setId(memoId); memoDao.update(bean); } setResult(RESULT_OK); finish(); }

保存前要做一道校验:标题为空时,用内容的前 10 个字当标题,内容是空则提示“内容不能为空”。这个大作业场景里,用户经常只写内容不写标题,如果存库直接放过,列表页就会显示一条没有标题的灰条,看起来像 bug,其实是数据问题。预设默认标题这个处理能让演示顺畅很多。

回传结果用RESULT_OK,主界面在onActivityResult里重新查库刷新列表。不要直接传一个“保存成功”的布尔值,因为主界面本来就要刷新数据,直接重新查最简单。

3.3 搜索功能别用contains:SQL通配符和防注入要一起写

搜索框在主界面顶部,监听输入变化后调MemoDao的查询方法。关键词匹配标题和内容,按置顶优先、更新时间倒序排列:

public List<MemoBean> search(String keyword) { SQLiteDatabase db = getReadableDatabase(); String sql = "SELECT * FROM memo " + "WHERE title LIKE ? OR content LIKE ? " + "ORDER BY is_pinned DESC, update_time DESC"; String kw = "%" + keyword + "%"; Cursor cursor = db.rawQuery(sql, new String[]{kw, kw}); List<MemoBean> result = new ArrayList<>(); while (cursor.moveToNext()) { // 从cursor逐列读出字段,封装成MemoBean加入result } cursor.close(); return result; }

LIKE的模糊匹配用了?占位符,这是防 SQL 注入的底线写法。很多教程直接写LIKE '%" + keyword + "%',单次跑没问题,但答辩时老师问“如果 keyword 里带单引号会怎样”,答不上来就是硬伤。用rawQuery传参的方式,一句话就能解释清楚。

搜索结果的排序和列表页保持完全一致,都是is_pinned优先、update_time倒序。这样从搜索切回列表时视觉顺序不变,不会让评委觉得是两个页面。

3.4 提醒功能做“应用内提醒”最稳:避开Android 12精确闹钟限制

系统级提醒要接AlarmManager加SCHEDULE_EXACT_ALARM权限,还要处理 Doze 模式、应用被杀后广播收不到等一串问题。期末这个时间点,用系统推送做提醒的风险很高,真机上演示时闹钟不响的概率不低。我常用的方案是“应用内未办提醒条”:打开 App 时,顶部横幅提示“你有 2 条过期待办”。

private void checkRemindStatus(List<MemoBean> list) { long now = System.currentTimeMillis(); for (MemoBean bean : list) { if (bean.getRemindTime() > 0 && bean.getRemindTime() < now && bean.getStatus() == 0) { bean.setStatus(2); // 2表示已过期未办 } } }

这段代码放在查询数据之后、adapter.setData之前,状态直接写回数据列表。status的三态设计要提前定好:0 待办、1 已完成、2 已过期。列表项“完成”按钮把 status 改成 1,数据库同步更新;首页把 status 为 2 的条目淡色展示,形成视觉区分。

如果你一定要做系统通知,有一个折中办法:把系统推送做成加分项而不是核心项,代码写在独立工具类里,导入文档里注明“真机上需手动授予通知权限”,演示时用真机。但默认验收路径还是走应用内提醒,稳得多。

4. 数据持久化:SQLite表结构设计决定能不能拿高分

4.1 三种存储方式对比:期末选手写SQLite,别选Room

数据存储是备忘录 App 的技术核心,也是最容易被答辩追问的地方。三种方案的取舍直接关系到你能不能守住建表、查询、更新这些基础问题:

存储方案学习成本答辩友好度翻车概率适合场景
SharedPreferences极低低,存不了复杂结构低仅保存设置项
手写 SQLite中,SQL 是课纲重点高,每一行都能讲中,代码量大但透明期末大作业首选
Room高,注解和依赖多中,讲不清原理容易露馅高,依赖同步慢有把握的进阶项目

Room 是 Google 官方推荐的 ORM,但对期末来说成本偏高:要加 kapt 插件配注解处理器,要写 Entity、Dao、Database 三套接口,导入时多三个依赖就可能少三份稳定性。手写SQLiteOpenHelper的好处是所有逻辑摆在明面上,建表写在onCreate里,查询写在MemoDao里,评委问“这个 SQL 是什么意思”你能直接答。

SharedPreferences 只适合存“是否已引导”“主题色偏好”这类单项配置。用 JSON 串去存整个列表属于歪路,因为一旦数据量大,每次读出来都要整体反序列化,性能和代码复杂度都会失控。

4.2 表结构:一张memo表,八个字段够应付所有功能

很多同学建表只建三列:id、title、content。跑通时觉得挺顺,但一到答辩就露怯,因为支撑不了搜索、置顶、提醒和完成状态。我用的表结构可以一次把这几个功能全兜住:

CREATE TABLE memo ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL DEFAULT '', content TEXT NOT NULL DEFAULT '', remind_time INTEGER NOT NULL DEFAULT -1, is_pinned INTEGER NOT NULL DEFAULT 0, status INTEGER NOT NULL DEFAULT 0, create_time INTEGER NOT NULL, update_time INTEGER NOT NULL ); CREATE INDEX idx_memo_update ON memo(update_time DESC);

字段设计看着简单,每个字段都有讲究:

  • id用AUTOINCREMENT,保证删除后新增的条目拿不到旧主键。如果删掉第 2 条再新增,没有 AUTOINCREMENT 的话,新条目可能复用 id=2,出现详情页错位的诡异问题。
  • remind_time存 epoch 毫秒整数,默认 -1 表示未设提醒。用整数而不是TEXT保存时间,是为了直接算差值判断是否过期。
  • is_pinned默认 0,1 表示置顶,排序时一个ORDER BY is_pinned DESC就解决。
  • status配合 3.4 的三态:0 待办、1 完成、2 过期。
  • create_time和update_time拆开存,新增时两个都写,编辑时只更新update_time。这在“今天”“最近三天”这类筛选场景里会用到。

idx_memo_update索引是为了让按时间排序的查询不至于全表扫描。数据量小的时候感知不到,但这个索引的存在能让答辩时多一个可讲的点。建索引的 SQL 可以写在onCreate里一起执行。

4.3 升级数据库的正确姿势:别用DROP TABLE应付答辩

SQLiteOpenHelper的onUpgrade是期末项目里最容易被糊弄过去的方法。很多人图省事,直接在onUpgrade里DROP TABLE再重建,这样老用户升级后数据全没。答辩现场只要老师问一句“用户升级后数据还在吗”,这个方案就崩了。

正确的做法是版本号递增,每个版本只执行对应的一段迁移:

@Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (oldVersion < 2) { db.execSQL("ALTER TABLE memo ADD COLUMN remind_time INTEGER NOT NULL DEFAULT -1"); } if (oldVersion < 3) { db.execSQL("CREATE INDEX idx_memo_update ON memo(update_time DESC)"); } if (oldVersion < 4) { db.execSQL("ALTER TABLE memo ADD COLUMN status INTEGER NOT NULL DEFAULT 0"); } }

这段代码的写法是“按版本号递进”,不是“按目标版本覆盖”。oldVersion < 2表示从任何低于 2 的版本升上来都要执行加列操作,这样从 1 直接升到 4 时三段都会执行。用ALTER TABLE加列可以保留已有行数据,这是迁移的基本原则。

导入文档里值得专门写一小节“数据库版本设计”,把版本 1 到版本 4 的变更列成清单。这个内容很多大作业不会写,写了就是区分度。

4.4 导出备份:一个把列表写成txt的小工具

导出功能是给“数据持久化”章节画龙点睛的。它不复杂,但能让评委看到你考虑了数据可迁移性。做法是把备忘录列表写成一个 txt 文件:

File dir = getExternalFilesDir("backup"); File file = new File(dir, "memo_backup_" + System.currentTimeMillis() + ".txt"); try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { for (MemoBean bean : list) { writer.write(bean.getTitle() + " | " + bean.getContent() + " | " + TimeUtil.format(bean.getUpdateTime())); writer.newLine(); } Toast.makeText(this, "已导出到 " + file.getAbsolutePath(), Toast.LENGTH_LONG).show(); } catch (IOException e) { e.printStackTrace(); }

关键点落在getExternalFilesDir("backup")上。这个方法拿到的是 App 专属外部目录,既不需要WRITE_EXTERNAL_STORAGE权限,也天然绕开 Android 10 以后的分区存储限制。如果换成Environment.getExternalStorageDirectory()直接写公共目录,Android 11 以上大概率直接报权限错误,这也是一处常见翻车点。导出后吐司里打印完整路径,方便演示时找出文件。

5. 备忘录App避坑清单:5个高频翻车点还原验收现场

5.1 导入工程后一编译就报错:Gradle同步了一半就失败

现象:老师拿到工程导入 Android Studio,界面一直转圈,最后提示Unable to resolve dependency或SDK location not found。

原因:依赖仓库访问慢导致超时,或者目标机器的 SDK 版本和你的工程不一致。很多同学自己机器上开着代理没感觉,换台机器就原形毕露。

解决:工程里不要依赖冷门仓库,只保留 Google 和 Maven Central 的默认配置。导入文档第一行就写清楚“JDK 1.8/17、compileSdk 33、minSdk 24”,老师机器版本不匹配时能立刻知道调哪里。另外打包一份 Gradle 离线缓存不现实,最稳的办法是文档里写三步排查顺序:先看 SDK 路径,再看 JDK 版本,最后开离线模式重试。

5.2 列表刷新但界面没变化:notifyDataSetChanged没执行

现象:在新增页保存后返回主页,列表还是老样子,重启应用才看到新数据。

原因:onActivityResult里没写刷新逻辑,或者setData没触发notifyDataSetChanged。我在 3.1 里专门强调过setData内部要调notify,但很多人把刷新写在了 Activity 的onResume里,结果setData又没调用到。

解决:onActivityResult收到RESULT_OK后,重新查库一次,再调adapter.setData(newList)。另外把onResume里的刷新代码去掉,避免重复加载。这个坑的根源是“数据变更后没有主动通知适配器”,养成“改数据必 notify”的习惯能根治。

5.3 中文内容在模拟器上展示成乱码或输入法弹不出

现象:演示时用模拟器,想在搜索框里输入中文,结果输入法弹不出来,或者打出来的字变成问号,老师以为你编码没做对。

原因:Android Studio 模拟器默认英文输入法,部分版本的 Google 拼音输入法没有预装。

解决:最稳妥的方案是演示走真机,其次是预置一条包含中文的演示数据,让搜索可以直接点“最近”标签跳到结果,不用现场打字。我一般在MainActivity的onCreate里加一个if (dao.count() == 0) 插入3条示例数据,既解决空列表问题,又保证搜索和详情有内容可看。

5.4 删除后新增的数据ID对不上,点详情进错条目

现象:先删除第 2 条备忘录,再新增一条,然后点新增那条的详情,弹出的却是旧数据的内容。

原因:列表点击事件里把position当成 id 传给了详情页。删除后列表的position发生位移,UI 位置和数据库主键不再对应。

解决:点击事件里必须传bean.getId():

holder.itemView.setOnClickListener(v -> { if (listener != null) { listener.onClick(bean.getId()); } });

接口回调签名改成onClick(int memoId),EditActivity拿到的才是真正的主键。这条属于数据一致性的大坑,代码审查和答辩时都会被重点追问,最好在写的时候一步到位。

5.5 导出txt后找不到文件,答辩现场翻车

现象:演示导出功能,Toast 提示导出成功,但老师问“文件在哪”,你打开文件管理器找不到路径。

原因:getExternalFilesDir路径在Android/data/包名/files/下,很多文件管理器默认不显示这个目录,容易让人以为导出失败了。

解决:演示时不要用文件管理器找文件。两个思路:一是代码里把完整路径 Toast 出来,现场用adb shell ls /sdcard/Android/data/com.example.memo/files/backup/展示;二是加一个“分享”按钮,调系统的Intent.ACTION_SEND把文件直接发到邮件或备忘录。后者在真机演示时效果更直观,评委能看到文件真真实实地被发出去。

6. 验收前最后一道工序:演示脚本、边界测试和两个提分细节

6.1 演示脚本:三分钟把评分表走一遍

评委看演示的时间通常不超过三分钟。我会按这条顺序来:冷启动进主页看空状态和列表,点新建加一条带时间的备忘录,用关键词搜出刚才那条,置顶它,改一个字段,删除另一条,最后重启应用确认数据还在。整个流程覆盖增删改查、搜索、置顶、持久化六个评分点,中间不切换页面超过两次。新增操作一定要现场做,不要点预置数据糊弄;预置数据是给搜索用的,不是给新增用的。

6.2 边界输入测试清单

输入场景预期行为
标题为空,只有内容用内容前 10 个字做标题,正常保存
标题和内容都为空格拦截提示“内容不能为空”,不写入数据库
标题超过 50 个字符布局不崩,列表项单行省略显示
提醒时间设为过去的时刻打开 App 后进入过期态(status=2)
连续新增 50 条列表滚动流畅,不卡顿、不错位

6.3 截图和导入文档的提分细节

截图不是把手机桌面拍一张就完事。我会截四张关键界面:列表空状态、列表非空状态、编辑页带键盘、搜索后的结果页。每张图上用红框标出关键按钮或状态,这种对比式截图能让评委不用装 App 就能看出功能全貌。导入文档的最后一页放一份功能自测表,每项对应一个截图,这本质上是把整理“移动应用开发笔记”的习惯用在了作业上。带过几次大作业后我养成一个习惯:功能写完先当评委问自己一句,如果这是别人电脑上第一次打开,跑不跑得起来。这个习惯救过我很多次。希望帮到你。

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

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

用JavaScript玩转WPS自动化:文件管理与超链接批处理实战指南

1. 为什么我用JS而不是VBA来折腾WPS自动化 这两年“AI自动化办公”的热度高得离谱&#xff0c;不少同学一上手就想搞大模型接管Excel。但真正能把日常重复劳动省下来的&#xff0c;往往不是那些花哨的AI对话&#xff0c;而是老老实实的脚本自动化。而我选的这条路&#xff0c;是…

作者头像 李华
网站建设 2026/10/2 8:51:20

Open WebUI 私有化部署实战:从 Docker 到 RAG 知识库完整指南

Open WebUI 这个开源项目&#xff0c;在本地部署 AI 的圈子里热度一直居高不下。我前前后后帮团队和个人捣鼓过好几套私有化 AI 知识库方案&#xff0c;从商业产品到开源全家桶都摸了一圈&#xff0c;最后长期留用的就是 Open WebUI——一个纯开源的 Web 界面&#xff0c;Docke…

作者头像 李华
网站建设 2026/10/2 8:50:51

企业高管运作模型MA-B系列:决策卡点与信息回流机制拆解

1. 先理解企业高管运作模型到底解决什么事1.1 高管层的真正难题不是决策质量&#xff0c;而是决策的"共同上下文"在企业里待过一段时间的朋友应该都有感受&#xff1a;高管会议室里最不缺的就是聪明人&#xff0c;缺的是一种"在同一张地图上讨论问题"的能力…

作者头像 李华
网站建设 2026/10/2 8:50:17

RelayRouter:文本流语义治理的实时工作流中枢

1. 这不是“聊天变视频”的噱头&#xff0c;而是文本工作流的底层重构最近 Gemini Live Avatar 的演示视频刷屏了——说话、眨眼、手势、情绪反馈&#xff0c;一气呵成。很多人第一反应是&#xff1a;“哇&#xff0c;AI终于能‘活’起来了。”但作为在实时系统里摸爬滚打八年、…

作者头像 李华
网站建设 2026/10/2 8:49:59

EF Core迁移记录合并实战:从历史包袱到干净基线

很多人把EF Core的迁移机制当成项目里的"常规操作"&#xff0c;加个表执行 dotnet ef migrations add &#xff0c;部署时跑一遍 database update &#xff0c;一切看起来都有章可循。但真实项目跑到一年以上&#xff0c;打开 Migrations 目录数一数&#xff0…

作者头像 李华
网站建设 2026/10/2 8:48:07

RankIQA图像质量评估实战:基于排序学习的PyTorch实现

简介&#xff1a;基于PyTorch的图像质量评估模型RankIQA源码与说明&#xff0c;面向计算机视觉方向需要完成课程设计或期末大作业的学生&#xff0c;帮助快速掌握图像质量评估任务的模型实现、训练与评估流程&#xff0c;并可作为高分项目直接提交。压缩包共58个文件&#xff0…

作者头像 李华