从零开始Android商业项目Vibe coding完全指南(二)

20260524

读者应具备的知识基础

Kotlin、 Compose、 基础Android开发(谷歌官网课程优先)

准备项目基础

在我的实践中,纯粹由AI从零开始 Vibe coding并不合适,非常容易出现各种问题。再加上标准的Android应用开发,本身已经存在最佳实践。因此我们换一种方法,就像造房子,我们先自己打好地基,再让AI往上施工。

新建项目略去不表。我们来看技术架构,遵照谷歌推荐的做法,我们先做简单规划:

  • 目标平台Android 16,要上架Play商店这是硬性要求。

  • 最低支持 Android 11,本漫画软件需要实现对书籍文件的编辑管理,也就是依赖所有文件管理权限 android.permission.MANAGE_EXTERNAL_STORAGE,11以下旧式的权限会在某些场景产生问题。

  • 单Activity应用

  • 纯Compose,使用bom管理相关依赖库,不去追求实验性UI

  • 分UI层,Data层两层。保证单向数据流。

  • 使用容器管理依赖项(简单情况一个文件就好,并不当然需要Hilt)

  • SplashScreen搞定开屏页

  • navigation3导航页面切换

  • 分离出mangasupport模块。我们的app有两个版本免费版、付费版,显然不可能许多代码写两遍,因此我们把通用业务逻辑放到这里

  • 开源库依赖若干。比如,为支持Zip漫画支持,我们选用zip4j注意,这个库并未考虑多线程调用的情况,我们将做两个小修改解决问题。

配置navigation3

navigation2是基于Fragment的,而我们的项目目标是纯Compose,因此最好用上navigation3,特别是Screen间的传值,navigation3要好得多,对于返回栈的自由管理也方便得多。Google的nagation3教程写得比较随意。我推荐你观看扔物线的视频,有个基础概念,再按教程进行配置。

但是,在实操这里,有关NavigationStateNavigator你可能用不到它那种三个Tab页,各自独立一套回退栈的情况。这时,你可以参考我提供的简单回退栈(含附加功能)实现:

NavigationState,开启了rememberViewModelStoreNavEntryDecorator(),每个ViewModel将与对应页面绑定生命周期。

kotlin 复制代码
@Composable
fun rememberNavigationState(
    startRoute: NavKey,
): NavigationState {
    val backStack = rememberNavBackStack(startRoute)
    return remember(backStack) {
        NavigationState(
            backStack = backStack
        )
    }
}

class NavigationState(
    val backStack: NavBackStack<NavKey>
) {

}

/**
 * Convert NavigationState into NavEntries.
 */
@Composable
fun NavigationState.toEntries(
    entryProvider: (NavKey) -> NavEntry<NavKey>
): SnapshotStateList<NavEntry<NavKey>> {
    val decorators = listOf(
        rememberSaveableStateHolderNavEntryDecorator<NavKey>(),
        // ✨ 关键:加入这个装饰器!它会为每一个入栈的页面分配独立的 ViewModelStore
        rememberViewModelStoreNavEntryDecorator()
    )
    val l = rememberDecoratedNavEntries(
        backStack = backStack,
        entryDecorators = decorators,
        entryProvider = entryProvider
    )

    return l.toMutableStateList()
}

Navigator,加入了防抖功能

kotlin 复制代码
class Navigator(val state: NavigationState, val finishApp: () -> Unit) {
    var navigateLastTime = 0L
    var goBackLastTime = 0L

    val backStack = state.backStack

    fun navigate(route: NavKey): Boolean {
        val now = System.currentTimeMillis()
        val tmpLastTime = navigateLastTime
        navigateLastTime = now
        if (now - tmpLastTime > DEBOUNCE_TIME) {
            backStack.add(route)
            return true
        }
        return false
    }

    fun navigateToBrowser(initialFilePath: String) {
        val now = System.currentTimeMillis()
        val tmpLastTime = navigateLastTime
        navigateLastTime = now
        if (now - tmpLastTime > DEBOUNCE_TIME) {
            backStack.add(Browser(initialFilePath))
        }
    }

    fun navigateAndClear(route: NavKey) {
        if (navigate(route))
            for (i in backStack.size - 2 downTo 0)
                backStack.removeAt(i)
    }

    fun goBack() {
        val now = System.currentTimeMillis()
        val tmpLastTime = goBackLastTime
        goBackLastTime = now
        if (now - tmpLastTime > DEBOUNCE_TIME) {
            if (backStack.size > 1) {
                backStack.removeLastOrNull()
            } else {
                finishApp()
            }
        }
    }

    companion object {
        const val DEBOUNCE_TIME = 300L
    }
}

让zip4j库实现多线程安全

zip4j库本身未提供多线程模式。在它的使用上,我实测它创建ZipFile的花销很大,因此一经创建,我们就利用ZipFile实例来做多线程解压,这样才能具有流畅阅读的体验。但是,zip4j库在多线程解压时会抛出错误造成解压失败。

经过一段时间的bug追踪,我定位并分析出了解决多线程安全的改法。由于这仅仅是小众软件中的小众需求,我没有向官方提交代码。具体改法如下:

1、ZipFile类第105行,openInputStreams改为并发容器

2、AbstractExtractFileTask,修改extractFile方法内65行以下代码:

kotlin 复制代码
            boolean errorOccurred = false;
            try {
                checkOutputDirectoryStructure(outputFile);
            } catch (Exception e) {
                errorOccurred = true;
            }
