
1. 零基础也能跑通Android Studio Cursor 做音乐播放器骨架如果你完全没写过安卓但想做出一个能扫描本地 mp3、能播放暂停、能切歌的音乐播放器这篇就是给你准备的。我会用 Android Studio 建项目用 Cursor 辅助写代码再通过 TaoToken 统一 Key 把 AI 能力接进编辑器里让补全和改错都更顺。整套流程不需要你懂 Gradle 原理也不需要背 Kotlin 语法照着复制配置、点运行就能看到界面。先说清楚这个骨架能做什么主界面是一个音乐列表加底部控制栏包含播放/暂停、上一首、下一首、播放模式切换启动时申请访问本地目录权限默认读取设备根目录下的/mp3/文件夹并按修改时间倒序排列。适合谁刚装好 Android Studio 的新手、想用 AI 辅助写第一个 App 的人、以及想把 AI 编码工具接进日常开发流的同学。我试过把报错直接丢给 Cursor 让它改效率比自己查文档快很多。下面按“建项目 → 配 TaoToken → 写骨架 → 运行验证 → 排错”的顺序走每一步都给可复制的片段。2. 前置准备TaoToken 统一 Key 与 Cursor 接入TaoToken 在这里的角色是“统一入口”你只需要一个 Key就能在 Cursor 里调用多种模型来完成代码补全、解释和改错。官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 端点是 https://taotoken.net/api 这个不加 UTM。注意TaoToken 是合规的 API 聚合服务不是任何形式的网络中转工具我们只把它当作模型调用的统一网关来用。你需要先拿到 API Key。进入控制台创建密钥的页面在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 创建后复制那串以sk-开头的字符串先存到记事本里后面配置要用。如果你还想在网页里直接和模型对话验证 Key 是否可用可以打开模型对话页 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 试一句“你好”能正常回复就说明 Key 没问题。Cursor 的配置入口在设置里的 Models 面板选择 OpenAI 兼容方式把 Base URL 填成https://taotoken.net/apiAPI Key 填你刚复制的那串。保存后新建一个对话问它“用 Kotlin 写一个 RecyclerView 适配器”能出代码就说明接入成功。这一步是整个流程的地基Key 不通后面全白搭。3. 可复制配置settings.json 骨架与项目结构Cursor 的模型配置除了在界面点选也可以直接写进settings.json这样换机器或重装时一键恢复。文件位置在 Cursor 的用户配置目录下Windows 一般是%APPDATA%\Cursor\User\settings.jsonmacOS 是~/Library/Application Support/Cursor/User/settings.json。把下面这段合并进去注意 JSON 不能有多余逗号{ cursor.aiProvider: openai, cursor.openaiBaseUrl: https://taotoken.net/api, cursor.openaiApiKey: sk-你的Key粘贴在这里, cursor.defaultModel: gpt-4o-mini, editor.formatOnSave: true, editor.fontSize: 14 }注意sk-开头的 Key 属于敏感信息不要提交到 Git 仓库也不要在截图里露出完整字符串。团队协作时建议用环境变量注入而不是硬编码在配置文件里。Android Studio 这边新建项目时选 Empty Views Activity语言选 Kotlin最低 SDK 建议 API 24Android 7.0目标 SDK 选 34。项目建好后目录结构大致是这样你只需要关注标出来的几个文件app/ ├─ src/main/ │ ├─ java/com/example/musicplayer/ │ │ ├─ MainActivity.kt // 主界面逻辑 │ │ ├─ MusicAdapter.kt // 列表适配器 │ │ └─ MusicPlayerManager.kt // 播放控制封装 │ ├─ res/layout/ │ │ └─ activity_main.xml // 界面布局 │ └─ AndroidManifest.xml // 权限声明 └─ build.gradle.kts // 依赖配置在build.gradle.kts的 dependencies 里加上媒体播放和列表所需依赖Media3 是官方推荐的播放库dependencies { implementation(androidx.media3:media3-exoplayer:1.3.1) implementation(androidx.media3:media3-session:1.3.1) implementation(androidx.recyclerview:recyclerview:1.3.2) implementation(androidx.constraintlayout:constraintlayout:2.1.4) }权限声明写在AndroidManifest.xml的application外面Android 13 以上读音频需要READ_MEDIA_AUDIO低版本用READ_EXTERNAL_STORAGEuses-permission android:nameandroid.permission.READ_MEDIA_AUDIO / uses-permission android:nameandroid.permission.READ_EXTERNAL_STORAGE android:maxSdkVersion32 / uses-permission android:nameandroid.permission.FOREGROUND_SERVICE /4. 骨架代码列表、播放控制与权限申请布局文件activity_main.xml用 ConstraintLayout 分成上下两块上面 80% 放 RecyclerView下面 20% 放控制按钮。搜索按钮和排序按钮放顶部返回顶部按钮挨着排序按钮。下面这段可以直接替换默认布局?xml version1.0 encodingutf-8? androidx.constraintlayout.widget.ConstraintLayout xmlns:androidhttp://schemas.android.com/apk/res/android xmlns:apphttp://schemas.android.com/apk/res-auto android:layout_widthmatch_parent android:layout_heightmatch_parent ImageButton android:idid/btnSearch android:layout_width48dp android:layout_height48dp android:srcandroid:drawable/ic_menu_search app:layout_constraintTop_toTopOfparent app:layout_constraintStart_toStartOfparent / ImageButton android:idid/btnSort android:layout_width48dp android:layout_height48dp android:srcandroid:drawable/ic_menu_sort_by_size app:layout_constraintTop_toTopOfparent app:layout_constraintEnd_toEndOfparent / androidx.recyclerview.widget.RecyclerView android:idid/recyclerView android:layout_width0dp android:layout_height0dp app:layout_constraintTop_toBottomOfid/btnSearch app:layout_constraintBottom_toTopOfid/controlBar app:layout_constraintStart_toStartOfparent app:layout_constraintEnd_toEndOfparent / LinearLayout android:idid/controlBar android:layout_widthmatch_parent android:layout_height80dp android:orientationhorizontal android:gravitycenter app:layout_constraintBottom_toBottomOfparent ImageButton android:idid/btnPrev android:layout_width56dp android:layout_height56dp android:srcandroid:drawable/ic_media_previous / ImageButton android:idid/btnPlay android:layout_width64dp android:layout_height64dp android:srcandroid:drawable/ic_media_play / ImageButton android:idid/btnNext android:layout_width56dp android:layout_height56dp android:srcandroid:drawable/ic_media_next / ImageButton android:idid/btnMode android:layout_width56dp android:layout_height56dp android:srcandroid:drawable/ic_menu_rotate / /LinearLayout /androidx.constraintlayout.widget.ConstraintLayoutMainActivity.kt里做三件事申请权限、扫描/mp3/目录、把文件列表交给适配器。扫描逻辑用File遍历按lastModified()倒序。下面这段是核心骨架Cursor 可以帮你补全细节但结构先立住class MainActivity : AppCompatActivity() { private lateinit var adapter: MusicAdapter private val musicList mutableListOfFile() private var player: ExoPlayer? null private val permissionLauncher registerForActivityResult( ActivityResultContracts.RequestPermission() ) { granted - if (granted) loadMusic() else toast(需要权限才能读取音乐) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) adapter MusicAdapter(musicList) { file - playFile(file) } findViewByIdRecyclerView(R.id.recyclerView).apply { layoutManager LinearLayoutManager(thisMainActivity) adapter thisMainActivity.adapter } player ExoPlayer.Builder(this).build() requestPermissionIfNeeded() } private fun requestPermissionIfNeeded() { val perm if (Build.VERSION.SDK_INT 33) Manifest.permission.READ_MEDIA_AUDIO else Manifest.permission.READ_EXTERNAL_STORAGE permissionLauncher.launch(perm) } private fun loadMusic() { val dir File(Environment.getExternalStorageDirectory(), mp3) if (!dir.exists()) { toast(未找到 /mp3/ 目录); return } musicList.clear() musicList.addAll(dir.listFiles { f - f.extension mp3 } ?.sortedByDescending { it.lastModified() } ?: emptyList()) adapter.notifyDataSetChanged() } private fun playFile(file: File) { player?.apply { setMediaItem(MediaItem.fromUri(Uri.fromFile(file))) prepare() play() } } }MusicAdapter.kt就是一个标准的 RecyclerView 适配器把文件名绑到 TextView 上点击回调传出去。Cursor 里输入“帮我写一个 Kotlin RecyclerView 适配器item 是文件名带点击回调”它会生成完整代码你只需要把包名和布局 id 对上。5. 运行验证看到列表和播放按钮代码写完先别急着上真机用 Android Studio 的模拟器跑一遍最快。点右上角 Device Manager 创建一个 Pixel 5、API 34 的模拟器启动后点绿色三角运行。第一次编译会下载 Gradle 依赖耐心等几分钟。模拟器起来后你需要往/mp3/目录放几个测试文件。用 Android Studio 的 Device ExplorerView → Tool Windows → Device Explorer展开sdcard新建mp3文件夹把电脑上的 mp3 拖进去。回到 App 点一下搜索按钮触发重新扫描列表应该按修改时间倒序显示文件名。验证成功的标志有三个一是启动时弹出权限申请框同意后列表自动加载二是点任意一首歌底部播放按钮变成暂停图标能听到声音三是点下一首能切到列表里的下一首。如果列表是空的先确认/mp3/里确实有.mp3后缀的文件再检查权限是否真的授予了。真机验证时小米澎湃 OS 对存储权限管得比较严需要在系统设置里给 App 单独开“访问本地文件”的权限。Android 14 上如果只声明了READ_MEDIA_AUDIO但用户没授权扫描会返回空列表这是正常现象不是代码 bug。6. 本篇常见错排查第一个高频错误是Unresolved reference: ExoPlayer。这通常是依赖没同步点 File → Sync Project with Gradle Files或者检查build.gradle.kts里 media3 的版本号是否写对。如果同步后还报错把 Cursor 切到项目根目录让它读一遍build.gradle.kts再问“为什么 ExoPlayer 找不到”。第二个是权限申请后列表仍为空。Android 13 以上READ_EXTERNAL_STORAGE已经失效必须用READ_MEDIA_AUDIO。如果你在AndroidManifest.xml里两个都写了但没加maxSdkVersion高版本系统会忽略低版本权限导致申请的是错的权限。检查requestPermissionIfNeeded()里的版本判断分支。第三个是 Cursor 补全的代码包名对不上。AI 生成代码时经常用com.example.myapp这种默认包名而你的项目可能是com.example.musicplayer。解决办法是在 Cursor 里先打开你的MainActivity.kt让它基于当前文件上下文补全而不是新建对话从零生成。第四个是播放没声音但没报错。检查模拟器是否静音、mp3 文件是否损坏、以及ExoPlayer是否调用了prepare()。如果用的是setMediaItem后直接play()在部分版本上需要先prepare()再play()顺序反了会静默失败。如果你在接入或排错时卡住可以对照接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 检查 Base URL 和 Key 的写法需要重新生成 Key 就去 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。长期用 Cursor 写安卓项目的话Coding Plan https://taotoken.net/coding?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 更适合高频调用场景比每次单独配 Key 省事。骨架跑通之后下一步可以加搜索过滤、播放模式切换、通知栏控制。这些都可以在 Cursor 里用自然语言描述需求让它补全你负责运行和验证。第一个 App 不用追求完美能响、能切、能看到列表就已经跨过门槛了。