//          目前这个补丁似乎工作得很好 :)
            if (errorOccurred) {
                if (outputFile.getParentFile().exists())
                    unzipFile(zipInputStream, outputFile, progressMonitor, readBuff);
            } else {
                unzipFile(zipInputStream, outputFile, progressMonitor, readBuff);
            }

            //      Original code:
//      checkOutputDirectoryStructure(outputFile);
//      unzipFile(zipInputStream, outputFile, progressMonitor, readBuff);

Vibe coding的原则与能力边界

对话模式

Gemini提供三种对话模式:问答,快速,计划。

这三种模式的推理能力逐级上升。问答模式我一般不使用,因为往往都涉及到任务实施,即便不需要改代码,我都是在快速模式中,给出提示词"先别改代码,你说说看......"。计划模式耗时会比较久一点,用于实施前进行深度思考。

我建议仅对明显有难度、或Gemini明显处理有问题的部分采用这种模式,否则一律采用快速模式。这是因为这种模式最适合快问快答,加速代码生成与演化。

原则

原则一:采用迭代开发。

实现具有难度的功能,我的建议是采用迭代开发思维,先让AI完成一个 基本实现->测试实现是否完成->Git保存代码->然后追加功能->再测试......走这样一个循环。即便最终AI无法完成该功能,你也会得到一个有所保障的半成品。

据我观察,AI写出的代码,有时候尚可,有时又会非常诡异,比如可能有以下问题:

  • 莫名定义了变量,但到后面有的地方用一下,有的地方又不用。
  • 本应当相当简单就能实现,它却花费了大量代码
  • 有些经典公式它不懂得使用,于是得不到性能最优解。一个典型例子是a/b的向上取整公式, (a+b-1)/b。又比如某些大数运算下防止溢出需要先减后加或是先除后乘,AI完全没有这样的自觉
  • 某些代码片段某些情况下存在Bug。可能是没有有效性检查
  • 代码其实是一坨Shit,但刚好能跑且跑出了你要的结果

原则二:如非必要,不要费神去阅读AI写的代码。

特别是如果你也像我一样有长年编码经验,有优质代码洁癖的话,AI写的代码会简直没眼看。你应当以结果为导向,把它的产出视作黑盒处理。
原则三:认清AI代码可能存在的问题

如前所述,AI代码往往不像优秀工程师那样可以产出最优解。需要警惕的是,性能最不最优姑且可以放到一边,但微小错误的累积有可能造成灭顶之灾。你应当在软件架构上做出良好的设计分层,这样的话发生火灾也不会蔓延。

AI写代码并不如人类谨慎。特别是已经开发完成、验收无误的代码,如果它开发新功能时视作有关联,它可能会一并修改,从而直接引入不必要的新Bug。

原则四:警惕退化

如果你十分清楚修改范围,最好使用 @ 符号或者语言描述限制AI的自由发挥。

能力边界

AI显然存在能力边界,在那些领域,你应当做好自己上手的准备,以下是Gemini的回答:

虽然它是这样说,但我体验时许多稍微复杂的功能它也无法实现:(当然这不是它的错,所有AI半斤八两,关于这一点你可自行验证。

Vibe coding示例:实现水平翻页功能

水平翻页功能

在新的架构下,基于旧有代码,我们很快构建起了App原型。

我们创建了一个BrowserScreen专门用于阅读,提供三种阅读模式:水平翻页,水平连续、垂直连续。

漫画阅读最基本的实现就是占满整个屏幕的一页,水平方向左右翻页,类同于纸质书籍的阅读体验。

其中,显然水平连续、垂直连续使用LazyRow/LazyColumn即可简单实现,这直接对应View体系中的RecyclerView,我的旧项目中有现成代码随便改两行即告完成。水平翻页就不一样了,我采用了自定义类实现,并且通过定义接口,提供多种不同翻页效果的实现类。而在Compose体系下,这显然无法直接对应,并且由于实现路径的差异,也并不是很好直接移植。

很好,我们把这个任务丢给AI。在计划模式下提问:

小技巧:主键盘区回车键发送对话,而小键盘区回车键是换行。

AI回答如下:

看上去挺像那么一回事,好吧,实际上我没怎么看:) 希望你没有忘记原则二,如非必要我不会去读AI写的代码,当然也不会让你受苦。

我们让AI实施,然后直接运行检查下效果。

看上去一切都很美好,直到最后,放大后的图片再移动时出现了黑块:(

下一期,我们将尝试修复这个bug。

相关推荐
深念Y21 小时前
理解大模型API缓存机制:从Claude Code的缓存失效到DeepSeek的硬盘缓存
缓存·ai·api·提示词·kvcache·vibecoding·claudecode
Captaincc3 天前
来自 Codex 官方团队的分享:如何把 Codex 用到极致
前端·vibecoding
松加德的杰洛特4 天前
Openspec+ Superpower AI 编程工程化探索
vibecoding
陌陌卡上4 天前
从 Vibecoding 入门,到 Agent 差点入土
ai·创意·vibecoding·想象
万能小林子4 天前
2026 AI开发新范式:Vibe Coding生成网页 + 3分钟打包成App,非技术人也能独立发布自己的App!
人工智能·uni-app·ai编程·web app·vibecoding
本末倒置1835 天前
别再复制粘贴了!一行命令把网页变成干净的 Markdown
claude·vibecoding
大橙子打游戏6 天前
用 Claude Code 的人,都需要的“对话归档箱”
vibecoding
mask哥6 天前
codex安装并配置第三方大模型api方法详解
人工智能·ai编程·codex·vibecoding
用户398181661747 天前
Claude Code LSP 在 Windows 上的配置与排坑
vibecoding